consensus.hoon 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /= dk /apps/dumbnet/lib/types
  2. /= sp /common/stark/prover
  3. /= mine /common/pow
  4. /= dumb-transact /common/tx-engine
  5. /= * /common/zoon
  6. :: this library is where _every_ update to the consensus state
  7. :: occurs, no matter how minor.
  8. |_ [c=consensus-state:dk =blockchain-constants:dumb-transact]
  9. +* t ~(. dumb-transact blockchain-constants)
  10. +| %genesis
  11. ::
  12. :: +set-genesis-seal: set .genesis-seal
  13. ++ set-genesis-seal
  14. |= [height=page-number:t msg-hash=@t]
  15. ^- consensus-state:dk
  16. ~> %slog.[0 leaf+"setting genesis seal."]
  17. =/ seal (new:genesis-seal:t height msg-hash)
  18. c(genesis-seal seal)
  19. ::
  20. ++ add-btc-data
  21. |= btc-hash=(unit btc-hash:t)
  22. ^- consensus-state:dk
  23. ?: =(~ btc-hash)
  24. ~> %slog.[0 leaf+"Not checking btc hash for genesis block"]
  25. c(btc-data `btc-hash)
  26. ~> %slog.[0 leaf+"received btc block hash, waiting to hear nockchain genesis block!"]
  27. c(btc-data `btc-hash)
  28. ::
  29. +| %checks-and-computes
  30. ++ inputs-in-heaviest-balance
  31. |= raw=raw-tx:t
  32. ^- ?
  33. (inputs-in-balance raw get-cur-balance-names)
  34. ::
  35. ++ inputs-in-balance
  36. |= [raw=raw-tx:t balance=(z-set nname:t)]
  37. ^- ?
  38. :: set of inputs required by tx that are not in balance
  39. =/ in-balance=(z-set nname:t)
  40. (~(dif z-in (inputs-names:raw-tx:t raw)) balance)
  41. :: %.y: all inputs in .raw are in balance
  42. :: %.n: input(s) in .raw not in balance
  43. =(*(z-set nname:t) in-balance)
  44. ::
  45. ++ get-cur-height
  46. ^- page-number:t
  47. height:(~(got z-by blocks.c) (need heaviest-block.c))
  48. ::
  49. ++ get-cur-balance
  50. ^- (z-map nname:t nnote:t)
  51. ?~ heaviest-block.c
  52. ::~& >> "no known blocks, balance is empty"
  53. *(z-map nname:t nnote:t)
  54. (~(got z-by balance.c) u.heaviest-block.c)
  55. ::
  56. ++ get-cur-balance-names
  57. ^- (z-set nname:t)
  58. ~(key z-by get-cur-balance)
  59. ::
  60. ::
  61. :: +compute-target: find the new target
  62. ::
  63. :: this is supposed to be mathematically identical to
  64. :: https://github.com/bitcoin/bitcoin/blob/master/src/pow.cpp
  65. ::
  66. :: note that this works differently from what you might expect.
  67. :: we/bitcoin compute "target" where the larger the number is,
  68. :: the easier the block is to find. difficulty is just a human
  69. :: friendly form to read target in. that's why this appears
  70. :: backwards, where e.g. an epoch that takes 2x as long as the
  71. :: desired duration results in doubling the target.
  72. ++ compute-target
  73. |= [bid=block-id:t prev-target=bignum:bignum:t]
  74. ^- bignum:bignum:t
  75. (compute-target-raw (compute-epoch-duration bid) prev-target)
  76. ::
  77. :: +compute-target-raw: helper for +compute-target
  78. ::
  79. :: makes it easier for unit tests. we currently do not use
  80. :: bignum arithmetic due to lack of testing and it not yet
  81. :: being necessary. once consensus logic starts being run
  82. :: in the zkvm, we will need to change to bignum arithmetic.
  83. ++ compute-target-raw
  84. |= [epoch-dur=@ prev-target-bn=bignum:bignum:t]
  85. ^- bignum:bignum:t
  86. =/ prev-target-atom=@ (merge:bignum:t prev-target-bn)
  87. =/ capped-epoch-dur=@
  88. ?: (lth epoch-dur quarter-ted:t)
  89. quarter-ted:t
  90. ?: (gth epoch-dur quadruple-ted:t)
  91. quadruple-ted:t
  92. epoch-dur
  93. =/ next-target-atom=@
  94. %+ div
  95. (mul prev-target-atom capped-epoch-dur)
  96. target-epoch-duration:t
  97. =/ next-target-bn=bignum:bignum:t
  98. ?: (gth next-target-atom max-target-atom:t)
  99. max-target:t
  100. (chunk:bignum:t next-target-atom)
  101. ?: =(prev-target-atom next-target-atom)
  102. next-target-bn
  103. ~> %slog.[0 (cat 3 'previous target: ' (scot %ud prev-target-atom))]
  104. ~> %slog.[0 (cat 3 'new target: ' (scot %ud next-target-atom))]
  105. next-target-bn
  106. ::
  107. :: +compute-epoch-duration: computes the duration of an epoch in seconds
  108. ::
  109. :: to mitigate certain types of "time warp" attacks, the timestamp we mark
  110. :: as the end of an epoch is the median time of the last 11 blocks in the
  111. :: epoch. this also happens to be the min timestamp for the first block
  112. :: in the following epoch, which is already kept track of in
  113. :: .min-timestamps, where the value at a given block-id is the min
  114. :: timestamp of block that has that block-id as its parent. thus
  115. :: the duration of a given epoch is the difference between the minimum timestamp
  116. :: of the first block of the next epoch and the first block of the current
  117. :: epoch.
  118. ++ compute-epoch-duration
  119. |= last-block=block-id:t
  120. ^- @
  121. =/ prev-last-block=block-id:t
  122. (~(got z-by epoch-start.c) last-block)
  123. =/ epoch-start=@
  124. (~(got z-by min-timestamps.c) prev-last-block)
  125. =/ epoch-end=@
  126. (~(got z-by min-timestamps.c) last-block)
  127. ~| "time warp attack: negative epoch duration"
  128. (sub epoch-end epoch-start)
  129. ::
  130. :: +check-size: check on page size, requires all raw-tx
  131. ++ check-size
  132. |= [p=pending-state:dk pag=page:t]
  133. ^- ?
  134. (lte (compute-size:page:t pag raw-txs.p) max-block-size:t)
  135. ::
  136. +| %page-handling
  137. ++ add-page
  138. |= [pag=page:t acc=tx-acc:t now=@da]
  139. ^- consensus-state:dk
  140. :: update balance
  141. ::
  142. =? balance.c !=(~ balance.acc)
  143. :: if balance.acc is empty, this would still add the following to balance.c,
  144. :: so we do it conditionally.
  145. (~(put z-by balance.c) digest.pag balance.acc)
  146. =/ coinbases=(list coinbase:t)
  147. %+ turn ~(tap z-in ~(key z-by coinbase.pag))
  148. |= =lock:t
  149. (new:coinbase:t pag lock)
  150. =. balance.c
  151. %+ roll coinbases
  152. |= [=coinbase:t bal=_balance.c]
  153. (~(put z-bi bal) digest.pag name.coinbase coinbase)
  154. :: update txs
  155. ::
  156. =. txs.c
  157. %+ roll ~(tap z-in txs.acc)
  158. |= [=tx:t txs=_txs.c]
  159. (~(put z-bi txs) digest.pag id.tx tx)
  160. :: update blocks
  161. ::
  162. =. blocks.c
  163. (~(put z-by blocks.c) digest.pag (to-local-page:page:t pag))
  164. ::
  165. :: update epoch map. the first block-id in an epoch maps to its parent,
  166. :: and each subsequent block maps to the same block-id as the first. this is helpful
  167. :: bookkeeping to avoid a length pointer chase of parent of parent of...
  168. :: when reaching the end of an epoch and needing to compute its length.
  169. =. epoch-start.c
  170. ?: =(*page-number:t height.pag)
  171. :: genesis block is also considered the last block of the "0th" epoch.
  172. (~(put z-by epoch-start.c) digest.pag digest.pag)
  173. ?: =(0 epoch-counter.pag)
  174. (~(put z-by epoch-start.c) digest.pag parent.pag)
  175. %- ~(put z-by epoch-start.c)
  176. :- digest.pag
  177. (~(got z-by epoch-start.c) parent.pag)
  178. =. min-timestamps.c (update-min-timestamps now pag)
  179. ::
  180. =. targets.c
  181. ?: =(+(epoch-counter.pag) blocks-per-epoch:t)
  182. :: last block of an epoch means update to target
  183. %- ~(put z-by targets.c)
  184. :- digest.pag
  185. (compute-target digest.pag target.pag)
  186. ?: =(height.pag *page-number:t) :: genesis block
  187. %- ~(put z-by targets.c)
  188. [digest.pag target.pag]
  189. :: target remains the same throughout an epoch
  190. %- ~(put z-by targets.c)
  191. :- digest.pag
  192. (~(got z-by targets.c) parent.pag)
  193. :: note we do not update heaviest-block here, since that is conditional
  194. :: and the effects emitted depend on whether we do it.
  195. c
  196. ::
  197. :: +validate-page-without-txs-da: helper for urbit time
  198. ++ validate-page-without-txs-da
  199. |= [pag=page:t now=@da]
  200. (validate-page-without-txs pag (time-in-secs:page:t now))
  201. ::
  202. :: +validate-page-without-txs: with parent, without raw-txs
  203. ::
  204. :: performs every check that can be done on a page when you
  205. :: know its parent, except for validating the powork or digest,
  206. :: but don't have all of the raw-txs. not to be performed on
  207. :: genesis block, which has its own check. this check should
  208. :: be performed before adding a block to pending state.
  209. ++ validate-page-without-txs
  210. |= [pag=page:t now-secs=@]
  211. ^- (reason:dk ~)
  212. =/ par=page:t (to-page:local-page:t (~(got z-by blocks.c) parent.pag))
  213. :: this is already checked in +heard-block but is done here again
  214. :: to avoid a footgun
  215. ?. (check-digest:page:t pag)
  216. [%.n %page-digest-invalid-2]
  217. ::
  218. =/ check-epoch-counter=?
  219. ?& (lth epoch-counter.pag blocks-per-epoch:t)
  220. ?| ?& =(0 epoch-counter.pag)
  221. :: epoch-counter is zero-indexed so we decrement
  222. =(epoch-counter.par (dec blocks-per-epoch:t))
  223. == :: start of an epoch
  224. :: counter is one greater than its parent's counter.
  225. =(epoch-counter.pag +(epoch-counter.par))
  226. ==
  227. ==
  228. ?. check-epoch-counter
  229. [%.n %page-epoch-invalid]
  230. ::
  231. =/ check-pow-hash=?
  232. ?. check-pow-flag:t
  233. :: this case only happens during testing
  234. ::~& "skipping pow hash check for {(trip (to-b58:hash:t digest.pag))}"
  235. %.y
  236. %- check-target:mine
  237. :_ target.pag
  238. (proof-to-pow:t (need pow.pag))
  239. ?. check-pow-hash
  240. [%.n %pow-target-check-failed]
  241. ::
  242. =/ check-timestamp=?
  243. ?& %+ gte timestamp.pag
  244. (~(got z-by min-timestamps.c) parent.pag)
  245. ::
  246. (lte timestamp.pag (add now-secs max-future-timestamp:t))
  247. ==
  248. ?. check-timestamp
  249. [%.n %page-timestamp-invalid]
  250. ::
  251. :: check target
  252. ?. =(target.pag (~(got z-by targets.c) parent.pag))
  253. [%.n %page-target-invalid]
  254. ::
  255. :: check height
  256. ?. =(height.pag +(height.par))
  257. [%.n %page-height-invalid]
  258. ::
  259. =/ check-heaviness=?
  260. .= accumulated-work.pag
  261. %- chunk:bignum:t
  262. %+ add
  263. (merge:bignum:t accumulated-work.par)
  264. (merge:bignum:t (compute-work:page:t target.pag))
  265. ?. check-heaviness
  266. [%.n %page-heaviness-invalid]
  267. ::
  268. =/ check-coinbase-split=?
  269. (based:coinbase-split:t coinbase.pag)
  270. ?. check-coinbase-split
  271. [%.n %coinbase-split-not-based]
  272. =/ check-msg-length=?
  273. (lth (lent msg.pag) 20)
  274. ?. check-msg-length
  275. [%.n %msg-too-large]
  276. =/ check-msg-valid=?
  277. (validate:page-msg:t msg.pag)
  278. ?. check-msg-valid
  279. [%.n %msg-not-valid]
  280. ::
  281. [%.y ~]
  282. ::
  283. :: +validate-page-with-txs: to be run after all txs gathered
  284. ::
  285. :: note that this does _not_ repeat earlier validation steps,
  286. :: namely that done by +validate-page-withouts-txs and checking
  287. :: the powork. it returns ~ if any of the checks fail, and
  288. :: a $tx-acc otherwise, which is the datum needed to add the
  289. :: page to consensus state.
  290. ++ validate-page-with-txs
  291. |= [p=pending-state:dk pag=page:t]
  292. ^- (reason:dk tx-acc:t)
  293. =/ digest-b58=tape (trip (to-b58:hash:t digest.pag))
  294. ?. (check-size p pag)
  295. ::~& >>> "block {digest-b58} is too large"
  296. [%.n %block-too-large]
  297. =/ raw-tx-set=(set (unit raw-tx:t))
  298. (~(run z-in tx-ids.pag) |=(=tx-id:t (~(get z-by raw-txs.p) tx-id)))
  299. =/ raw-tx-list=(list (unit raw-tx:t)) ~(tap z-in raw-tx-set)
  300. =| tx-list=(list tx:t)
  301. =. tx-list
  302. |-
  303. ?~ raw-tx-list tx-list
  304. ?~ i.raw-tx-list
  305. ~ :: exit early b/c raw-tx was not present in raw-tx-set
  306. =/ utx=(unit tx:t) (mole |.((new:tx:t u.i.raw-tx-list height.pag)))
  307. ?~ utx :: exit early b/c raw-tx failed to convert
  308. ~
  309. %= $
  310. tx-list [u.utx tx-list]
  311. raw-tx-list t.raw-tx-list
  312. ==
  313. ?: ?&(=(~ tx-list) !=(~ raw-tx-list))
  314. :: failed to build a raw-tx, so the page is invalid
  315. [%.n %raw-txs-failed-to-build]
  316. :: initialize balance transfer accumulator with parent block's balance
  317. =/ acc=tx-acc:t
  318. (new:tx-acc:t (~(get z-by balance.c) parent.pag))
  319. ::
  320. :: test to see that the input notes for all transactions
  321. :: exist in the parent block's balance, that they are not
  322. :: over- or underspent, and that the resulting
  323. :: output notes are valid as well. a lot is going
  324. :: on here - this is a load-bearing chunk of code in the
  325. :: transaction engine.
  326. ::
  327. =/ balance-transfer=(unit tx-acc:t)
  328. |-
  329. ?~ tx-list
  330. (some acc)
  331. =/ new-acc=(unit tx-acc:t)
  332. (process:tx-acc:t acc i.tx-list height.pag)
  333. ?~ new-acc ~ :: tx failed to process
  334. $(acc u.new-acc, tx-list t.tx-list)
  335. ::
  336. ?~ balance-transfer
  337. :: balance transfer failed
  338. ::~& >>> "block {digest-b58} invalid"
  339. [%.n %balance-transfer-failed]
  340. ::
  341. :: check that the coinbase split adds up to emission+fees
  342. =/ total-split=coins:t
  343. %+ roll ~(val z-by coinbase.pag)
  344. |=([c=coins:t s=coins:t] (add c s))
  345. =/ emission-and-fees=coins:t
  346. (add (emission-calc:coinbase:t height.pag) fees.u.balance-transfer)
  347. ?. =(emission-and-fees total-split)
  348. [%.n %improper-split]
  349. ::~& > "block {digest-b58} txs validated"
  350. [%.y u.balance-transfer]
  351. ::
  352. :: +update-heaviest: set new heaviest block if it is so
  353. ++ update-heaviest
  354. |= pag=page:t
  355. ^- consensus-state:dk
  356. =/ digest-b58=cord (to-b58:hash:t digest.pag)
  357. ::~> %slog.[0 leaf+"checking if block {digest-b58} is heaviest"]
  358. ?: =(~ heaviest-block.c)
  359. :: if we have no heaviest block, this must be genesis block.
  360. ~| "received non-genesis block before genesis block"
  361. ?> =(*page-number:t height.pag)
  362. c(heaviest-block (some digest.pag))
  363. :: > rather than >= since we take the first heaviest block we've heard
  364. ?: %+ compare-heaviness:page:t pag
  365. (~(got z-by blocks.c) (need heaviest-block.c))
  366. =/ print-var
  367. %- trip
  368. ^- @t
  369. %^ cat 3
  370. digest-b58
  371. ' is new heaviest block'
  372. ~> %slog.[0 leaf+print-var]
  373. c(heaviest-block (some digest.pag))
  374. =/ print-var
  375. %- trip
  376. ^- @t
  377. %^ cat 3
  378. digest-b58
  379. ' is NOT new heaviest block'
  380. ~> %slog.[0 leaf+print-var]
  381. c
  382. ::
  383. :: +get-elders: get list of ancestor block IDs up to 24 deep
  384. :: (ordered newest->oldest)
  385. ++ get-elders
  386. |= [d=derived-state:dk bid=block-id:t]
  387. ^- (unit [page-number:t (list block-id:t)])
  388. =/ block (~(get z-by blocks.c) bid)
  389. ?~ block
  390. ~
  391. =/ pag=page:t (to-page:local-page:t u.block)
  392. =/ height=page-number:t height.pag
  393. =/ ids=(list block-id:t) [bid ~]
  394. =/ count 1
  395. |-
  396. ?: =(height *page-number:t) `[height (flop ids)]
  397. ?: =(24 count) `[height (flop ids)]
  398. =/ prev-height=page-number:t (dec height)
  399. =/ prev-id=(unit block-id:t) (~(get z-by heaviest-chain.d) prev-height)
  400. ?~ prev-id
  401. :: if prev-id is null, something is wrong
  402. ~
  403. $(height prev-height, ids [u.prev-id ids], count +(count))
  404. ::
  405. +| %timestamp
  406. :: +update-min-timestamps: sets min timestamp of children of .id
  407. ::
  408. ++ update-min-timestamps
  409. |= [now=@da pag=page:t]
  410. ^- (z-map block-id:t @)
  411. =/ min-timestamp=@
  412. :: get timestamps of up to N=min-past-blocks prior blocks.
  413. =| prev-timestamps=(list @)
  414. =/ b=@ (dec min-past-blocks:t) :: iteration counter
  415. =/ cur-block=page:t pag
  416. |-
  417. =. prev-timestamps [timestamp.cur-block prev-timestamps]
  418. ?: ?| =(0 b) :: we've looked back +min-past-blocks blocks
  419. ::
  420. :: we've reached genesis block
  421. =(*page-number:t height.cur-block)
  422. ==
  423. :: return median of timestamps
  424. (median:t prev-timestamps)
  425. %= $
  426. b (dec b)
  427. cur-block (to-page:local-page:t (~(got z-by blocks.c) parent.cur-block))
  428. ==
  429. ::
  430. (~(put z-by min-timestamps.c) digest.pag min-timestamp)
  431. --