consensus.hoon 14 KB

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