chia-blockchain 2.5.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1042) hide show
  1. chia/__init__.py +10 -0
  2. chia/__main__.py +5 -0
  3. chia/_tests/README.md +53 -0
  4. chia/_tests/__init__.py +0 -0
  5. chia/_tests/blockchain/__init__.py +0 -0
  6. chia/_tests/blockchain/blockchain_test_utils.py +195 -0
  7. chia/_tests/blockchain/config.py +4 -0
  8. chia/_tests/blockchain/test_augmented_chain.py +145 -0
  9. chia/_tests/blockchain/test_blockchain.py +4202 -0
  10. chia/_tests/blockchain/test_blockchain_transactions.py +1031 -0
  11. chia/_tests/blockchain/test_build_chains.py +59 -0
  12. chia/_tests/blockchain/test_get_block_generator.py +72 -0
  13. chia/_tests/blockchain/test_lookup_fork_chain.py +194 -0
  14. chia/_tests/build-init-files.py +92 -0
  15. chia/_tests/build-job-matrix.py +204 -0
  16. chia/_tests/check_pytest_monitor_output.py +34 -0
  17. chia/_tests/check_sql_statements.py +72 -0
  18. chia/_tests/chia-start-sim +42 -0
  19. chia/_tests/clvm/__init__.py +0 -0
  20. chia/_tests/clvm/benchmark_costs.py +23 -0
  21. chia/_tests/clvm/coin_store.py +149 -0
  22. chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
  23. chia/_tests/clvm/test_clvm_step.py +37 -0
  24. chia/_tests/clvm/test_condition_codes.py +13 -0
  25. chia/_tests/clvm/test_curry_and_treehash.py +55 -0
  26. chia/_tests/clvm/test_message_conditions.py +184 -0
  27. chia/_tests/clvm/test_program.py +150 -0
  28. chia/_tests/clvm/test_puzzle_compression.py +143 -0
  29. chia/_tests/clvm/test_puzzle_drivers.py +45 -0
  30. chia/_tests/clvm/test_puzzles.py +242 -0
  31. chia/_tests/clvm/test_singletons.py +540 -0
  32. chia/_tests/clvm/test_spend_sim.py +181 -0
  33. chia/_tests/cmds/__init__.py +0 -0
  34. chia/_tests/cmds/cmd_test_utils.py +469 -0
  35. chia/_tests/cmds/config.py +3 -0
  36. chia/_tests/cmds/conftest.py +23 -0
  37. chia/_tests/cmds/test_click_types.py +200 -0
  38. chia/_tests/cmds/test_cmd_framework.py +620 -0
  39. chia/_tests/cmds/test_cmds_util.py +97 -0
  40. chia/_tests/cmds/test_daemon.py +92 -0
  41. chia/_tests/cmds/test_dev_gh.py +131 -0
  42. chia/_tests/cmds/test_farm_cmd.py +66 -0
  43. chia/_tests/cmds/test_show.py +116 -0
  44. chia/_tests/cmds/test_sim.py +207 -0
  45. chia/_tests/cmds/test_timelock_args.py +75 -0
  46. chia/_tests/cmds/test_tx_config_args.py +154 -0
  47. chia/_tests/cmds/testing_classes.py +59 -0
  48. chia/_tests/cmds/wallet/__init__.py +0 -0
  49. chia/_tests/cmds/wallet/test_consts.py +47 -0
  50. chia/_tests/cmds/wallet/test_dao.py +565 -0
  51. chia/_tests/cmds/wallet/test_did.py +403 -0
  52. chia/_tests/cmds/wallet/test_nft.py +471 -0
  53. chia/_tests/cmds/wallet/test_notifications.py +124 -0
  54. chia/_tests/cmds/wallet/test_offer.toffer +1 -0
  55. chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
  56. chia/_tests/cmds/wallet/test_vcs.py +400 -0
  57. chia/_tests/cmds/wallet/test_wallet.py +1125 -0
  58. chia/_tests/cmds/wallet/test_wallet_check.py +109 -0
  59. chia/_tests/conftest.py +1419 -0
  60. chia/_tests/connection_utils.py +125 -0
  61. chia/_tests/core/__init__.py +0 -0
  62. chia/_tests/core/cmds/__init__.py +0 -0
  63. chia/_tests/core/cmds/test_beta.py +382 -0
  64. chia/_tests/core/cmds/test_keys.py +1734 -0
  65. chia/_tests/core/cmds/test_wallet.py +126 -0
  66. chia/_tests/core/config.py +3 -0
  67. chia/_tests/core/consensus/__init__.py +0 -0
  68. chia/_tests/core/consensus/test_block_creation.py +54 -0
  69. chia/_tests/core/consensus/test_pot_iterations.py +117 -0
  70. chia/_tests/core/custom_types/__init__.py +0 -0
  71. chia/_tests/core/custom_types/test_coin.py +107 -0
  72. chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
  73. chia/_tests/core/custom_types/test_spend_bundle.py +70 -0
  74. chia/_tests/core/daemon/__init__.py +0 -0
  75. chia/_tests/core/daemon/config.py +4 -0
  76. chia/_tests/core/daemon/test_daemon.py +2128 -0
  77. chia/_tests/core/daemon/test_daemon_register.py +109 -0
  78. chia/_tests/core/daemon/test_keychain_proxy.py +101 -0
  79. chia/_tests/core/data_layer/__init__.py +0 -0
  80. chia/_tests/core/data_layer/config.py +5 -0
  81. chia/_tests/core/data_layer/conftest.py +106 -0
  82. chia/_tests/core/data_layer/test_data_cli.py +56 -0
  83. chia/_tests/core/data_layer/test_data_layer.py +83 -0
  84. chia/_tests/core/data_layer/test_data_layer_util.py +218 -0
  85. chia/_tests/core/data_layer/test_data_rpc.py +3847 -0
  86. chia/_tests/core/data_layer/test_data_store.py +2424 -0
  87. chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
  88. chia/_tests/core/data_layer/test_plugin.py +91 -0
  89. chia/_tests/core/data_layer/util.py +233 -0
  90. chia/_tests/core/farmer/__init__.py +0 -0
  91. chia/_tests/core/farmer/config.py +3 -0
  92. chia/_tests/core/farmer/test_farmer_api.py +103 -0
  93. chia/_tests/core/full_node/__init__.py +0 -0
  94. chia/_tests/core/full_node/config.py +4 -0
  95. chia/_tests/core/full_node/dos/__init__.py +0 -0
  96. chia/_tests/core/full_node/dos/config.py +3 -0
  97. chia/_tests/core/full_node/full_sync/__init__.py +0 -0
  98. chia/_tests/core/full_node/full_sync/config.py +4 -0
  99. chia/_tests/core/full_node/full_sync/test_full_sync.py +443 -0
  100. chia/_tests/core/full_node/ram_db.py +27 -0
  101. chia/_tests/core/full_node/stores/__init__.py +0 -0
  102. chia/_tests/core/full_node/stores/config.py +4 -0
  103. chia/_tests/core/full_node/stores/test_block_store.py +590 -0
  104. chia/_tests/core/full_node/stores/test_coin_store.py +897 -0
  105. chia/_tests/core/full_node/stores/test_full_node_store.py +1219 -0
  106. chia/_tests/core/full_node/stores/test_hint_store.py +229 -0
  107. chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
  108. chia/_tests/core/full_node/test_address_manager.py +588 -0
  109. chia/_tests/core/full_node/test_block_height_map.py +556 -0
  110. chia/_tests/core/full_node/test_conditions.py +556 -0
  111. chia/_tests/core/full_node/test_full_node.py +2700 -0
  112. chia/_tests/core/full_node/test_generator_tools.py +82 -0
  113. chia/_tests/core/full_node/test_hint_management.py +104 -0
  114. chia/_tests/core/full_node/test_node_load.py +34 -0
  115. chia/_tests/core/full_node/test_performance.py +179 -0
  116. chia/_tests/core/full_node/test_subscriptions.py +492 -0
  117. chia/_tests/core/full_node/test_transactions.py +203 -0
  118. chia/_tests/core/full_node/test_tx_processing_queue.py +155 -0
  119. chia/_tests/core/large_block.py +2388 -0
  120. chia/_tests/core/make_block_generator.py +70 -0
  121. chia/_tests/core/mempool/__init__.py +0 -0
  122. chia/_tests/core/mempool/config.py +4 -0
  123. chia/_tests/core/mempool/test_mempool.py +3255 -0
  124. chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
  125. chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
  126. chia/_tests/core/mempool/test_mempool_item_queries.py +190 -0
  127. chia/_tests/core/mempool/test_mempool_manager.py +2084 -0
  128. chia/_tests/core/mempool/test_mempool_performance.py +64 -0
  129. chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
  130. chia/_tests/core/node_height.py +28 -0
  131. chia/_tests/core/server/__init__.py +0 -0
  132. chia/_tests/core/server/config.py +3 -0
  133. chia/_tests/core/server/flood.py +84 -0
  134. chia/_tests/core/server/serve.py +135 -0
  135. chia/_tests/core/server/test_api_protocol.py +21 -0
  136. chia/_tests/core/server/test_capabilities.py +66 -0
  137. chia/_tests/core/server/test_dos.py +319 -0
  138. chia/_tests/core/server/test_event_loop.py +109 -0
  139. chia/_tests/core/server/test_loop.py +294 -0
  140. chia/_tests/core/server/test_node_discovery.py +73 -0
  141. chia/_tests/core/server/test_rate_limits.py +482 -0
  142. chia/_tests/core/server/test_server.py +226 -0
  143. chia/_tests/core/server/test_upnp.py +8 -0
  144. chia/_tests/core/services/__init__.py +0 -0
  145. chia/_tests/core/services/config.py +3 -0
  146. chia/_tests/core/services/test_services.py +188 -0
  147. chia/_tests/core/ssl/__init__.py +0 -0
  148. chia/_tests/core/ssl/config.py +3 -0
  149. chia/_tests/core/ssl/test_ssl.py +202 -0
  150. chia/_tests/core/test_coins.py +33 -0
  151. chia/_tests/core/test_cost_calculation.py +313 -0
  152. chia/_tests/core/test_crawler.py +175 -0
  153. chia/_tests/core/test_crawler_rpc.py +53 -0
  154. chia/_tests/core/test_daemon_rpc.py +24 -0
  155. chia/_tests/core/test_db_conversion.py +130 -0
  156. chia/_tests/core/test_db_validation.py +162 -0
  157. chia/_tests/core/test_farmer_harvester_rpc.py +505 -0
  158. chia/_tests/core/test_filter.py +35 -0
  159. chia/_tests/core/test_full_node_rpc.py +768 -0
  160. chia/_tests/core/test_merkle_set.py +343 -0
  161. chia/_tests/core/test_program.py +47 -0
  162. chia/_tests/core/test_rpc_util.py +86 -0
  163. chia/_tests/core/test_seeder.py +420 -0
  164. chia/_tests/core/test_setproctitle.py +13 -0
  165. chia/_tests/core/util/__init__.py +0 -0
  166. chia/_tests/core/util/config.py +4 -0
  167. chia/_tests/core/util/test_block_cache.py +44 -0
  168. chia/_tests/core/util/test_cached_bls.py +57 -0
  169. chia/_tests/core/util/test_config.py +337 -0
  170. chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
  171. chia/_tests/core/util/test_files.py +391 -0
  172. chia/_tests/core/util/test_jsonify.py +146 -0
  173. chia/_tests/core/util/test_keychain.py +522 -0
  174. chia/_tests/core/util/test_keyring_wrapper.py +491 -0
  175. chia/_tests/core/util/test_lockfile.py +380 -0
  176. chia/_tests/core/util/test_log_exceptions.py +187 -0
  177. chia/_tests/core/util/test_lru_cache.py +56 -0
  178. chia/_tests/core/util/test_significant_bits.py +40 -0
  179. chia/_tests/core/util/test_streamable.py +883 -0
  180. chia/_tests/db/__init__.py +0 -0
  181. chia/_tests/db/test_db_wrapper.py +566 -0
  182. chia/_tests/environments/__init__.py +0 -0
  183. chia/_tests/environments/common.py +35 -0
  184. chia/_tests/environments/full_node.py +47 -0
  185. chia/_tests/environments/wallet.py +429 -0
  186. chia/_tests/ether.py +19 -0
  187. chia/_tests/farmer_harvester/__init__.py +0 -0
  188. chia/_tests/farmer_harvester/config.py +3 -0
  189. chia/_tests/farmer_harvester/test_farmer.py +1264 -0
  190. chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
  191. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +131 -0
  192. chia/_tests/farmer_harvester/test_third_party_harvesters.py +528 -0
  193. chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
  194. chia/_tests/fee_estimation/__init__.py +0 -0
  195. chia/_tests/fee_estimation/config.py +3 -0
  196. chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
  197. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
  198. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +144 -0
  199. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
  200. chia/_tests/generator/__init__.py +0 -0
  201. chia/_tests/generator/puzzles/__init__.py +0 -0
  202. chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
  203. chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
  204. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
  205. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
  206. chia/_tests/generator/test_compression.py +201 -0
  207. chia/_tests/generator/test_generator_types.py +44 -0
  208. chia/_tests/generator/test_rom.py +180 -0
  209. chia/_tests/plot_sync/__init__.py +0 -0
  210. chia/_tests/plot_sync/config.py +3 -0
  211. chia/_tests/plot_sync/test_delta.py +101 -0
  212. chia/_tests/plot_sync/test_plot_sync.py +618 -0
  213. chia/_tests/plot_sync/test_receiver.py +451 -0
  214. chia/_tests/plot_sync/test_sender.py +116 -0
  215. chia/_tests/plot_sync/test_sync_simulated.py +451 -0
  216. chia/_tests/plot_sync/util.py +68 -0
  217. chia/_tests/plotting/__init__.py +0 -0
  218. chia/_tests/plotting/config.py +3 -0
  219. chia/_tests/plotting/test_plot_manager.py +781 -0
  220. chia/_tests/plotting/util.py +12 -0
  221. chia/_tests/pools/__init__.py +0 -0
  222. chia/_tests/pools/config.py +5 -0
  223. chia/_tests/pools/test_pool_cli_parsing.py +128 -0
  224. chia/_tests/pools/test_pool_cmdline.py +1001 -0
  225. chia/_tests/pools/test_pool_config.py +42 -0
  226. chia/_tests/pools/test_pool_puzzles_lifecycle.py +397 -0
  227. chia/_tests/pools/test_pool_rpc.py +1123 -0
  228. chia/_tests/pools/test_pool_wallet.py +205 -0
  229. chia/_tests/pools/test_wallet_pool_store.py +161 -0
  230. chia/_tests/process_junit.py +348 -0
  231. chia/_tests/rpc/__init__.py +0 -0
  232. chia/_tests/rpc/test_rpc_client.py +138 -0
  233. chia/_tests/rpc/test_rpc_server.py +183 -0
  234. chia/_tests/simulation/__init__.py +0 -0
  235. chia/_tests/simulation/config.py +6 -0
  236. chia/_tests/simulation/test_simulation.py +501 -0
  237. chia/_tests/simulation/test_simulator.py +232 -0
  238. chia/_tests/simulation/test_start_simulator.py +107 -0
  239. chia/_tests/testconfig.py +13 -0
  240. chia/_tests/timelord/__init__.py +0 -0
  241. chia/_tests/timelord/config.py +3 -0
  242. chia/_tests/timelord/test_new_peak.py +437 -0
  243. chia/_tests/timelord/test_timelord.py +11 -0
  244. chia/_tests/tools/1315537.json +170 -0
  245. chia/_tests/tools/1315544.json +160 -0
  246. chia/_tests/tools/1315630.json +150 -0
  247. chia/_tests/tools/300000.json +105 -0
  248. chia/_tests/tools/442734.json +140 -0
  249. chia/_tests/tools/466212.json +130 -0
  250. chia/_tests/tools/__init__.py +0 -0
  251. chia/_tests/tools/config.py +5 -0
  252. chia/_tests/tools/test-blockchain-db.sqlite +0 -0
  253. chia/_tests/tools/test_full_sync.py +30 -0
  254. chia/_tests/tools/test_legacy_keyring.py +82 -0
  255. chia/_tests/tools/test_run_block.py +128 -0
  256. chia/_tests/tools/test_virtual_project.py +591 -0
  257. chia/_tests/util/__init__.py +0 -0
  258. chia/_tests/util/benchmark_cost.py +170 -0
  259. chia/_tests/util/benchmarks.py +153 -0
  260. chia/_tests/util/bip39_test_vectors.json +148 -0
  261. chia/_tests/util/blockchain.py +134 -0
  262. chia/_tests/util/blockchain_mock.py +132 -0
  263. chia/_tests/util/build_network_protocol_files.py +302 -0
  264. chia/_tests/util/clvm_generator.bin +0 -0
  265. chia/_tests/util/config.py +3 -0
  266. chia/_tests/util/constants.py +20 -0
  267. chia/_tests/util/db_connection.py +37 -0
  268. chia/_tests/util/full_sync.py +253 -0
  269. chia/_tests/util/gen_ssl_certs.py +114 -0
  270. chia/_tests/util/generator_tools_testing.py +45 -0
  271. chia/_tests/util/get_name_puzzle_conditions.py +52 -0
  272. chia/_tests/util/key_tool.py +36 -0
  273. chia/_tests/util/misc.py +675 -0
  274. chia/_tests/util/network_protocol_data.py +1072 -0
  275. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  276. chia/_tests/util/protocol_messages_json.py +2701 -0
  277. chia/_tests/util/rpc.py +26 -0
  278. chia/_tests/util/run_block.py +163 -0
  279. chia/_tests/util/setup_nodes.py +481 -0
  280. chia/_tests/util/spend_sim.py +492 -0
  281. chia/_tests/util/split_managers.py +102 -0
  282. chia/_tests/util/temp_file.py +14 -0
  283. chia/_tests/util/test_action_scope.py +144 -0
  284. chia/_tests/util/test_async_pool.py +366 -0
  285. chia/_tests/util/test_build_job_matrix.py +42 -0
  286. chia/_tests/util/test_build_network_protocol_files.py +7 -0
  287. chia/_tests/util/test_chia_version.py +50 -0
  288. chia/_tests/util/test_collection.py +11 -0
  289. chia/_tests/util/test_condition_tools.py +229 -0
  290. chia/_tests/util/test_config.py +426 -0
  291. chia/_tests/util/test_dump_keyring.py +60 -0
  292. chia/_tests/util/test_errors.py +10 -0
  293. chia/_tests/util/test_full_block_utils.py +279 -0
  294. chia/_tests/util/test_installed.py +20 -0
  295. chia/_tests/util/test_limited_semaphore.py +53 -0
  296. chia/_tests/util/test_logging_filter.py +42 -0
  297. chia/_tests/util/test_misc.py +445 -0
  298. chia/_tests/util/test_network.py +73 -0
  299. chia/_tests/util/test_network_protocol_files.py +578 -0
  300. chia/_tests/util/test_network_protocol_json.py +267 -0
  301. chia/_tests/util/test_network_protocol_test.py +256 -0
  302. chia/_tests/util/test_paginator.py +71 -0
  303. chia/_tests/util/test_pprint.py +17 -0
  304. chia/_tests/util/test_priority_mutex.py +488 -0
  305. chia/_tests/util/test_recursive_replace.py +116 -0
  306. chia/_tests/util/test_replace_str_to_bytes.py +137 -0
  307. chia/_tests/util/test_service_groups.py +15 -0
  308. chia/_tests/util/test_ssl_check.py +31 -0
  309. chia/_tests/util/test_testnet_overrides.py +19 -0
  310. chia/_tests/util/test_tests_misc.py +38 -0
  311. chia/_tests/util/test_timing.py +37 -0
  312. chia/_tests/util/test_trusted_peer.py +51 -0
  313. chia/_tests/util/time_out_assert.py +191 -0
  314. chia/_tests/wallet/__init__.py +0 -0
  315. chia/_tests/wallet/cat_wallet/__init__.py +0 -0
  316. chia/_tests/wallet/cat_wallet/config.py +4 -0
  317. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
  318. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
  319. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1826 -0
  320. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
  321. chia/_tests/wallet/cat_wallet/test_trades.py +2600 -0
  322. chia/_tests/wallet/clawback/__init__.py +0 -0
  323. chia/_tests/wallet/clawback/config.py +3 -0
  324. chia/_tests/wallet/clawback/test_clawback_decorator.py +78 -0
  325. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
  326. chia/_tests/wallet/clawback/test_clawback_metadata.py +50 -0
  327. chia/_tests/wallet/config.py +4 -0
  328. chia/_tests/wallet/conftest.py +278 -0
  329. chia/_tests/wallet/dao_wallet/__init__.py +0 -0
  330. chia/_tests/wallet/dao_wallet/config.py +3 -0
  331. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1330 -0
  332. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
  333. chia/_tests/wallet/db_wallet/__init__.py +0 -0
  334. chia/_tests/wallet/db_wallet/config.py +3 -0
  335. chia/_tests/wallet/db_wallet/test_db_graftroot.py +141 -0
  336. chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
  337. chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
  338. chia/_tests/wallet/did_wallet/__init__.py +0 -0
  339. chia/_tests/wallet/did_wallet/config.py +4 -0
  340. chia/_tests/wallet/did_wallet/test_did.py +2284 -0
  341. chia/_tests/wallet/nft_wallet/__init__.py +0 -0
  342. chia/_tests/wallet/nft_wallet/config.py +4 -0
  343. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1493 -0
  344. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1024 -0
  345. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +375 -0
  346. chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
  347. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
  348. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2584 -0
  349. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
  350. chia/_tests/wallet/rpc/__init__.py +0 -0
  351. chia/_tests/wallet/rpc/config.py +4 -0
  352. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +285 -0
  353. chia/_tests/wallet/rpc/test_wallet_rpc.py +3153 -0
  354. chia/_tests/wallet/simple_sync/__init__.py +0 -0
  355. chia/_tests/wallet/simple_sync/config.py +3 -0
  356. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +718 -0
  357. chia/_tests/wallet/sync/__init__.py +0 -0
  358. chia/_tests/wallet/sync/config.py +4 -0
  359. chia/_tests/wallet/sync/test_wallet_sync.py +1692 -0
  360. chia/_tests/wallet/test_address_type.py +189 -0
  361. chia/_tests/wallet/test_bech32m.py +45 -0
  362. chia/_tests/wallet/test_clvm_streamable.py +244 -0
  363. chia/_tests/wallet/test_coin_management.py +354 -0
  364. chia/_tests/wallet/test_coin_selection.py +588 -0
  365. chia/_tests/wallet/test_conditions.py +400 -0
  366. chia/_tests/wallet/test_debug_spend_bundle.py +218 -0
  367. chia/_tests/wallet/test_new_wallet_protocol.py +1174 -0
  368. chia/_tests/wallet/test_nft_store.py +192 -0
  369. chia/_tests/wallet/test_notifications.py +196 -0
  370. chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
  371. chia/_tests/wallet/test_puzzle_store.py +132 -0
  372. chia/_tests/wallet/test_sign_coin_spends.py +159 -0
  373. chia/_tests/wallet/test_signer_protocol.py +947 -0
  374. chia/_tests/wallet/test_singleton.py +122 -0
  375. chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
  376. chia/_tests/wallet/test_singleton_store.py +152 -0
  377. chia/_tests/wallet/test_taproot.py +19 -0
  378. chia/_tests/wallet/test_transaction_store.py +945 -0
  379. chia/_tests/wallet/test_util.py +185 -0
  380. chia/_tests/wallet/test_wallet.py +2139 -0
  381. chia/_tests/wallet/test_wallet_action_scope.py +85 -0
  382. chia/_tests/wallet/test_wallet_blockchain.py +111 -0
  383. chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
  384. chia/_tests/wallet/test_wallet_interested_store.py +43 -0
  385. chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
  386. chia/_tests/wallet/test_wallet_node.py +780 -0
  387. chia/_tests/wallet/test_wallet_retry.py +95 -0
  388. chia/_tests/wallet/test_wallet_state_manager.py +259 -0
  389. chia/_tests/wallet/test_wallet_test_framework.py +275 -0
  390. chia/_tests/wallet/test_wallet_trade_store.py +218 -0
  391. chia/_tests/wallet/test_wallet_user_store.py +34 -0
  392. chia/_tests/wallet/test_wallet_utils.py +156 -0
  393. chia/_tests/wallet/vc_wallet/__init__.py +0 -0
  394. chia/_tests/wallet/vc_wallet/config.py +3 -0
  395. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
  396. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
  397. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +830 -0
  398. chia/_tests/wallet/wallet_block_tools.py +327 -0
  399. chia/_tests/weight_proof/__init__.py +0 -0
  400. chia/_tests/weight_proof/config.py +3 -0
  401. chia/_tests/weight_proof/test_weight_proof.py +528 -0
  402. chia/apis.py +19 -0
  403. chia/clvm/__init__.py +0 -0
  404. chia/cmds/__init__.py +0 -0
  405. chia/cmds/beta.py +184 -0
  406. chia/cmds/beta_funcs.py +137 -0
  407. chia/cmds/check_wallet_db.py +420 -0
  408. chia/cmds/chia.py +151 -0
  409. chia/cmds/cmd_classes.py +323 -0
  410. chia/cmds/cmd_helpers.py +242 -0
  411. chia/cmds/cmds_util.py +488 -0
  412. chia/cmds/coin_funcs.py +275 -0
  413. chia/cmds/coins.py +182 -0
  414. chia/cmds/completion.py +49 -0
  415. chia/cmds/configure.py +332 -0
  416. chia/cmds/dao.py +1064 -0
  417. chia/cmds/dao_funcs.py +598 -0
  418. chia/cmds/data.py +708 -0
  419. chia/cmds/data_funcs.py +385 -0
  420. chia/cmds/db.py +87 -0
  421. chia/cmds/db_backup_func.py +77 -0
  422. chia/cmds/db_upgrade_func.py +452 -0
  423. chia/cmds/db_validate_func.py +184 -0
  424. chia/cmds/dev.py +18 -0
  425. chia/cmds/farm.py +100 -0
  426. chia/cmds/farm_funcs.py +200 -0
  427. chia/cmds/gh.py +275 -0
  428. chia/cmds/init.py +63 -0
  429. chia/cmds/init_funcs.py +367 -0
  430. chia/cmds/installers.py +131 -0
  431. chia/cmds/keys.py +527 -0
  432. chia/cmds/keys_funcs.py +863 -0
  433. chia/cmds/netspace.py +50 -0
  434. chia/cmds/netspace_funcs.py +54 -0
  435. chia/cmds/options.py +32 -0
  436. chia/cmds/param_types.py +238 -0
  437. chia/cmds/passphrase.py +131 -0
  438. chia/cmds/passphrase_funcs.py +292 -0
  439. chia/cmds/peer.py +51 -0
  440. chia/cmds/peer_funcs.py +129 -0
  441. chia/cmds/plotnft.py +260 -0
  442. chia/cmds/plotnft_funcs.py +405 -0
  443. chia/cmds/plots.py +230 -0
  444. chia/cmds/plotters.py +18 -0
  445. chia/cmds/rpc.py +208 -0
  446. chia/cmds/show.py +72 -0
  447. chia/cmds/show_funcs.py +215 -0
  448. chia/cmds/signer.py +296 -0
  449. chia/cmds/sim.py +225 -0
  450. chia/cmds/sim_funcs.py +509 -0
  451. chia/cmds/start.py +24 -0
  452. chia/cmds/start_funcs.py +109 -0
  453. chia/cmds/stop.py +62 -0
  454. chia/cmds/units.py +9 -0
  455. chia/cmds/wallet.py +1901 -0
  456. chia/cmds/wallet_funcs.py +1874 -0
  457. chia/consensus/__init__.py +0 -0
  458. chia/consensus/block_body_validation.py +562 -0
  459. chia/consensus/block_creation.py +546 -0
  460. chia/consensus/block_header_validation.py +1059 -0
  461. chia/consensus/block_record.py +31 -0
  462. chia/consensus/block_rewards.py +53 -0
  463. chia/consensus/blockchain.py +1087 -0
  464. chia/consensus/blockchain_interface.py +56 -0
  465. chia/consensus/coinbase.py +30 -0
  466. chia/consensus/condition_costs.py +9 -0
  467. chia/consensus/constants.py +49 -0
  468. chia/consensus/cost_calculator.py +15 -0
  469. chia/consensus/default_constants.py +89 -0
  470. chia/consensus/deficit.py +55 -0
  471. chia/consensus/difficulty_adjustment.py +412 -0
  472. chia/consensus/find_fork_point.py +111 -0
  473. chia/consensus/full_block_to_block_record.py +167 -0
  474. chia/consensus/get_block_challenge.py +106 -0
  475. chia/consensus/get_block_generator.py +27 -0
  476. chia/consensus/make_sub_epoch_summary.py +210 -0
  477. chia/consensus/multiprocess_validation.py +268 -0
  478. chia/consensus/pos_quality.py +19 -0
  479. chia/consensus/pot_iterations.py +67 -0
  480. chia/consensus/puzzles/__init__.py +0 -0
  481. chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
  482. chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
  483. chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
  484. chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
  485. chia/consensus/vdf_info_computation.py +156 -0
  486. chia/daemon/__init__.py +0 -0
  487. chia/daemon/client.py +252 -0
  488. chia/daemon/keychain_proxy.py +502 -0
  489. chia/daemon/keychain_server.py +365 -0
  490. chia/daemon/server.py +1606 -0
  491. chia/daemon/windows_signal.py +56 -0
  492. chia/data_layer/__init__.py +0 -0
  493. chia/data_layer/data_layer.py +1291 -0
  494. chia/data_layer/data_layer_api.py +33 -0
  495. chia/data_layer/data_layer_errors.py +50 -0
  496. chia/data_layer/data_layer_server.py +170 -0
  497. chia/data_layer/data_layer_util.py +985 -0
  498. chia/data_layer/data_layer_wallet.py +1311 -0
  499. chia/data_layer/data_store.py +2267 -0
  500. chia/data_layer/dl_wallet_store.py +407 -0
  501. chia/data_layer/download_data.py +389 -0
  502. chia/data_layer/puzzles/__init__.py +0 -0
  503. chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
  504. chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
  505. chia/data_layer/s3_plugin_config.yml +33 -0
  506. chia/data_layer/s3_plugin_service.py +468 -0
  507. chia/data_layer/util/__init__.py +0 -0
  508. chia/data_layer/util/benchmark.py +107 -0
  509. chia/data_layer/util/plugin.py +40 -0
  510. chia/farmer/__init__.py +0 -0
  511. chia/farmer/farmer.py +923 -0
  512. chia/farmer/farmer_api.py +820 -0
  513. chia/full_node/__init__.py +0 -0
  514. chia/full_node/bitcoin_fee_estimator.py +85 -0
  515. chia/full_node/block_height_map.py +271 -0
  516. chia/full_node/block_store.py +576 -0
  517. chia/full_node/bundle_tools.py +19 -0
  518. chia/full_node/coin_store.py +647 -0
  519. chia/full_node/fee_estimate.py +54 -0
  520. chia/full_node/fee_estimate_store.py +24 -0
  521. chia/full_node/fee_estimation.py +92 -0
  522. chia/full_node/fee_estimator.py +90 -0
  523. chia/full_node/fee_estimator_constants.py +38 -0
  524. chia/full_node/fee_estimator_interface.py +42 -0
  525. chia/full_node/fee_history.py +25 -0
  526. chia/full_node/fee_tracker.py +564 -0
  527. chia/full_node/full_node.py +3327 -0
  528. chia/full_node/full_node_api.py +2025 -0
  529. chia/full_node/full_node_store.py +1033 -0
  530. chia/full_node/hint_management.py +56 -0
  531. chia/full_node/hint_store.py +93 -0
  532. chia/full_node/mempool.py +589 -0
  533. chia/full_node/mempool_check_conditions.py +146 -0
  534. chia/full_node/mempool_manager.py +853 -0
  535. chia/full_node/pending_tx_cache.py +112 -0
  536. chia/full_node/puzzles/__init__.py +0 -0
  537. chia/full_node/puzzles/block_program_zero.clsp +14 -0
  538. chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
  539. chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
  540. chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
  541. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
  542. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
  543. chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
  544. chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
  545. chia/full_node/signage_point.py +16 -0
  546. chia/full_node/subscriptions.py +247 -0
  547. chia/full_node/sync_store.py +146 -0
  548. chia/full_node/tx_processing_queue.py +78 -0
  549. chia/full_node/util/__init__.py +0 -0
  550. chia/full_node/weight_proof.py +1720 -0
  551. chia/harvester/__init__.py +0 -0
  552. chia/harvester/harvester.py +272 -0
  553. chia/harvester/harvester_api.py +380 -0
  554. chia/introducer/__init__.py +0 -0
  555. chia/introducer/introducer.py +122 -0
  556. chia/introducer/introducer_api.py +70 -0
  557. chia/legacy/__init__.py +0 -0
  558. chia/legacy/keyring.py +155 -0
  559. chia/plot_sync/__init__.py +0 -0
  560. chia/plot_sync/delta.py +61 -0
  561. chia/plot_sync/exceptions.py +56 -0
  562. chia/plot_sync/receiver.py +386 -0
  563. chia/plot_sync/sender.py +340 -0
  564. chia/plot_sync/util.py +43 -0
  565. chia/plotters/__init__.py +0 -0
  566. chia/plotters/bladebit.py +388 -0
  567. chia/plotters/chiapos.py +63 -0
  568. chia/plotters/madmax.py +224 -0
  569. chia/plotters/plotters.py +577 -0
  570. chia/plotters/plotters_util.py +133 -0
  571. chia/plotting/__init__.py +0 -0
  572. chia/plotting/cache.py +213 -0
  573. chia/plotting/check_plots.py +283 -0
  574. chia/plotting/create_plots.py +278 -0
  575. chia/plotting/manager.py +436 -0
  576. chia/plotting/util.py +336 -0
  577. chia/pools/__init__.py +0 -0
  578. chia/pools/pool_config.py +110 -0
  579. chia/pools/pool_puzzles.py +459 -0
  580. chia/pools/pool_wallet.py +933 -0
  581. chia/pools/pool_wallet_info.py +118 -0
  582. chia/pools/puzzles/__init__.py +0 -0
  583. chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
  584. chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
  585. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
  586. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
  587. chia/protocols/__init__.py +0 -0
  588. chia/protocols/farmer_protocol.py +102 -0
  589. chia/protocols/full_node_protocol.py +219 -0
  590. chia/protocols/harvester_protocol.py +216 -0
  591. chia/protocols/introducer_protocol.py +25 -0
  592. chia/protocols/pool_protocol.py +177 -0
  593. chia/protocols/protocol_message_types.py +139 -0
  594. chia/protocols/protocol_state_machine.py +87 -0
  595. chia/protocols/protocol_timing.py +8 -0
  596. chia/protocols/shared_protocol.py +86 -0
  597. chia/protocols/timelord_protocol.py +93 -0
  598. chia/protocols/wallet_protocol.py +401 -0
  599. chia/py.typed +0 -0
  600. chia/rpc/__init__.py +0 -0
  601. chia/rpc/crawler_rpc_api.py +80 -0
  602. chia/rpc/data_layer_rpc_api.py +644 -0
  603. chia/rpc/data_layer_rpc_client.py +188 -0
  604. chia/rpc/data_layer_rpc_util.py +58 -0
  605. chia/rpc/farmer_rpc_api.py +365 -0
  606. chia/rpc/farmer_rpc_client.py +86 -0
  607. chia/rpc/full_node_rpc_api.py +959 -0
  608. chia/rpc/full_node_rpc_client.py +292 -0
  609. chia/rpc/harvester_rpc_api.py +141 -0
  610. chia/rpc/harvester_rpc_client.py +54 -0
  611. chia/rpc/rpc_client.py +164 -0
  612. chia/rpc/rpc_server.py +521 -0
  613. chia/rpc/timelord_rpc_api.py +32 -0
  614. chia/rpc/util.py +93 -0
  615. chia/rpc/wallet_request_types.py +904 -0
  616. chia/rpc/wallet_rpc_api.py +4943 -0
  617. chia/rpc/wallet_rpc_client.py +1814 -0
  618. chia/seeder/__init__.py +0 -0
  619. chia/seeder/crawl_store.py +425 -0
  620. chia/seeder/crawler.py +410 -0
  621. chia/seeder/crawler_api.py +135 -0
  622. chia/seeder/dns_server.py +593 -0
  623. chia/seeder/peer_record.py +146 -0
  624. chia/seeder/start_crawler.py +92 -0
  625. chia/server/__init__.py +0 -0
  626. chia/server/address_manager.py +658 -0
  627. chia/server/address_manager_store.py +237 -0
  628. chia/server/api_protocol.py +116 -0
  629. chia/server/capabilities.py +24 -0
  630. chia/server/chia_policy.py +346 -0
  631. chia/server/introducer_peers.py +76 -0
  632. chia/server/node_discovery.py +714 -0
  633. chia/server/outbound_message.py +33 -0
  634. chia/server/rate_limit_numbers.py +214 -0
  635. chia/server/rate_limits.py +153 -0
  636. chia/server/server.py +741 -0
  637. chia/server/signal_handlers.py +120 -0
  638. chia/server/ssl_context.py +32 -0
  639. chia/server/start_data_layer.py +151 -0
  640. chia/server/start_farmer.py +98 -0
  641. chia/server/start_full_node.py +112 -0
  642. chia/server/start_harvester.py +93 -0
  643. chia/server/start_introducer.py +81 -0
  644. chia/server/start_service.py +316 -0
  645. chia/server/start_timelord.py +89 -0
  646. chia/server/start_wallet.py +113 -0
  647. chia/server/upnp.py +118 -0
  648. chia/server/ws_connection.py +766 -0
  649. chia/simulator/__init__.py +0 -0
  650. chia/simulator/add_blocks_in_batches.py +54 -0
  651. chia/simulator/block_tools.py +2054 -0
  652. chia/simulator/full_node_simulator.py +794 -0
  653. chia/simulator/keyring.py +128 -0
  654. chia/simulator/setup_services.py +506 -0
  655. chia/simulator/simulator_constants.py +13 -0
  656. chia/simulator/simulator_full_node_rpc_api.py +99 -0
  657. chia/simulator/simulator_full_node_rpc_client.py +60 -0
  658. chia/simulator/simulator_protocol.py +29 -0
  659. chia/simulator/simulator_test_tools.py +164 -0
  660. chia/simulator/socket.py +24 -0
  661. chia/simulator/ssl_certs.py +114 -0
  662. chia/simulator/ssl_certs_1.py +697 -0
  663. chia/simulator/ssl_certs_10.py +697 -0
  664. chia/simulator/ssl_certs_2.py +697 -0
  665. chia/simulator/ssl_certs_3.py +697 -0
  666. chia/simulator/ssl_certs_4.py +697 -0
  667. chia/simulator/ssl_certs_5.py +697 -0
  668. chia/simulator/ssl_certs_6.py +697 -0
  669. chia/simulator/ssl_certs_7.py +697 -0
  670. chia/simulator/ssl_certs_8.py +697 -0
  671. chia/simulator/ssl_certs_9.py +697 -0
  672. chia/simulator/start_simulator.py +143 -0
  673. chia/simulator/wallet_tools.py +246 -0
  674. chia/ssl/__init__.py +0 -0
  675. chia/ssl/chia_ca.crt +19 -0
  676. chia/ssl/chia_ca.key +28 -0
  677. chia/ssl/create_ssl.py +249 -0
  678. chia/ssl/dst_root_ca.pem +20 -0
  679. chia/timelord/__init__.py +0 -0
  680. chia/timelord/iters_from_block.py +50 -0
  681. chia/timelord/timelord.py +1226 -0
  682. chia/timelord/timelord_api.py +138 -0
  683. chia/timelord/timelord_launcher.py +190 -0
  684. chia/timelord/timelord_state.py +244 -0
  685. chia/timelord/types.py +22 -0
  686. chia/types/__init__.py +0 -0
  687. chia/types/aliases.py +35 -0
  688. chia/types/block_protocol.py +20 -0
  689. chia/types/blockchain_format/__init__.py +0 -0
  690. chia/types/blockchain_format/classgroup.py +5 -0
  691. chia/types/blockchain_format/coin.py +28 -0
  692. chia/types/blockchain_format/foliage.py +8 -0
  693. chia/types/blockchain_format/pool_target.py +5 -0
  694. chia/types/blockchain_format/program.py +269 -0
  695. chia/types/blockchain_format/proof_of_space.py +135 -0
  696. chia/types/blockchain_format/reward_chain_block.py +6 -0
  697. chia/types/blockchain_format/serialized_program.py +5 -0
  698. chia/types/blockchain_format/sized_bytes.py +11 -0
  699. chia/types/blockchain_format/slots.py +9 -0
  700. chia/types/blockchain_format/sub_epoch_summary.py +5 -0
  701. chia/types/blockchain_format/tree_hash.py +72 -0
  702. chia/types/blockchain_format/vdf.py +86 -0
  703. chia/types/clvm_cost.py +13 -0
  704. chia/types/coin_record.py +43 -0
  705. chia/types/coin_spend.py +115 -0
  706. chia/types/condition_opcodes.py +73 -0
  707. chia/types/condition_with_args.py +16 -0
  708. chia/types/eligible_coin_spends.py +365 -0
  709. chia/types/end_of_slot_bundle.py +5 -0
  710. chia/types/fee_rate.py +38 -0
  711. chia/types/full_block.py +5 -0
  712. chia/types/generator_types.py +13 -0
  713. chia/types/header_block.py +5 -0
  714. chia/types/internal_mempool_item.py +18 -0
  715. chia/types/mempool_inclusion_status.py +9 -0
  716. chia/types/mempool_item.py +85 -0
  717. chia/types/mempool_submission_status.py +30 -0
  718. chia/types/mojos.py +7 -0
  719. chia/types/peer_info.py +64 -0
  720. chia/types/signing_mode.py +29 -0
  721. chia/types/spend_bundle.py +30 -0
  722. chia/types/spend_bundle_conditions.py +7 -0
  723. chia/types/transaction_queue_entry.py +55 -0
  724. chia/types/unfinished_block.py +5 -0
  725. chia/types/unfinished_header_block.py +37 -0
  726. chia/types/validation_state.py +14 -0
  727. chia/types/weight_proof.py +49 -0
  728. chia/util/__init__.py +0 -0
  729. chia/util/action_scope.py +168 -0
  730. chia/util/async_pool.py +226 -0
  731. chia/util/augmented_chain.py +134 -0
  732. chia/util/batches.py +42 -0
  733. chia/util/bech32m.py +126 -0
  734. chia/util/beta_metrics.py +119 -0
  735. chia/util/block_cache.py +56 -0
  736. chia/util/byte_types.py +12 -0
  737. chia/util/check_fork_next_block.py +33 -0
  738. chia/util/chia_logging.py +144 -0
  739. chia/util/chia_version.py +33 -0
  740. chia/util/collection.py +17 -0
  741. chia/util/condition_tools.py +201 -0
  742. chia/util/config.py +367 -0
  743. chia/util/cpu.py +22 -0
  744. chia/util/db_synchronous.py +23 -0
  745. chia/util/db_version.py +32 -0
  746. chia/util/db_wrapper.py +430 -0
  747. chia/util/default_root.py +27 -0
  748. chia/util/dump_keyring.py +93 -0
  749. chia/util/english.txt +2048 -0
  750. chia/util/errors.py +353 -0
  751. chia/util/file_keyring.py +469 -0
  752. chia/util/files.py +97 -0
  753. chia/util/full_block_utils.py +345 -0
  754. chia/util/generator_tools.py +72 -0
  755. chia/util/hash.py +31 -0
  756. chia/util/initial-config.yaml +694 -0
  757. chia/util/inline_executor.py +26 -0
  758. chia/util/ints.py +19 -0
  759. chia/util/ip_address.py +39 -0
  760. chia/util/json_util.py +37 -0
  761. chia/util/keychain.py +676 -0
  762. chia/util/keyring_wrapper.py +327 -0
  763. chia/util/limited_semaphore.py +41 -0
  764. chia/util/lock.py +49 -0
  765. chia/util/log_exceptions.py +32 -0
  766. chia/util/logging.py +36 -0
  767. chia/util/lru_cache.py +31 -0
  768. chia/util/math.py +20 -0
  769. chia/util/network.py +182 -0
  770. chia/util/paginator.py +48 -0
  771. chia/util/path.py +31 -0
  772. chia/util/permissions.py +20 -0
  773. chia/util/prev_transaction_block.py +21 -0
  774. chia/util/priority_mutex.py +95 -0
  775. chia/util/profiler.py +197 -0
  776. chia/util/recursive_replace.py +24 -0
  777. chia/util/safe_cancel_task.py +16 -0
  778. chia/util/service_groups.py +47 -0
  779. chia/util/setproctitle.py +22 -0
  780. chia/util/significant_bits.py +32 -0
  781. chia/util/ssl_check.py +213 -0
  782. chia/util/streamable.py +642 -0
  783. chia/util/task_referencer.py +59 -0
  784. chia/util/task_timing.py +382 -0
  785. chia/util/timing.py +67 -0
  786. chia/util/vdf_prover.py +30 -0
  787. chia/util/virtual_project_analysis.py +540 -0
  788. chia/util/ws_message.py +66 -0
  789. chia/wallet/__init__.py +0 -0
  790. chia/wallet/cat_wallet/__init__.py +0 -0
  791. chia/wallet/cat_wallet/cat_constants.py +75 -0
  792. chia/wallet/cat_wallet/cat_info.py +47 -0
  793. chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
  794. chia/wallet/cat_wallet/cat_utils.py +164 -0
  795. chia/wallet/cat_wallet/cat_wallet.py +855 -0
  796. chia/wallet/cat_wallet/dao_cat_info.py +28 -0
  797. chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
  798. chia/wallet/cat_wallet/lineage_store.py +74 -0
  799. chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
  800. chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
  801. chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
  802. chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
  803. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
  804. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
  805. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
  806. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
  807. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
  808. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
  809. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
  810. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
  811. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
  812. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
  813. chia/wallet/coin_selection.py +188 -0
  814. chia/wallet/conditions.py +1512 -0
  815. chia/wallet/dao_wallet/__init__.py +0 -0
  816. chia/wallet/dao_wallet/dao_info.py +61 -0
  817. chia/wallet/dao_wallet/dao_utils.py +811 -0
  818. chia/wallet/dao_wallet/dao_wallet.py +2119 -0
  819. chia/wallet/db_wallet/__init__.py +0 -0
  820. chia/wallet/db_wallet/db_wallet_puzzles.py +111 -0
  821. chia/wallet/derivation_record.py +30 -0
  822. chia/wallet/derive_keys.py +146 -0
  823. chia/wallet/did_wallet/__init__.py +0 -0
  824. chia/wallet/did_wallet/did_info.py +39 -0
  825. chia/wallet/did_wallet/did_wallet.py +1494 -0
  826. chia/wallet/did_wallet/did_wallet_puzzles.py +221 -0
  827. chia/wallet/did_wallet/puzzles/__init__.py +0 -0
  828. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
  829. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
  830. chia/wallet/driver_protocol.py +26 -0
  831. chia/wallet/key_val_store.py +55 -0
  832. chia/wallet/lineage_proof.py +58 -0
  833. chia/wallet/nft_wallet/__init__.py +0 -0
  834. chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
  835. chia/wallet/nft_wallet/nft_info.py +120 -0
  836. chia/wallet/nft_wallet/nft_puzzles.py +305 -0
  837. chia/wallet/nft_wallet/nft_wallet.py +1687 -0
  838. chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
  839. chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
  840. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
  841. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
  842. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
  843. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
  844. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
  845. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
  846. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
  847. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
  848. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
  849. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
  850. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
  851. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
  852. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
  853. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
  854. chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
  855. chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
  856. chia/wallet/nft_wallet/uncurry_nft.py +217 -0
  857. chia/wallet/notification_manager.py +117 -0
  858. chia/wallet/notification_store.py +178 -0
  859. chia/wallet/outer_puzzles.py +84 -0
  860. chia/wallet/payment.py +33 -0
  861. chia/wallet/puzzle_drivers.py +118 -0
  862. chia/wallet/puzzles/__init__.py +0 -0
  863. chia/wallet/puzzles/augmented_condition.clsp +13 -0
  864. chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
  865. chia/wallet/puzzles/clawback/__init__.py +0 -0
  866. chia/wallet/puzzles/clawback/drivers.py +188 -0
  867. chia/wallet/puzzles/clawback/metadata.py +38 -0
  868. chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
  869. chia/wallet/puzzles/condition_codes.clib +77 -0
  870. chia/wallet/puzzles/curry-and-treehash.clib +102 -0
  871. chia/wallet/puzzles/curry.clib +135 -0
  872. chia/wallet/puzzles/curry_by_index.clib +16 -0
  873. chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
  874. chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
  875. chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
  876. chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
  877. chia/wallet/puzzles/dao_finished_state.clsp +35 -0
  878. chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
  879. chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
  880. chia/wallet/puzzles/dao_lockup.clsp +288 -0
  881. chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
  882. chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
  883. chia/wallet/puzzles/dao_proposal.clsp +377 -0
  884. chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
  885. chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
  886. chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
  887. chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
  888. chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
  889. chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
  890. chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
  891. chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
  892. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
  893. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
  894. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
  895. chia/wallet/puzzles/dao_treasury.clsp +115 -0
  896. chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
  897. chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
  898. chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
  899. chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
  900. chia/wallet/puzzles/json.clib +25 -0
  901. chia/wallet/puzzles/load_clvm.py +161 -0
  902. chia/wallet/puzzles/merkle_utils.clib +18 -0
  903. chia/wallet/puzzles/notification.clsp +7 -0
  904. chia/wallet/puzzles/notification.clsp.hex +1 -0
  905. chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
  906. chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
  907. chia/wallet/puzzles/p2_conditions.clsp +3 -0
  908. chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
  909. chia/wallet/puzzles/p2_conditions.py +26 -0
  910. chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
  911. chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
  912. chia/wallet/puzzles/p2_delegated_conditions.py +21 -0
  913. chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
  914. chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
  915. chia/wallet/puzzles/p2_delegated_puzzle.py +34 -0
  916. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
  917. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
  918. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +160 -0
  919. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
  920. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
  921. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +21 -0
  922. chia/wallet/puzzles/p2_parent.clsp +19 -0
  923. chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
  924. chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
  925. chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
  926. chia/wallet/puzzles/p2_puzzle_hash.py +27 -0
  927. chia/wallet/puzzles/p2_singleton.clsp +30 -0
  928. chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
  929. chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
  930. chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
  931. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
  932. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
  933. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
  934. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
  935. chia/wallet/puzzles/puzzle_utils.py +34 -0
  936. chia/wallet/puzzles/settlement_payments.clsp +49 -0
  937. chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
  938. chia/wallet/puzzles/sha256tree.clib +11 -0
  939. chia/wallet/puzzles/singleton_launcher.clsp +16 -0
  940. chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
  941. chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
  942. chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
  943. chia/wallet/puzzles/singleton_top_layer.py +296 -0
  944. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
  945. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
  946. chia/wallet/puzzles/singleton_top_layer_v1_1.py +345 -0
  947. chia/wallet/puzzles/singleton_truths.clib +21 -0
  948. chia/wallet/puzzles/tails.py +348 -0
  949. chia/wallet/puzzles/utility_macros.clib +48 -0
  950. chia/wallet/signer_protocol.py +125 -0
  951. chia/wallet/singleton.py +106 -0
  952. chia/wallet/singleton_record.py +30 -0
  953. chia/wallet/trade_manager.py +1102 -0
  954. chia/wallet/trade_record.py +67 -0
  955. chia/wallet/trading/__init__.py +0 -0
  956. chia/wallet/trading/offer.py +702 -0
  957. chia/wallet/trading/trade_status.py +13 -0
  958. chia/wallet/trading/trade_store.py +526 -0
  959. chia/wallet/transaction_record.py +158 -0
  960. chia/wallet/transaction_sorting.py +14 -0
  961. chia/wallet/uncurried_puzzle.py +17 -0
  962. chia/wallet/util/__init__.py +0 -0
  963. chia/wallet/util/address_type.py +55 -0
  964. chia/wallet/util/blind_signer_tl.py +164 -0
  965. chia/wallet/util/clvm_streamable.py +203 -0
  966. chia/wallet/util/compute_hints.py +66 -0
  967. chia/wallet/util/compute_memos.py +43 -0
  968. chia/wallet/util/curry_and_treehash.py +91 -0
  969. chia/wallet/util/debug_spend_bundle.py +232 -0
  970. chia/wallet/util/merkle_tree.py +100 -0
  971. chia/wallet/util/merkle_utils.py +102 -0
  972. chia/wallet/util/new_peak_queue.py +82 -0
  973. chia/wallet/util/notifications.py +12 -0
  974. chia/wallet/util/peer_request_cache.py +174 -0
  975. chia/wallet/util/pprint.py +39 -0
  976. chia/wallet/util/puzzle_compression.py +95 -0
  977. chia/wallet/util/puzzle_decorator.py +100 -0
  978. chia/wallet/util/puzzle_decorator_type.py +7 -0
  979. chia/wallet/util/query_filter.py +59 -0
  980. chia/wallet/util/transaction_type.py +23 -0
  981. chia/wallet/util/tx_config.py +158 -0
  982. chia/wallet/util/wallet_sync_utils.py +351 -0
  983. chia/wallet/util/wallet_types.py +72 -0
  984. chia/wallet/vc_wallet/__init__.py +0 -0
  985. chia/wallet/vc_wallet/cr_cat_drivers.py +664 -0
  986. chia/wallet/vc_wallet/cr_cat_wallet.py +877 -0
  987. chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
  988. chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
  989. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
  990. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
  991. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
  992. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
  993. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
  994. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
  995. chia/wallet/vc_wallet/vc_drivers.py +838 -0
  996. chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
  997. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
  998. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
  999. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
  1000. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
  1001. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
  1002. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
  1003. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
  1004. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
  1005. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
  1006. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
  1007. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
  1008. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
  1009. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
  1010. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
  1011. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
  1012. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
  1013. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
  1014. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
  1015. chia/wallet/vc_wallet/vc_store.py +263 -0
  1016. chia/wallet/vc_wallet/vc_wallet.py +638 -0
  1017. chia/wallet/wallet.py +698 -0
  1018. chia/wallet/wallet_action_scope.py +96 -0
  1019. chia/wallet/wallet_blockchain.py +244 -0
  1020. chia/wallet/wallet_coin_record.py +72 -0
  1021. chia/wallet/wallet_coin_store.py +351 -0
  1022. chia/wallet/wallet_info.py +35 -0
  1023. chia/wallet/wallet_interested_store.py +188 -0
  1024. chia/wallet/wallet_nft_store.py +279 -0
  1025. chia/wallet/wallet_node.py +1765 -0
  1026. chia/wallet/wallet_node_api.py +207 -0
  1027. chia/wallet/wallet_pool_store.py +119 -0
  1028. chia/wallet/wallet_protocol.py +90 -0
  1029. chia/wallet/wallet_puzzle_store.py +396 -0
  1030. chia/wallet/wallet_retry_store.py +70 -0
  1031. chia/wallet/wallet_singleton_store.py +259 -0
  1032. chia/wallet/wallet_spend_bundle.py +25 -0
  1033. chia/wallet/wallet_state_manager.py +2819 -0
  1034. chia/wallet/wallet_transaction_store.py +496 -0
  1035. chia/wallet/wallet_user_store.py +110 -0
  1036. chia/wallet/wallet_weight_proof_handler.py +126 -0
  1037. chia_blockchain-2.5.1rc1.dist-info/LICENSE +201 -0
  1038. chia_blockchain-2.5.1rc1.dist-info/METADATA +156 -0
  1039. chia_blockchain-2.5.1rc1.dist-info/RECORD +1042 -0
  1040. chia_blockchain-2.5.1rc1.dist-info/WHEEL +4 -0
  1041. chia_blockchain-2.5.1rc1.dist-info/entry_points.txt +17 -0
  1042. mozilla-ca/cacert.pem +3611 -0
@@ -0,0 +1,4202 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ import random
6
+ import time
7
+ from collections.abc import AsyncIterator, Awaitable
8
+ from contextlib import asynccontextmanager
9
+ from dataclasses import replace
10
+ from typing import Optional
11
+
12
+ import pytest
13
+ from chia_rs import AugSchemeMPL, G2Element, MerkleSet
14
+ from clvm.casts import int_to_bytes
15
+
16
+ from chia._tests.blockchain.blockchain_test_utils import (
17
+ _validate_and_add_block,
18
+ _validate_and_add_block_multi_error,
19
+ _validate_and_add_block_multi_result,
20
+ _validate_and_add_block_no_error,
21
+ check_block_store_invariant,
22
+ )
23
+ from chia._tests.conftest import ConsensusMode
24
+ from chia._tests.util.blockchain import create_blockchain
25
+ from chia._tests.util.get_name_puzzle_conditions import get_name_puzzle_conditions
26
+ from chia.consensus.block_body_validation import ForkInfo
27
+ from chia.consensus.block_header_validation import validate_finished_header_block
28
+ from chia.consensus.block_record import BlockRecord
29
+ from chia.consensus.block_rewards import calculate_base_farmer_reward
30
+ from chia.consensus.blockchain import AddBlockResult, Blockchain
31
+ from chia.consensus.coinbase import create_farmer_coin
32
+ from chia.consensus.constants import ConsensusConstants
33
+ from chia.consensus.find_fork_point import lookup_fork_chain
34
+ from chia.consensus.full_block_to_block_record import block_to_block_record
35
+ from chia.consensus.get_block_generator import get_block_generator
36
+ from chia.consensus.multiprocess_validation import PreValidationResult, pre_validate_block
37
+ from chia.consensus.pot_iterations import is_overflow_block
38
+ from chia.simulator.block_tools import BlockTools, create_block_tools_async
39
+ from chia.simulator.keyring import TempKeyring
40
+ from chia.simulator.wallet_tools import WalletTool
41
+ from chia.types.blockchain_format.classgroup import ClassgroupElement
42
+ from chia.types.blockchain_format.coin import Coin
43
+ from chia.types.blockchain_format.foliage import TransactionsInfo
44
+ from chia.types.blockchain_format.serialized_program import SerializedProgram
45
+ from chia.types.blockchain_format.sized_bytes import bytes32
46
+ from chia.types.blockchain_format.slots import InfusedChallengeChainSubSlot
47
+ from chia.types.blockchain_format.vdf import VDFInfo, VDFProof, validate_vdf
48
+ from chia.types.condition_opcodes import ConditionOpcode
49
+ from chia.types.condition_with_args import ConditionWithArgs
50
+ from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
51
+ from chia.types.full_block import FullBlock
52
+ from chia.types.generator_types import BlockGenerator
53
+ from chia.types.spend_bundle import SpendBundle
54
+ from chia.types.unfinished_block import UnfinishedBlock
55
+ from chia.types.validation_state import ValidationState
56
+ from chia.util.augmented_chain import AugmentedBlockchain
57
+ from chia.util.errors import Err
58
+ from chia.util.generator_tools import get_block_header
59
+ from chia.util.hash import std_hash
60
+ from chia.util.ints import uint8, uint32, uint64
61
+ from chia.util.keychain import Keychain
62
+ from chia.util.recursive_replace import recursive_replace
63
+ from chia.util.vdf_prover import get_vdf_info_and_proof
64
+ from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
65
+ DEFAULT_HIDDEN_PUZZLE_HASH,
66
+ calculate_synthetic_secret_key,
67
+ )
68
+
69
+ log = logging.getLogger(__name__)
70
+ bad_element = ClassgroupElement.create(b"\x00")
71
+
72
+
73
+ @asynccontextmanager
74
+ async def make_empty_blockchain(constants: ConsensusConstants) -> AsyncIterator[Blockchain]:
75
+ """
76
+ Provides a list of 10 valid blocks, as well as a blockchain with 9 blocks added to it.
77
+ """
78
+
79
+ async with create_blockchain(constants, 2) as (bc, _):
80
+ yield bc
81
+
82
+
83
+ class TestGenesisBlock:
84
+ @pytest.mark.anyio
85
+ async def test_block_tools_proofs_400(
86
+ self, default_400_blocks: list[FullBlock], blockchain_constants: ConsensusConstants
87
+ ) -> None:
88
+ vdf, proof = get_vdf_info_and_proof(
89
+ blockchain_constants,
90
+ ClassgroupElement.get_default_element(),
91
+ blockchain_constants.GENESIS_CHALLENGE,
92
+ uint64(231),
93
+ )
94
+ if validate_vdf(proof, blockchain_constants, ClassgroupElement.get_default_element(), vdf) is False:
95
+ raise Exception("invalid proof")
96
+
97
+ @pytest.mark.anyio
98
+ async def test_block_tools_proofs_1000(
99
+ self, default_1000_blocks: list[FullBlock], blockchain_constants: ConsensusConstants
100
+ ) -> None:
101
+ vdf, proof = get_vdf_info_and_proof(
102
+ blockchain_constants,
103
+ ClassgroupElement.get_default_element(),
104
+ blockchain_constants.GENESIS_CHALLENGE,
105
+ uint64(231),
106
+ )
107
+ if validate_vdf(proof, blockchain_constants, ClassgroupElement.get_default_element(), vdf) is False:
108
+ raise Exception("invalid proof")
109
+
110
+ @pytest.mark.anyio
111
+ async def test_block_tools_proofs(self, blockchain_constants: ConsensusConstants) -> None:
112
+ vdf, proof = get_vdf_info_and_proof(
113
+ blockchain_constants,
114
+ ClassgroupElement.get_default_element(),
115
+ blockchain_constants.GENESIS_CHALLENGE,
116
+ uint64(231),
117
+ )
118
+ if validate_vdf(proof, blockchain_constants, ClassgroupElement.get_default_element(), vdf) is False:
119
+ raise Exception("invalid proof")
120
+
121
+ @pytest.mark.anyio
122
+ async def test_non_overflow_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
123
+ assert empty_blockchain.get_peak() is None
124
+ genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0]
125
+ await _validate_and_add_block(empty_blockchain, genesis)
126
+ peak = empty_blockchain.get_peak()
127
+ assert peak is not None
128
+ assert peak.height == 0
129
+
130
+ @pytest.mark.anyio
131
+ async def test_overflow_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
132
+ genesis = bt.get_consecutive_blocks(1, force_overflow=True)[0]
133
+ await _validate_and_add_block(empty_blockchain, genesis)
134
+
135
+ @pytest.mark.anyio
136
+ async def test_genesis_empty_slots(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
137
+ genesis = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=30)[0]
138
+ await _validate_and_add_block(empty_blockchain, genesis)
139
+
140
+ @pytest.mark.anyio
141
+ async def test_overflow_genesis_empty_slots(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
142
+ genesis = bt.get_consecutive_blocks(1, force_overflow=True, skip_slots=3)[0]
143
+ await _validate_and_add_block(empty_blockchain, genesis)
144
+
145
+ @pytest.mark.anyio
146
+ async def test_genesis_validate_1(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
147
+ genesis = bt.get_consecutive_blocks(1, force_overflow=False)[0]
148
+ bad_prev = bytes([1] * 32)
149
+ genesis = recursive_replace(genesis, "foliage.prev_block_hash", bad_prev)
150
+ await _validate_and_add_block(empty_blockchain, genesis, expected_error=Err.INVALID_PREV_BLOCK_HASH)
151
+
152
+
153
+ class TestBlockHeaderValidation:
154
+ @pytest.mark.limit_consensus_modes(reason="save time")
155
+ @pytest.mark.anyio
156
+ async def test_long_chain(self, empty_blockchain: Blockchain, default_1000_blocks: list[FullBlock]) -> None:
157
+ blocks = default_1000_blocks
158
+ fork_info = ForkInfo(blocks[0].height - 1, blocks[0].height - 1, blocks[0].prev_header_hash)
159
+ for block in blocks:
160
+ if (
161
+ len(block.finished_sub_slots) > 0
162
+ and block.finished_sub_slots[0].challenge_chain.subepoch_summary_hash is not None
163
+ ):
164
+ # Sub/Epoch. Try using a bad ssi and difficulty to test 2m and 2n
165
+ new_finished_ss = recursive_replace(
166
+ block.finished_sub_slots[0],
167
+ "challenge_chain.new_sub_slot_iters",
168
+ uint64(10_000_000),
169
+ )
170
+ block_bad = recursive_replace(
171
+ block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:]
172
+ )
173
+ header_block_bad = get_block_header(block_bad)
174
+ # TODO: Inspect these block values as they are currently None
175
+ expected_difficulty = block.finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
176
+ expected_sub_slot_iters = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
177
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
178
+ _, error = validate_finished_header_block(
179
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
180
+ )
181
+ assert error is not None
182
+ assert error.code == Err.INVALID_NEW_SUB_SLOT_ITERS
183
+
184
+ # Also fails calling the outer methods, but potentially with a different error
185
+ await _validate_and_add_block(
186
+ empty_blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK, fork_info=fork_info
187
+ )
188
+
189
+ new_finished_ss_2 = recursive_replace(
190
+ block.finished_sub_slots[0],
191
+ "challenge_chain.new_difficulty",
192
+ uint64(10_000_000),
193
+ )
194
+ block_bad_2 = recursive_replace(
195
+ block, "finished_sub_slots", [new_finished_ss_2] + block.finished_sub_slots[1:]
196
+ )
197
+
198
+ header_block_bad_2 = get_block_header(block_bad_2)
199
+ # TODO: Inspect these block values as they are currently None
200
+ expected_difficulty = block.finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
201
+ expected_sub_slot_iters = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
202
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
203
+ _, error = validate_finished_header_block(
204
+ empty_blockchain.constants, empty_blockchain, header_block_bad_2, False, expected_vs
205
+ )
206
+ assert error is not None
207
+ assert error.code == Err.INVALID_NEW_DIFFICULTY
208
+
209
+ # Also fails calling the outer methods, but potentially with a different error
210
+ await _validate_and_add_block(
211
+ empty_blockchain, block_bad_2, expected_result=AddBlockResult.INVALID_BLOCK, fork_info=fork_info
212
+ )
213
+
214
+ # 3c
215
+ new_finished_ss_3: EndOfSubSlotBundle = recursive_replace(
216
+ block.finished_sub_slots[0],
217
+ "challenge_chain.subepoch_summary_hash",
218
+ bytes([0] * 32),
219
+ )
220
+ new_finished_ss_3 = recursive_replace(
221
+ new_finished_ss_3,
222
+ "reward_chain.challenge_chain_sub_slot_hash",
223
+ new_finished_ss_3.challenge_chain.get_hash(),
224
+ )
225
+ log.warning(f"Number of slots: {len(block.finished_sub_slots)}")
226
+ block_bad_3 = recursive_replace(block, "finished_sub_slots", [new_finished_ss_3])
227
+
228
+ header_block_bad_3 = get_block_header(block_bad_3)
229
+ # TODO: Inspect these block values as they are currently None
230
+ expected_difficulty = block.finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
231
+ expected_sub_slot_iters = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
232
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
233
+ _, error = validate_finished_header_block(
234
+ empty_blockchain.constants, empty_blockchain, header_block_bad_3, False, expected_vs
235
+ )
236
+ assert error is not None
237
+ assert error.code == Err.INVALID_SUB_EPOCH_SUMMARY
238
+
239
+ # Also fails calling the outer methods, but potentially with a different error
240
+ await _validate_and_add_block(
241
+ empty_blockchain, block_bad_3, expected_result=AddBlockResult.INVALID_BLOCK, fork_info=fork_info
242
+ )
243
+
244
+ # 3d
245
+ new_finished_ss_4 = recursive_replace(
246
+ block.finished_sub_slots[0],
247
+ "challenge_chain.subepoch_summary_hash",
248
+ std_hash(b"123"),
249
+ )
250
+ new_finished_ss_4 = recursive_replace(
251
+ new_finished_ss_4,
252
+ "reward_chain.challenge_chain_sub_slot_hash",
253
+ new_finished_ss_4.challenge_chain.get_hash(),
254
+ )
255
+ block_bad_4 = recursive_replace(block, "finished_sub_slots", [new_finished_ss_4])
256
+
257
+ header_block_bad_4 = get_block_header(block_bad_4)
258
+ # TODO: Inspect these block values as they are currently None
259
+ expected_difficulty = block.finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
260
+ expected_sub_slot_iters = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
261
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
262
+ _, error = validate_finished_header_block(
263
+ empty_blockchain.constants, empty_blockchain, header_block_bad_4, False, expected_vs
264
+ )
265
+ assert error is not None
266
+ assert error.code == Err.INVALID_SUB_EPOCH_SUMMARY
267
+
268
+ # Also fails calling the outer methods, but potentially with a different error
269
+ await _validate_and_add_block(
270
+ empty_blockchain, block_bad_4, expected_result=AddBlockResult.INVALID_BLOCK, fork_info=fork_info
271
+ )
272
+ await _validate_and_add_block(empty_blockchain, block, fork_info=fork_info)
273
+ log.info(
274
+ f"Added block {block.height} total iters {block.total_iters} "
275
+ f"new slot? {len(block.finished_sub_slots)}"
276
+ )
277
+ peak = empty_blockchain.get_peak()
278
+ assert peak is not None
279
+ assert peak.height == len(blocks) - 1
280
+
281
+ @pytest.mark.anyio
282
+ async def test_unfinished_blocks(
283
+ self, empty_blockchain: Blockchain, softfork_height: uint32, bt: BlockTools
284
+ ) -> None:
285
+ blockchain = empty_blockchain
286
+ blocks = bt.get_consecutive_blocks(3)
287
+ for block in blocks[:-1]:
288
+ await _validate_and_add_block(empty_blockchain, block)
289
+ block = blocks[-1]
290
+ unf = UnfinishedBlock(
291
+ block.finished_sub_slots,
292
+ block.reward_chain_block.get_unfinished(),
293
+ block.challenge_chain_sp_proof,
294
+ block.reward_chain_sp_proof,
295
+ block.foliage,
296
+ block.foliage_transaction_block,
297
+ block.transactions_info,
298
+ block.transactions_generator,
299
+ [],
300
+ )
301
+ npc_result = None
302
+ # if this assert fires, remove it along with the pragma for the block
303
+ # below
304
+ assert unf.transactions_generator is None
305
+ if unf.transactions_generator is not None: # pragma: no cover
306
+ block_generator = await get_block_generator(blockchain.lookup_block_generators, unf)
307
+ assert block_generator is not None
308
+ block_bytes = bytes(unf)
309
+ npc_result = await blockchain.run_generator(block_bytes, block_generator, height=softfork_height)
310
+
311
+ validate_res = await blockchain.validate_unfinished_block(unf, npc_result, False)
312
+ err = validate_res.error
313
+ assert err is None
314
+
315
+ await _validate_and_add_block(empty_blockchain, block)
316
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, force_overflow=True)
317
+ block = blocks[-1]
318
+ unf = UnfinishedBlock(
319
+ block.finished_sub_slots,
320
+ block.reward_chain_block.get_unfinished(),
321
+ block.challenge_chain_sp_proof,
322
+ block.reward_chain_sp_proof,
323
+ block.foliage,
324
+ block.foliage_transaction_block,
325
+ block.transactions_info,
326
+ block.transactions_generator,
327
+ [],
328
+ )
329
+ npc_result = None
330
+ # if this assert fires, remove it along with the pragma for the block
331
+ # below
332
+ assert unf.transactions_generator is None
333
+ if unf.transactions_generator is not None: # pragma: no cover
334
+ block_generator = await get_block_generator(blockchain.lookup_block_generators, unf)
335
+ assert block_generator is not None
336
+ block_bytes = bytes(unf)
337
+ npc_result = await blockchain.run_generator(block_bytes, block_generator, height=softfork_height)
338
+ validate_res = await blockchain.validate_unfinished_block(unf, npc_result, False)
339
+ assert validate_res.error is None
340
+
341
+ @pytest.mark.anyio
342
+ async def test_empty_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
343
+ for block in bt.get_consecutive_blocks(2, skip_slots=3):
344
+ await _validate_and_add_block(empty_blockchain, block)
345
+
346
+ @pytest.mark.anyio
347
+ async def test_empty_slots_non_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
348
+ blockchain = empty_blockchain
349
+ blocks = bt.get_consecutive_blocks(10)
350
+ for block in blocks:
351
+ await _validate_and_add_block(empty_blockchain, block)
352
+
353
+ blocks = bt.get_consecutive_blocks(10, skip_slots=2, block_list_input=blocks)
354
+ for block in blocks[10:]:
355
+ await _validate_and_add_block(empty_blockchain, block)
356
+ peak = blockchain.get_peak()
357
+ assert peak is not None
358
+ assert peak.height == 19
359
+
360
+ @pytest.mark.anyio
361
+ async def test_one_sb_per_slot(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
362
+ blockchain = empty_blockchain
363
+ num_blocks = 20
364
+ blocks: list[FullBlock] = []
365
+ for _ in range(num_blocks):
366
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1)
367
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
368
+ peak = blockchain.get_peak()
369
+ assert peak is not None
370
+ assert peak.height == num_blocks - 1
371
+
372
+ @pytest.mark.anyio
373
+ async def test_all_overflow(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
374
+ blockchain = empty_blockchain
375
+ num_rounds = 5
376
+ blocks: list[FullBlock] = []
377
+ num_blocks = 0
378
+ for i in range(1, num_rounds):
379
+ num_blocks += i
380
+ blocks = bt.get_consecutive_blocks(i, block_list_input=blocks, skip_slots=1, force_overflow=True)
381
+ for block in blocks[-i:]:
382
+ await _validate_and_add_block(empty_blockchain, block)
383
+ peak = blockchain.get_peak()
384
+ assert peak is not None
385
+ assert peak.height == num_blocks - 1
386
+
387
+ @pytest.mark.anyio
388
+ async def test_unf_block_overflow(
389
+ self, empty_blockchain: Blockchain, softfork_height: uint32, bt: BlockTools
390
+ ) -> None:
391
+ blockchain = empty_blockchain
392
+
393
+ blocks: list[FullBlock] = []
394
+ while True:
395
+ # This creates an overflow block, then a normal block, and then an overflow in the next sub-slot
396
+ # blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, force_overflow=True)
397
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
398
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, force_overflow=True)
399
+
400
+ await _validate_and_add_block(blockchain, blocks[-2])
401
+
402
+ sb_1 = blockchain.block_record(blocks[-2].header_hash)
403
+
404
+ sb_2_next_ss = blocks[-1].total_iters - blocks[-2].total_iters < sb_1.sub_slot_iters
405
+ # We might not get a normal block for sb_2, and we might not get them in the right slots
406
+ # So this while loop keeps trying
407
+ if sb_1.overflow and sb_2_next_ss:
408
+ block = blocks[-1]
409
+ unf = UnfinishedBlock(
410
+ [],
411
+ block.reward_chain_block.get_unfinished(),
412
+ block.challenge_chain_sp_proof,
413
+ block.reward_chain_sp_proof,
414
+ block.foliage,
415
+ block.foliage_transaction_block,
416
+ block.transactions_info,
417
+ block.transactions_generator,
418
+ [],
419
+ )
420
+ npc_result = None
421
+ # if this assert fires, remove it along with the pragma for the block
422
+ # below
423
+ assert block.transactions_generator is None
424
+ if block.transactions_generator is not None: # pragma: no cover
425
+ block_generator = await get_block_generator(blockchain.lookup_block_generators, unf)
426
+ assert block_generator is not None
427
+ block_bytes = bytes(unf)
428
+ npc_result = await blockchain.run_generator(block_bytes, block_generator, height=softfork_height)
429
+ validate_res = await blockchain.validate_unfinished_block(
430
+ unf, npc_result, skip_overflow_ss_validation=True
431
+ )
432
+ assert validate_res.error is None
433
+ return None
434
+
435
+ await _validate_and_add_block(blockchain, blocks[-1])
436
+
437
+ @pytest.mark.anyio
438
+ async def test_one_sb_per_two_slots(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
439
+ blockchain = empty_blockchain
440
+ num_blocks = 20
441
+ blocks: list[FullBlock] = []
442
+ for _ in range(num_blocks): # Same thing, but 2 sub-slots per block
443
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2)
444
+ await _validate_and_add_block(blockchain, blocks[-1])
445
+ peak = blockchain.get_peak()
446
+ assert peak is not None
447
+ assert peak.height == num_blocks - 1
448
+
449
+ @pytest.mark.anyio
450
+ async def test_one_sb_per_five_slots(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
451
+ blockchain = empty_blockchain
452
+ num_blocks = 10
453
+ blocks: list[FullBlock] = []
454
+ for _ in range(num_blocks): # Same thing, but 5 sub-slots per block
455
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=5)
456
+ await _validate_and_add_block(blockchain, blocks[-1])
457
+ peak = blockchain.get_peak()
458
+ assert peak is not None
459
+ assert peak.height == num_blocks - 1
460
+
461
+ @pytest.mark.anyio
462
+ async def test_basic_chain_overflow(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
463
+ blocks = bt.get_consecutive_blocks(5, force_overflow=True)
464
+ for block in blocks:
465
+ await _validate_and_add_block(empty_blockchain, block)
466
+ peak = empty_blockchain.get_peak()
467
+ assert peak is not None
468
+ assert peak.height == len(blocks) - 1
469
+
470
+ @pytest.mark.anyio
471
+ async def test_one_sb_per_two_slots_force_overflow(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
472
+ blockchain = empty_blockchain
473
+ num_blocks = 10
474
+ blocks: list[FullBlock] = []
475
+ for _ in range(num_blocks):
476
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=2, force_overflow=True)
477
+ await _validate_and_add_block(blockchain, blocks[-1])
478
+ peak = blockchain.get_peak()
479
+ assert peak is not None
480
+ assert peak.height == num_blocks - 1
481
+
482
+ @pytest.mark.anyio
483
+ async def test_invalid_prev(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
484
+ # 1
485
+ blocks = bt.get_consecutive_blocks(2, force_overflow=False)
486
+ await _validate_and_add_block(empty_blockchain, blocks[0])
487
+ block_1_bad = recursive_replace(blocks[-1], "foliage.prev_block_hash", bytes([0] * 32))
488
+
489
+ await _validate_and_add_block(empty_blockchain, block_1_bad, expected_error=Err.INVALID_PREV_BLOCK_HASH)
490
+
491
+ @pytest.mark.anyio
492
+ async def test_invalid_pospace(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
493
+ # 2
494
+ blocks = bt.get_consecutive_blocks(2, force_overflow=False)
495
+ await _validate_and_add_block(empty_blockchain, blocks[0])
496
+ block_1_bad = recursive_replace(blocks[-1], "reward_chain_block.proof_of_space.proof", bytes([0] * 32))
497
+
498
+ await _validate_and_add_block(empty_blockchain, block_1_bad, expected_error=Err.INVALID_POSPACE)
499
+
500
+ @pytest.mark.anyio
501
+ async def test_invalid_sub_slot_challenge_hash_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
502
+ # 2a
503
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1)
504
+ new_finished_ss = recursive_replace(
505
+ blocks[0].finished_sub_slots[0],
506
+ "challenge_chain.challenge_chain_end_of_slot_vdf.challenge",
507
+ bytes([2] * 32),
508
+ )
509
+ block_0_bad = recursive_replace(
510
+ blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:]
511
+ )
512
+
513
+ header_block_bad = get_block_header(block_0_bad)
514
+ expected_vs = ValidationState(
515
+ empty_blockchain.constants.SUB_SLOT_ITERS_STARTING, empty_blockchain.constants.DIFFICULTY_STARTING, None
516
+ )
517
+ _, error = validate_finished_header_block(
518
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
519
+ )
520
+
521
+ assert error is not None
522
+ assert error.code == Err.INVALID_PREV_CHALLENGE_SLOT_HASH
523
+ await _validate_and_add_block(empty_blockchain, block_0_bad, expected_result=AddBlockResult.INVALID_BLOCK)
524
+
525
+ @pytest.mark.anyio
526
+ async def test_invalid_sub_slot_challenge_hash_non_genesis(
527
+ self, empty_blockchain: Blockchain, bt: BlockTools
528
+ ) -> None:
529
+ # 2b
530
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0)
531
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1, block_list_input=blocks)
532
+ new_finished_ss = recursive_replace(
533
+ blocks[1].finished_sub_slots[0],
534
+ "challenge_chain.challenge_chain_end_of_slot_vdf.challenge",
535
+ bytes([2] * 32),
536
+ )
537
+ block_1_bad = recursive_replace(
538
+ blocks[1], "finished_sub_slots", [new_finished_ss] + blocks[1].finished_sub_slots[1:]
539
+ )
540
+
541
+ await _validate_and_add_block(empty_blockchain, blocks[0])
542
+ header_block_bad = get_block_header(block_1_bad)
543
+ # TODO: Inspect these block values as they are currently None
544
+ expected_difficulty = blocks[1].finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
545
+ expected_sub_slot_iters = blocks[1].finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
546
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
547
+ _, error = validate_finished_header_block(
548
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
549
+ )
550
+ assert error is not None
551
+ assert error.code == Err.INVALID_PREV_CHALLENGE_SLOT_HASH
552
+ await _validate_and_add_block(empty_blockchain, block_1_bad, expected_result=AddBlockResult.INVALID_BLOCK)
553
+
554
+ @pytest.mark.anyio
555
+ async def test_invalid_sub_slot_challenge_hash_empty_ss(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
556
+ # 2c
557
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=0)
558
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=2, block_list_input=blocks)
559
+ new_finished_ss = recursive_replace(
560
+ blocks[1].finished_sub_slots[-1],
561
+ "challenge_chain.challenge_chain_end_of_slot_vdf.challenge",
562
+ bytes([2] * 32),
563
+ )
564
+ block_1_bad = recursive_replace(
565
+ blocks[1], "finished_sub_slots", blocks[1].finished_sub_slots[:-1] + [new_finished_ss]
566
+ )
567
+ await _validate_and_add_block(empty_blockchain, blocks[0])
568
+
569
+ header_block_bad = get_block_header(block_1_bad)
570
+ # TODO: Inspect these block values as they are currently None
571
+ expected_difficulty = blocks[1].finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
572
+ expected_sub_slot_iters = blocks[1].finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
573
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
574
+ _, error = validate_finished_header_block(
575
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
576
+ )
577
+ assert error is not None
578
+ assert error.code == Err.INVALID_PREV_CHALLENGE_SLOT_HASH
579
+ await _validate_and_add_block(empty_blockchain, block_1_bad, expected_result=AddBlockResult.INVALID_BLOCK)
580
+
581
+ @pytest.mark.anyio
582
+ async def test_genesis_no_icc(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
583
+ # 2d
584
+ blocks = bt.get_consecutive_blocks(1, force_overflow=False, skip_slots=1)
585
+ new_finished_ss = recursive_replace(
586
+ blocks[0].finished_sub_slots[0],
587
+ "infused_challenge_chain",
588
+ InfusedChallengeChainSubSlot(
589
+ VDFInfo(
590
+ bytes32.zeros,
591
+ uint64(1200),
592
+ ClassgroupElement.get_default_element(),
593
+ )
594
+ ),
595
+ )
596
+ block_0_bad = recursive_replace(
597
+ blocks[0], "finished_sub_slots", [new_finished_ss] + blocks[0].finished_sub_slots[1:]
598
+ )
599
+ await _validate_and_add_block(empty_blockchain, block_0_bad, expected_error=Err.SHOULD_NOT_HAVE_ICC)
600
+
601
+ async def do_test_invalid_icc_sub_slot_vdf(
602
+ self, keychain: Keychain, db_version: int, constants: ConsensusConstants
603
+ ) -> None:
604
+ bt_high_iters = await create_block_tools_async(
605
+ constants=constants.replace(
606
+ SUB_SLOT_ITERS_STARTING=uint64(2**12),
607
+ DIFFICULTY_STARTING=uint64(2**14),
608
+ ),
609
+ keychain=keychain,
610
+ )
611
+ async with create_blockchain(bt_high_iters.constants, db_version) as (bc1, _):
612
+ blocks = bt_high_iters.get_consecutive_blocks(10)
613
+ for block in blocks:
614
+ if (
615
+ len(block.finished_sub_slots) > 0
616
+ and block.finished_sub_slots[-1].infused_challenge_chain is not None
617
+ ):
618
+ # Bad iters
619
+ new_finished_ss = recursive_replace(
620
+ block.finished_sub_slots[-1],
621
+ "infused_challenge_chain",
622
+ InfusedChallengeChainSubSlot(
623
+ block.finished_sub_slots[
624
+ -1
625
+ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
626
+ number_of_iterations=uint64(10000000),
627
+ )
628
+ ),
629
+ )
630
+ block_bad = recursive_replace(
631
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
632
+ )
633
+ await _validate_and_add_block(bc1, block_bad, expected_error=Err.INVALID_ICC_EOS_VDF)
634
+
635
+ # Bad output
636
+ new_finished_ss_2 = recursive_replace(
637
+ block.finished_sub_slots[-1],
638
+ "infused_challenge_chain",
639
+ InfusedChallengeChainSubSlot(
640
+ block.finished_sub_slots[
641
+ -1
642
+ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
643
+ output=ClassgroupElement.get_default_element(),
644
+ )
645
+ ),
646
+ )
647
+ log.warning(f"Proof: {block.finished_sub_slots[-1].proofs}")
648
+ block_bad_2 = recursive_replace(
649
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2]
650
+ )
651
+ await _validate_and_add_block(bc1, block_bad_2, expected_error=Err.INVALID_ICC_EOS_VDF)
652
+
653
+ # Bad challenge hash
654
+ new_finished_ss_3 = recursive_replace(
655
+ block.finished_sub_slots[-1],
656
+ "infused_challenge_chain",
657
+ InfusedChallengeChainSubSlot(
658
+ block.finished_sub_slots[
659
+ -1
660
+ ].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
661
+ challenge=bytes32.zeros
662
+ )
663
+ ),
664
+ )
665
+ block_bad_3 = recursive_replace(
666
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3]
667
+ )
668
+ await _validate_and_add_block(bc1, block_bad_3, expected_error=Err.INVALID_ICC_EOS_VDF)
669
+
670
+ # Bad proof
671
+ new_finished_ss_5 = recursive_replace(
672
+ block.finished_sub_slots[-1],
673
+ "proofs.infused_challenge_chain_slot_proof",
674
+ VDFProof(uint8(0), b"1239819023890", False),
675
+ )
676
+ block_bad_5 = recursive_replace(
677
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5]
678
+ )
679
+ await _validate_and_add_block(bc1, block_bad_5, expected_error=Err.INVALID_ICC_EOS_VDF)
680
+
681
+ await _validate_and_add_block(bc1, block)
682
+
683
+ @pytest.mark.anyio
684
+ async def test_invalid_icc_sub_slot_vdf(self, db_version: int, blockchain_constants: ConsensusConstants) -> None:
685
+ with TempKeyring() as keychain:
686
+ await self.do_test_invalid_icc_sub_slot_vdf(keychain, db_version, blockchain_constants)
687
+
688
+ @pytest.mark.anyio
689
+ async def test_invalid_icc_into_cc(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
690
+ blockchain = empty_blockchain
691
+ blocks = bt.get_consecutive_blocks(1)
692
+ await _validate_and_add_block(blockchain, blocks[0])
693
+ case_1, case_2 = False, False
694
+ while not case_1 or not case_2:
695
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1)
696
+ block = blocks[-1]
697
+ if len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is not None:
698
+ if block.finished_sub_slots[-1].reward_chain.deficit == bt.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK:
699
+ # 2g
700
+ case_1 = True
701
+ new_finished_ss = recursive_replace(
702
+ block.finished_sub_slots[-1],
703
+ "challenge_chain",
704
+ block.finished_sub_slots[-1].challenge_chain.replace(
705
+ infused_challenge_chain_sub_slot_hash=bytes32([1] * 32)
706
+ ),
707
+ )
708
+ else:
709
+ # 2h
710
+ case_2 = True
711
+ new_finished_ss = recursive_replace(
712
+ block.finished_sub_slots[-1],
713
+ "challenge_chain",
714
+ block.finished_sub_slots[-1].challenge_chain.replace(
715
+ infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[
716
+ -1
717
+ ].infused_challenge_chain.get_hash(),
718
+ ),
719
+ )
720
+ block_bad = recursive_replace(
721
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
722
+ )
723
+
724
+ header_block_bad = get_block_header(block_bad)
725
+ # TODO: Inspect these block values as they are currently None
726
+ expected_difficulty = block.finished_sub_slots[0].challenge_chain.new_difficulty or uint64(0)
727
+ expected_sub_slot_iters = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters or uint64(0)
728
+ expected_vs = ValidationState(expected_sub_slot_iters, expected_difficulty, None)
729
+ _, error = validate_finished_header_block(
730
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
731
+ )
732
+ assert error is not None
733
+ assert error.code == Err.INVALID_ICC_HASH_CC
734
+ await _validate_and_add_block(blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK)
735
+
736
+ # 2i
737
+ new_finished_ss_bad_rc = recursive_replace(
738
+ block.finished_sub_slots[-1],
739
+ "reward_chain",
740
+ block.finished_sub_slots[-1].reward_chain.replace(infused_challenge_chain_sub_slot_hash=None),
741
+ )
742
+ block_bad = recursive_replace(
743
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
744
+ )
745
+ await _validate_and_add_block(blockchain, block_bad, expected_error=Err.INVALID_ICC_HASH_RC)
746
+ elif len(block.finished_sub_slots) > 0 and block.finished_sub_slots[-1].infused_challenge_chain is None:
747
+ # 2j
748
+ # TODO: This code path is currently not exercised
749
+ new_finished_ss_bad_cc = recursive_replace(
750
+ block.finished_sub_slots[-1],
751
+ "challenge_chain",
752
+ block.finished_sub_slots[-1].challenge_chain.replace(
753
+ infused_challenge_chain_sub_slot_hash=bytes32([1] * 32)
754
+ ),
755
+ )
756
+ block_bad = recursive_replace(
757
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_cc]
758
+ )
759
+ await _validate_and_add_block(blockchain, block_bad, expected_error=Err.INVALID_ICC_HASH_CC)
760
+
761
+ # 2k
762
+ # TODO: This code path is currently not exercised
763
+ new_finished_ss_bad_rc = recursive_replace(
764
+ block.finished_sub_slots[-1],
765
+ "reward_chain",
766
+ block.finished_sub_slots[-1].reward_chain.replace(
767
+ infused_challenge_chain_sub_slot_hash=bytes32([1] * 32)
768
+ ),
769
+ )
770
+ block_bad = recursive_replace(
771
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
772
+ )
773
+ await _validate_and_add_block(blockchain, block_bad, expected_error=Err.INVALID_ICC_HASH_RC)
774
+
775
+ # Finally, add the block properly
776
+ await _validate_and_add_block(blockchain, block)
777
+
778
+ @pytest.mark.anyio
779
+ async def test_empty_slot_no_ses(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
780
+ # 2l
781
+ blockchain = empty_blockchain
782
+ blocks = bt.get_consecutive_blocks(1)
783
+ await _validate_and_add_block(blockchain, blocks[0])
784
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=4)
785
+
786
+ new_finished_ss = recursive_replace(
787
+ blocks[-1].finished_sub_slots[-1],
788
+ "challenge_chain",
789
+ blocks[-1].finished_sub_slots[-1].challenge_chain.replace(subepoch_summary_hash=std_hash(b"0")),
790
+ )
791
+ block_bad = recursive_replace(
792
+ blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
793
+ )
794
+
795
+ header_block_bad = get_block_header(block_bad)
796
+ expected_vs = ValidationState(
797
+ empty_blockchain.constants.SUB_SLOT_ITERS_STARTING, empty_blockchain.constants.DIFFICULTY_STARTING, None
798
+ )
799
+ _, error = validate_finished_header_block(
800
+ empty_blockchain.constants, empty_blockchain, header_block_bad, False, expected_vs
801
+ )
802
+ assert error is not None
803
+ assert error.code == Err.INVALID_SUB_EPOCH_SUMMARY_HASH
804
+ await _validate_and_add_block(blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK)
805
+
806
+ @pytest.mark.anyio
807
+ async def test_empty_sub_slots_epoch(
808
+ self, empty_blockchain: Blockchain, default_400_blocks: list[FullBlock], bt: BlockTools
809
+ ) -> None:
810
+ # 2m
811
+ # Tests adding an empty sub slot after the sub-epoch / epoch.
812
+ # Also tests overflow block in epoch
813
+ blocks_base = default_400_blocks[: bt.constants.EPOCH_BLOCKS]
814
+ assert len(blocks_base) == bt.constants.EPOCH_BLOCKS
815
+ blocks_1 = bt.get_consecutive_blocks(1, block_list_input=blocks_base, force_overflow=True)
816
+ blocks_2 = bt.get_consecutive_blocks(1, skip_slots=5, block_list_input=blocks_base, force_overflow=True)
817
+ for block in blocks_base:
818
+ await _validate_and_add_block(empty_blockchain, block, skip_prevalidation=True)
819
+ await _validate_and_add_block(
820
+ empty_blockchain, blocks_1[-1], expected_result=AddBlockResult.NEW_PEAK, skip_prevalidation=True
821
+ )
822
+ assert blocks_1[-1].header_hash != blocks_2[-1].header_hash
823
+ await _validate_and_add_block(
824
+ empty_blockchain, blocks_2[-1], expected_result=AddBlockResult.ADDED_AS_ORPHAN, skip_prevalidation=True
825
+ )
826
+
827
+ @pytest.mark.anyio
828
+ async def test_wrong_cc_hash_rc(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
829
+ # 2o
830
+ blockchain = empty_blockchain
831
+ blocks = bt.get_consecutive_blocks(1, skip_slots=1)
832
+ blocks = bt.get_consecutive_blocks(1, skip_slots=1, block_list_input=blocks)
833
+ await _validate_and_add_block(empty_blockchain, blocks[0])
834
+
835
+ new_finished_ss = recursive_replace(
836
+ blocks[-1].finished_sub_slots[-1],
837
+ "reward_chain",
838
+ blocks[-1].finished_sub_slots[-1].reward_chain.replace(challenge_chain_sub_slot_hash=bytes32([3] * 32)),
839
+ )
840
+ block_1_bad = recursive_replace(
841
+ blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
842
+ )
843
+
844
+ await _validate_and_add_block(blockchain, block_1_bad, expected_error=Err.INVALID_CHALLENGE_SLOT_HASH_RC)
845
+
846
+ @pytest.mark.anyio
847
+ async def test_invalid_cc_sub_slot_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
848
+ # 2q
849
+ blocks: list[FullBlock] = []
850
+ found_overflow_slot: bool = False
851
+
852
+ while not found_overflow_slot:
853
+ blocks = bt.get_consecutive_blocks(1, blocks)
854
+ block = blocks[-1]
855
+ if (
856
+ len(block.finished_sub_slots)
857
+ and is_overflow_block(bt.constants, block.reward_chain_block.signage_point_index)
858
+ and block.finished_sub_slots[-1].challenge_chain.challenge_chain_end_of_slot_vdf.output
859
+ != ClassgroupElement.get_default_element()
860
+ ):
861
+ found_overflow_slot = True
862
+ # Bad iters
863
+ new_finished_ss = recursive_replace(
864
+ block.finished_sub_slots[-1],
865
+ "challenge_chain",
866
+ recursive_replace(
867
+ block.finished_sub_slots[-1].challenge_chain,
868
+ "challenge_chain_end_of_slot_vdf.number_of_iterations",
869
+ uint64(10000000),
870
+ ),
871
+ )
872
+ new_finished_ss = recursive_replace(
873
+ new_finished_ss,
874
+ "reward_chain.challenge_chain_sub_slot_hash",
875
+ new_finished_ss.challenge_chain.get_hash(),
876
+ )
877
+ log.warning(f"Num slots: {len(block.finished_sub_slots)}")
878
+ block_bad = recursive_replace(
879
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
880
+ )
881
+ log.warning(f"Signage point index: {block_bad.reward_chain_block.signage_point_index}")
882
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_EOS_VDF)
883
+
884
+ # Bad output
885
+ new_finished_ss_2 = recursive_replace(
886
+ block.finished_sub_slots[-1],
887
+ "challenge_chain",
888
+ recursive_replace(
889
+ block.finished_sub_slots[-1].challenge_chain,
890
+ "challenge_chain_end_of_slot_vdf.output",
891
+ ClassgroupElement.get_default_element(),
892
+ ),
893
+ )
894
+
895
+ new_finished_ss_2 = recursive_replace(
896
+ new_finished_ss_2,
897
+ "reward_chain.challenge_chain_sub_slot_hash",
898
+ new_finished_ss_2.challenge_chain.get_hash(),
899
+ )
900
+ block_bad_2 = recursive_replace(
901
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2]
902
+ )
903
+ await _validate_and_add_block(empty_blockchain, block_bad_2, expected_error=Err.INVALID_CC_EOS_VDF)
904
+
905
+ # Bad challenge hash
906
+ new_finished_ss_3 = recursive_replace(
907
+ block.finished_sub_slots[-1],
908
+ "challenge_chain",
909
+ recursive_replace(
910
+ block.finished_sub_slots[-1].challenge_chain,
911
+ "challenge_chain_end_of_slot_vdf.challenge",
912
+ bytes([1] * 32),
913
+ ),
914
+ )
915
+
916
+ new_finished_ss_3 = recursive_replace(
917
+ new_finished_ss_3,
918
+ "reward_chain.challenge_chain_sub_slot_hash",
919
+ new_finished_ss_3.challenge_chain.get_hash(),
920
+ )
921
+ block_bad_3 = recursive_replace(
922
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3]
923
+ )
924
+
925
+ await _validate_and_add_block_multi_error(
926
+ empty_blockchain,
927
+ block_bad_3,
928
+ [Err.INVALID_CC_EOS_VDF, Err.INVALID_PREV_CHALLENGE_SLOT_HASH, Err.INVALID_POSPACE],
929
+ )
930
+
931
+ # Bad proof
932
+ new_finished_ss_5 = recursive_replace(
933
+ block.finished_sub_slots[-1],
934
+ "proofs.challenge_chain_slot_proof",
935
+ VDFProof(uint8(0), b"1239819023890", False),
936
+ )
937
+ block_bad_5 = recursive_replace(
938
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5]
939
+ )
940
+ await _validate_and_add_block(empty_blockchain, block_bad_5, expected_error=Err.INVALID_CC_EOS_VDF)
941
+
942
+ await _validate_and_add_block(empty_blockchain, block)
943
+
944
+ @pytest.mark.anyio
945
+ async def test_invalid_rc_sub_slot_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
946
+ # 2p
947
+ blocks: list[FullBlock] = []
948
+ found_block: bool = False
949
+
950
+ while not found_block:
951
+ blocks = bt.get_consecutive_blocks(1, blocks)
952
+ block = blocks[-1]
953
+ if (
954
+ len(block.finished_sub_slots)
955
+ and block.finished_sub_slots[-1].reward_chain.end_of_slot_vdf.output
956
+ != ClassgroupElement.get_default_element()
957
+ ):
958
+ found_block = True
959
+ # Bad iters
960
+ new_finished_ss = recursive_replace(
961
+ block.finished_sub_slots[-1],
962
+ "reward_chain",
963
+ recursive_replace(
964
+ block.finished_sub_slots[-1].reward_chain,
965
+ "end_of_slot_vdf.number_of_iterations",
966
+ uint64(10000000),
967
+ ),
968
+ )
969
+ block_bad = recursive_replace(
970
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss]
971
+ )
972
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_EOS_VDF)
973
+
974
+ # Bad output
975
+ new_finished_ss_2 = recursive_replace(
976
+ block.finished_sub_slots[-1],
977
+ "reward_chain",
978
+ recursive_replace(
979
+ block.finished_sub_slots[-1].reward_chain,
980
+ "end_of_slot_vdf.output",
981
+ ClassgroupElement.get_default_element(),
982
+ ),
983
+ )
984
+ block_bad_2 = recursive_replace(
985
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_2]
986
+ )
987
+ await _validate_and_add_block(empty_blockchain, block_bad_2, expected_error=Err.INVALID_RC_EOS_VDF)
988
+
989
+ # Bad challenge hash
990
+ new_finished_ss_3 = recursive_replace(
991
+ block.finished_sub_slots[-1],
992
+ "reward_chain",
993
+ recursive_replace(
994
+ block.finished_sub_slots[-1].reward_chain,
995
+ "end_of_slot_vdf.challenge",
996
+ bytes32([1] * 32),
997
+ ),
998
+ )
999
+ block_bad_3 = recursive_replace(
1000
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_3]
1001
+ )
1002
+ await _validate_and_add_block(empty_blockchain, block_bad_3, expected_error=Err.INVALID_RC_EOS_VDF)
1003
+
1004
+ # Bad proof
1005
+ new_finished_ss_5 = recursive_replace(
1006
+ block.finished_sub_slots[-1],
1007
+ "proofs.reward_chain_slot_proof",
1008
+ VDFProof(uint8(0), b"1239819023890", False),
1009
+ )
1010
+ block_bad_5 = recursive_replace(
1011
+ block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_5]
1012
+ )
1013
+ await _validate_and_add_block(empty_blockchain, block_bad_5, expected_error=Err.INVALID_RC_EOS_VDF)
1014
+
1015
+ await _validate_and_add_block(empty_blockchain, block)
1016
+
1017
+ @pytest.mark.anyio
1018
+ async def test_genesis_bad_deficit(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1019
+ # 2r
1020
+ block = bt.get_consecutive_blocks(1, skip_slots=2)[0]
1021
+ new_finished_ss = recursive_replace(
1022
+ block.finished_sub_slots[-1],
1023
+ "reward_chain",
1024
+ recursive_replace(
1025
+ block.finished_sub_slots[-1].reward_chain,
1026
+ "deficit",
1027
+ bt.constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK - 1,
1028
+ ),
1029
+ )
1030
+ block_bad = recursive_replace(block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss])
1031
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_DEFICIT)
1032
+
1033
+ @pytest.mark.anyio
1034
+ async def test_reset_deficit(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1035
+ # 2s, 2t
1036
+ blockchain = empty_blockchain
1037
+ blocks = bt.get_consecutive_blocks(2)
1038
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1039
+ await _validate_and_add_block(empty_blockchain, blocks[1])
1040
+ case_1, case_2 = False, False
1041
+ while not case_1 or not case_2:
1042
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1)
1043
+ if len(blocks[-1].finished_sub_slots) > 0:
1044
+ new_finished_ss = recursive_replace(
1045
+ blocks[-1].finished_sub_slots[-1],
1046
+ "reward_chain",
1047
+ recursive_replace(
1048
+ blocks[-1].finished_sub_slots[-1].reward_chain,
1049
+ "deficit",
1050
+ uint8(0),
1051
+ ),
1052
+ )
1053
+ if blockchain.block_record(blocks[-2].header_hash).deficit == 0:
1054
+ case_1 = True
1055
+ else:
1056
+ case_2 = True
1057
+
1058
+ block_bad = recursive_replace(
1059
+ blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
1060
+ )
1061
+ await _validate_and_add_block_multi_error(
1062
+ empty_blockchain, block_bad, [Err.INVALID_DEFICIT, Err.INVALID_ICC_HASH_CC]
1063
+ )
1064
+
1065
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1066
+
1067
+ @pytest.mark.anyio
1068
+ async def test_genesis_has_ses(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1069
+ # 3a
1070
+ block = bt.get_consecutive_blocks(1, skip_slots=1)[0]
1071
+ new_finished_ss = recursive_replace(
1072
+ block.finished_sub_slots[0],
1073
+ "challenge_chain",
1074
+ recursive_replace(
1075
+ block.finished_sub_slots[0].challenge_chain,
1076
+ "subepoch_summary_hash",
1077
+ bytes32.zeros,
1078
+ ),
1079
+ )
1080
+
1081
+ new_finished_ss = recursive_replace(
1082
+ new_finished_ss,
1083
+ "reward_chain",
1084
+ new_finished_ss.reward_chain.replace(
1085
+ challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash()
1086
+ ),
1087
+ )
1088
+ block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:])
1089
+ with pytest.raises(AssertionError):
1090
+ # Fails pre validation
1091
+ await _validate_and_add_block(
1092
+ empty_blockchain, block_bad, expected_error=Err.INVALID_SUB_EPOCH_SUMMARY_HASH
1093
+ )
1094
+
1095
+ @pytest.mark.anyio
1096
+ async def test_no_ses_if_no_se(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1097
+ # 3b
1098
+ blocks = bt.get_consecutive_blocks(1)
1099
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1100
+
1101
+ while True:
1102
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1103
+ if len(blocks[-1].finished_sub_slots) > 0 and is_overflow_block(
1104
+ bt.constants, blocks[-1].reward_chain_block.signage_point_index
1105
+ ):
1106
+ new_finished_ss: EndOfSubSlotBundle = recursive_replace(
1107
+ blocks[-1].finished_sub_slots[0],
1108
+ "challenge_chain",
1109
+ recursive_replace(
1110
+ blocks[-1].finished_sub_slots[0].challenge_chain,
1111
+ "subepoch_summary_hash",
1112
+ bytes32.zeros,
1113
+ ),
1114
+ )
1115
+
1116
+ new_finished_ss = recursive_replace(
1117
+ new_finished_ss,
1118
+ "reward_chain",
1119
+ new_finished_ss.reward_chain.replace(
1120
+ challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash(),
1121
+ ),
1122
+ )
1123
+ block_bad = recursive_replace(
1124
+ blocks[-1], "finished_sub_slots", [new_finished_ss] + blocks[-1].finished_sub_slots[1:]
1125
+ )
1126
+ await _validate_and_add_block_multi_error(
1127
+ empty_blockchain,
1128
+ block_bad,
1129
+ expected_errors=[
1130
+ Err.INVALID_SUB_EPOCH_SUMMARY_HASH,
1131
+ Err.INVALID_SUB_EPOCH_SUMMARY,
1132
+ ],
1133
+ )
1134
+ return None
1135
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1136
+
1137
+ @pytest.mark.anyio
1138
+ async def test_too_many_blocks(self, empty_blockchain: Blockchain) -> None:
1139
+ # 4: TODO
1140
+ pass
1141
+
1142
+ @pytest.mark.anyio
1143
+ async def test_bad_pos(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1144
+ # 5
1145
+ blocks = bt.get_consecutive_blocks(2)
1146
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1147
+
1148
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.proof_of_space.challenge", std_hash(b""))
1149
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1150
+
1151
+ block_bad = recursive_replace(
1152
+ blocks[-1], "reward_chain_block.proof_of_space.pool_contract_puzzle_hash", std_hash(b"")
1153
+ )
1154
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1155
+
1156
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.proof_of_space.size", 62)
1157
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1158
+
1159
+ block_bad = recursive_replace(
1160
+ blocks[-1],
1161
+ "reward_chain_block.proof_of_space.plot_public_key",
1162
+ AugSchemeMPL.key_gen(std_hash(b"1231n")).get_g1(),
1163
+ )
1164
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1165
+ block_bad = recursive_replace(
1166
+ blocks[-1],
1167
+ "reward_chain_block.proof_of_space.size",
1168
+ 32,
1169
+ )
1170
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1171
+ block_bad = recursive_replace(
1172
+ blocks[-1],
1173
+ "reward_chain_block.proof_of_space.proof",
1174
+ bytes([1] * int(blocks[-1].reward_chain_block.proof_of_space.size * 64 / 8)),
1175
+ )
1176
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POSPACE)
1177
+
1178
+ # TODO: test not passing the plot filter
1179
+
1180
+ @pytest.mark.anyio
1181
+ async def test_bad_signage_point_index(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1182
+ # 6
1183
+ blocks = bt.get_consecutive_blocks(2)
1184
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1185
+
1186
+ with pytest.raises(ValueError):
1187
+ block_bad = recursive_replace(
1188
+ blocks[-1], "reward_chain_block.signage_point_index", bt.constants.NUM_SPS_SUB_SLOT
1189
+ )
1190
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_SP_INDEX)
1191
+ with pytest.raises(ValueError):
1192
+ block_bad = recursive_replace(
1193
+ blocks[-1], "reward_chain_block.signage_point_index", bt.constants.NUM_SPS_SUB_SLOT + 1
1194
+ )
1195
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_SP_INDEX)
1196
+
1197
+ @pytest.mark.anyio
1198
+ async def test_sp_0_no_sp(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1199
+ # 7
1200
+ blocks: list[FullBlock] = []
1201
+ case_1, case_2 = False, False
1202
+ while not case_1 or not case_2:
1203
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1204
+ if blocks[-1].reward_chain_block.signage_point_index == 0:
1205
+ case_1 = True
1206
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.signage_point_index", uint8(1))
1207
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_SP_INDEX)
1208
+
1209
+ elif not is_overflow_block(bt.constants, blocks[-1].reward_chain_block.signage_point_index):
1210
+ case_2 = True
1211
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.signage_point_index", uint8(0))
1212
+ await _validate_and_add_block_multi_error(
1213
+ empty_blockchain, block_bad, [Err.INVALID_SP_INDEX, Err.INVALID_POSPACE]
1214
+ )
1215
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1216
+
1217
+ @pytest.mark.anyio
1218
+ async def test_epoch_overflows(self, empty_blockchain: Blockchain) -> None:
1219
+ # 9. TODO. This is hard to test because it requires modifying the block tools to make these special blocks
1220
+ pass
1221
+
1222
+ @pytest.mark.anyio
1223
+ async def test_bad_total_iters(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1224
+ # 10
1225
+ blocks = bt.get_consecutive_blocks(2)
1226
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1227
+
1228
+ block_bad = recursive_replace(
1229
+ blocks[-1], "reward_chain_block.total_iters", blocks[-1].reward_chain_block.total_iters + 1
1230
+ )
1231
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_TOTAL_ITERS)
1232
+
1233
+ @pytest.mark.anyio
1234
+ async def test_bad_rc_sp_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1235
+ # 11
1236
+ blocks = bt.get_consecutive_blocks(1)
1237
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1238
+
1239
+ while True:
1240
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1241
+ if blocks[-1].reward_chain_block.signage_point_index != 0:
1242
+ block_bad = recursive_replace(
1243
+ blocks[-1], "reward_chain_block.reward_chain_sp_vdf.challenge", std_hash(b"1")
1244
+ )
1245
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_SP_VDF)
1246
+ block_bad = recursive_replace(
1247
+ blocks[-1],
1248
+ "reward_chain_block.reward_chain_sp_vdf.output",
1249
+ bad_element,
1250
+ )
1251
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_SP_VDF)
1252
+ block_bad = recursive_replace(
1253
+ blocks[-1],
1254
+ "reward_chain_block.reward_chain_sp_vdf.number_of_iterations",
1255
+ uint64(1111111111111),
1256
+ )
1257
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_SP_VDF)
1258
+ block_bad = recursive_replace(
1259
+ blocks[-1],
1260
+ "reward_chain_sp_proof",
1261
+ VDFProof(uint8(0), std_hash(b""), False),
1262
+ )
1263
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_SP_VDF)
1264
+ return None
1265
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1266
+
1267
+ @pytest.mark.anyio
1268
+ async def test_bad_rc_sp_sig(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1269
+ # 12
1270
+ blocks = bt.get_consecutive_blocks(2)
1271
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1272
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.reward_chain_sp_signature", G2Element.generator())
1273
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_SIGNATURE)
1274
+
1275
+ @pytest.mark.anyio
1276
+ async def test_bad_cc_sp_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1277
+ # 13. Note: does not validate fully due to proof of space being validated first
1278
+
1279
+ blocks = bt.get_consecutive_blocks(1)
1280
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1281
+
1282
+ while True:
1283
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1284
+ if blocks[-1].reward_chain_block.signage_point_index != 0:
1285
+ block_bad = recursive_replace(
1286
+ blocks[-1], "reward_chain_block.challenge_chain_sp_vdf.challenge", std_hash(b"1")
1287
+ )
1288
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK)
1289
+ block_bad = recursive_replace(
1290
+ blocks[-1],
1291
+ "reward_chain_block.challenge_chain_sp_vdf.output",
1292
+ bad_element,
1293
+ )
1294
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK)
1295
+ block_bad = recursive_replace(
1296
+ blocks[-1],
1297
+ "reward_chain_block.challenge_chain_sp_vdf.number_of_iterations",
1298
+ uint64(1111111111111),
1299
+ )
1300
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_result=AddBlockResult.INVALID_BLOCK)
1301
+ block_bad = recursive_replace(
1302
+ blocks[-1],
1303
+ "challenge_chain_sp_proof",
1304
+ VDFProof(uint8(0), std_hash(b""), False),
1305
+ )
1306
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_SP_VDF)
1307
+ return None
1308
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1309
+
1310
+ @pytest.mark.anyio
1311
+ async def test_bad_cc_sp_sig(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1312
+ # 14
1313
+ blocks = bt.get_consecutive_blocks(2)
1314
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1315
+ block_bad = recursive_replace(
1316
+ blocks[-1], "reward_chain_block.challenge_chain_sp_signature", G2Element.generator()
1317
+ )
1318
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_SIGNATURE)
1319
+
1320
+ @pytest.mark.anyio
1321
+ async def test_is_transaction_block(self, empty_blockchain: Blockchain) -> None:
1322
+ # 15: TODO
1323
+ pass
1324
+
1325
+ @pytest.mark.anyio
1326
+ async def test_bad_foliage_sb_sig(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1327
+ # 16
1328
+ blocks = bt.get_consecutive_blocks(2)
1329
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1330
+ block_bad = recursive_replace(blocks[-1], "foliage.foliage_block_data_signature", G2Element.generator())
1331
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PLOT_SIGNATURE)
1332
+
1333
+ @pytest.mark.anyio
1334
+ async def test_bad_foliage_transaction_block_sig(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1335
+ # 17
1336
+ blocks = bt.get_consecutive_blocks(1)
1337
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1338
+
1339
+ while True:
1340
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1341
+ if blocks[-1].foliage_transaction_block is not None:
1342
+ block_bad = recursive_replace(
1343
+ blocks[-1], "foliage.foliage_transaction_block_signature", G2Element.generator()
1344
+ )
1345
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PLOT_SIGNATURE)
1346
+ return None
1347
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1348
+
1349
+ @pytest.mark.anyio
1350
+ async def test_unfinished_reward_chain_sb_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1351
+ # 18
1352
+ blocks = bt.get_consecutive_blocks(2)
1353
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1354
+ block_bad: FullBlock = recursive_replace(
1355
+ blocks[-1], "foliage.foliage_block_data.unfinished_reward_block_hash", std_hash(b"2")
1356
+ )
1357
+ new_m = block_bad.foliage.foliage_block_data.get_hash()
1358
+ assert new_m is not None
1359
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1360
+ block_bad = recursive_replace(block_bad, "foliage.foliage_block_data_signature", new_fsb_sig)
1361
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_URSB_HASH)
1362
+
1363
+ @pytest.mark.anyio
1364
+ async def test_pool_target_height(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1365
+ # 19
1366
+ blocks = bt.get_consecutive_blocks(3)
1367
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1368
+ await _validate_and_add_block(empty_blockchain, blocks[1])
1369
+ block_bad: FullBlock = recursive_replace(blocks[-1], "foliage.foliage_block_data.pool_target.max_height", 1)
1370
+ new_m = block_bad.foliage.foliage_block_data.get_hash()
1371
+ assert new_m is not None
1372
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1373
+ block_bad = recursive_replace(block_bad, "foliage.foliage_block_data_signature", new_fsb_sig)
1374
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.OLD_POOL_TARGET)
1375
+
1376
+ @pytest.mark.anyio
1377
+ async def test_pool_target_pre_farm(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1378
+ # 20a
1379
+ blocks = bt.get_consecutive_blocks(1)
1380
+ block_bad: FullBlock = recursive_replace(
1381
+ blocks[-1], "foliage.foliage_block_data.pool_target.puzzle_hash", std_hash(b"12")
1382
+ )
1383
+ new_m = block_bad.foliage.foliage_block_data.get_hash()
1384
+ assert new_m is not None
1385
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1386
+ block_bad = recursive_replace(block_bad, "foliage.foliage_block_data_signature", new_fsb_sig)
1387
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PREFARM)
1388
+
1389
+ @pytest.mark.anyio
1390
+ async def test_pool_target_signature(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1391
+ # 20b
1392
+ blocks_initial = bt.get_consecutive_blocks(2)
1393
+ await _validate_and_add_block(empty_blockchain, blocks_initial[0])
1394
+ await _validate_and_add_block(empty_blockchain, blocks_initial[1])
1395
+
1396
+ attempts = 0
1397
+ while True:
1398
+ # Go until we get a block that has a pool pk, as opposed to a pool contract
1399
+ blocks = bt.get_consecutive_blocks(
1400
+ 1, blocks_initial, seed=std_hash(attempts.to_bytes(4, byteorder="big", signed=False))
1401
+ )
1402
+ if blocks[-1].foliage.foliage_block_data.pool_signature is not None:
1403
+ block_bad: FullBlock = recursive_replace(
1404
+ blocks[-1], "foliage.foliage_block_data.pool_signature", G2Element.generator()
1405
+ )
1406
+ new_m = block_bad.foliage.foliage_block_data.get_hash()
1407
+ assert new_m is not None
1408
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1409
+ block_bad = recursive_replace(block_bad, "foliage.foliage_block_data_signature", new_fsb_sig)
1410
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POOL_SIGNATURE)
1411
+ return None
1412
+ attempts += 1
1413
+
1414
+ @pytest.mark.anyio
1415
+ async def test_pool_target_contract(
1416
+ self, empty_blockchain: Blockchain, bt: BlockTools, seeded_random: random.Random
1417
+ ) -> None:
1418
+ # 20c invalid pool target with contract
1419
+ blocks_initial = bt.get_consecutive_blocks(2)
1420
+ await _validate_and_add_block(empty_blockchain, blocks_initial[0])
1421
+ await _validate_and_add_block(empty_blockchain, blocks_initial[1])
1422
+
1423
+ attempts = 0
1424
+ while True:
1425
+ # Go until we get a block that has a pool contract opposed to a pool pk
1426
+ blocks = bt.get_consecutive_blocks(
1427
+ 1, blocks_initial, seed=std_hash(attempts.to_bytes(4, byteorder="big", signed=False))
1428
+ )
1429
+ if blocks[-1].foliage.foliage_block_data.pool_signature is None:
1430
+ block_bad: FullBlock = recursive_replace(
1431
+ blocks[-1], "foliage.foliage_block_data.pool_target.puzzle_hash", bytes32.random(seeded_random)
1432
+ )
1433
+ new_m = block_bad.foliage.foliage_block_data.get_hash()
1434
+ assert new_m is not None
1435
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1436
+ block_bad = recursive_replace(block_bad, "foliage.foliage_block_data_signature", new_fsb_sig)
1437
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_POOL_TARGET)
1438
+ return None
1439
+ attempts += 1
1440
+
1441
+ @pytest.mark.anyio
1442
+ async def test_foliage_data_presence(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1443
+ # 22
1444
+ blocks = bt.get_consecutive_blocks(1)
1445
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1446
+ case_1, case_2 = False, False
1447
+ while not case_1 or not case_2:
1448
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1449
+ if blocks[-1].foliage_transaction_block is not None:
1450
+ case_1 = True
1451
+ block_bad: FullBlock = recursive_replace(blocks[-1], "foliage.foliage_transaction_block_hash", None)
1452
+ else:
1453
+ case_2 = True
1454
+ block_bad = recursive_replace(blocks[-1], "foliage.foliage_transaction_block_hash", std_hash(b""))
1455
+ await _validate_and_add_block_multi_error(
1456
+ empty_blockchain,
1457
+ block_bad,
1458
+ [
1459
+ Err.INVALID_FOLIAGE_BLOCK_PRESENCE,
1460
+ Err.INVALID_IS_TRANSACTION_BLOCK,
1461
+ Err.INVALID_PREV_BLOCK_HASH,
1462
+ Err.INVALID_PREV_BLOCK_HASH,
1463
+ ],
1464
+ )
1465
+
1466
+ @pytest.mark.anyio
1467
+ async def test_foliage_transaction_block_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1468
+ # 23
1469
+ blocks = bt.get_consecutive_blocks(1)
1470
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1471
+ case_1, case_2 = False, False
1472
+ while not case_1 or not case_2:
1473
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1474
+ if blocks[-1].foliage_transaction_block is not None:
1475
+ block_bad: FullBlock = recursive_replace(
1476
+ blocks[-1], "foliage.foliage_transaction_block_hash", std_hash(b"2")
1477
+ )
1478
+
1479
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1480
+ assert new_m is not None
1481
+ new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1482
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1483
+ await _validate_and_add_block(
1484
+ empty_blockchain, block_bad, expected_error=Err.INVALID_FOLIAGE_BLOCK_HASH
1485
+ )
1486
+ return None
1487
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1488
+
1489
+ @pytest.mark.anyio
1490
+ async def test_genesis_bad_prev_block(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1491
+ # 24a
1492
+ blocks = bt.get_consecutive_blocks(1)
1493
+ block_bad: FullBlock = recursive_replace(
1494
+ blocks[-1], "foliage_transaction_block.prev_transaction_block_hash", std_hash(b"2")
1495
+ )
1496
+ assert block_bad.foliage_transaction_block is not None
1497
+ block_bad = recursive_replace(
1498
+ block_bad, "foliage.foliage_transaction_block_hash", block_bad.foliage_transaction_block.get_hash()
1499
+ )
1500
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1501
+ assert new_m is not None
1502
+ new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1503
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1504
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PREV_BLOCK_HASH)
1505
+
1506
+ @pytest.mark.anyio
1507
+ async def test_bad_prev_block_non_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1508
+ # 24b
1509
+ blocks = bt.get_consecutive_blocks(1)
1510
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1511
+ while True:
1512
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1513
+ if blocks[-1].foliage_transaction_block is not None:
1514
+ block_bad: FullBlock = recursive_replace(
1515
+ blocks[-1], "foliage_transaction_block.prev_transaction_block_hash", std_hash(b"2")
1516
+ )
1517
+ assert block_bad.foliage_transaction_block is not None
1518
+ block_bad = recursive_replace(
1519
+ block_bad, "foliage.foliage_transaction_block_hash", block_bad.foliage_transaction_block.get_hash()
1520
+ )
1521
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1522
+ assert new_m is not None
1523
+ new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1524
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1525
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PREV_BLOCK_HASH)
1526
+ return None
1527
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1528
+
1529
+ @pytest.mark.anyio
1530
+ async def test_bad_filter_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1531
+ # 25
1532
+ blocks = bt.get_consecutive_blocks(1)
1533
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1534
+ while True:
1535
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1536
+ if blocks[-1].foliage_transaction_block is not None:
1537
+ block_bad: FullBlock = recursive_replace(
1538
+ blocks[-1], "foliage_transaction_block.filter_hash", std_hash(b"2")
1539
+ )
1540
+ assert block_bad.foliage_transaction_block is not None
1541
+ block_bad = recursive_replace(
1542
+ block_bad, "foliage.foliage_transaction_block_hash", block_bad.foliage_transaction_block.get_hash()
1543
+ )
1544
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1545
+ assert new_m is not None
1546
+ new_fbh_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
1547
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1548
+ await _validate_and_add_block(
1549
+ empty_blockchain, block_bad, expected_error=Err.INVALID_TRANSACTIONS_FILTER_HASH
1550
+ )
1551
+ return None
1552
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1553
+
1554
+ @pytest.mark.anyio
1555
+ async def test_bad_timestamp(self, bt: BlockTools) -> None:
1556
+ # 26
1557
+ # the test constants set MAX_FUTURE_TIME to 10 days, restore it to
1558
+ # default for this test
1559
+ constants = bt.constants.replace(MAX_FUTURE_TIME2=uint32(2 * 60))
1560
+ time_delta = 2 * 60 + 1
1561
+
1562
+ blocks = bt.get_consecutive_blocks(1)
1563
+
1564
+ async with make_empty_blockchain(constants) as b:
1565
+ await _validate_and_add_block(b, blocks[0])
1566
+ while True:
1567
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1568
+ if blocks[-1].foliage_transaction_block is not None:
1569
+ assert blocks[0].foliage_transaction_block is not None
1570
+ block_bad: FullBlock = recursive_replace(
1571
+ blocks[-1],
1572
+ "foliage_transaction_block.timestamp",
1573
+ blocks[0].foliage_transaction_block.timestamp - 10,
1574
+ )
1575
+ assert block_bad.foliage_transaction_block is not None
1576
+ block_bad = recursive_replace(
1577
+ block_bad,
1578
+ "foliage.foliage_transaction_block_hash",
1579
+ block_bad.foliage_transaction_block.get_hash(),
1580
+ )
1581
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1582
+ assert new_m is not None
1583
+ new_fbh_sig = bt.get_plot_signature(
1584
+ new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key
1585
+ )
1586
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1587
+ await _validate_and_add_block(b, block_bad, expected_error=Err.TIMESTAMP_TOO_FAR_IN_PAST)
1588
+
1589
+ assert blocks[0].foliage_transaction_block is not None
1590
+ block_bad = recursive_replace(
1591
+ blocks[-1],
1592
+ "foliage_transaction_block.timestamp",
1593
+ blocks[0].foliage_transaction_block.timestamp,
1594
+ )
1595
+ assert block_bad.foliage_transaction_block is not None
1596
+ block_bad = recursive_replace(
1597
+ block_bad,
1598
+ "foliage.foliage_transaction_block_hash",
1599
+ block_bad.foliage_transaction_block.get_hash(),
1600
+ )
1601
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1602
+ assert new_m is not None
1603
+ new_fbh_sig = bt.get_plot_signature(
1604
+ new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key
1605
+ )
1606
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1607
+ await _validate_and_add_block(b, block_bad, expected_error=Err.TIMESTAMP_TOO_FAR_IN_PAST)
1608
+
1609
+ # since tests can run slow sometimes, and since we're using
1610
+ # the system clock, add some extra slack
1611
+ slack = 30
1612
+ block_bad = recursive_replace(
1613
+ blocks[-1],
1614
+ "foliage_transaction_block.timestamp",
1615
+ blocks[0].foliage_transaction_block.timestamp + time_delta + slack,
1616
+ )
1617
+ assert block_bad.foliage_transaction_block is not None
1618
+ block_bad = recursive_replace(
1619
+ block_bad,
1620
+ "foliage.foliage_transaction_block_hash",
1621
+ block_bad.foliage_transaction_block.get_hash(),
1622
+ )
1623
+ new_m = block_bad.foliage.foliage_transaction_block_hash
1624
+ assert new_m is not None
1625
+ new_fbh_sig = bt.get_plot_signature(
1626
+ new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key
1627
+ )
1628
+ block_bad = recursive_replace(block_bad, "foliage.foliage_transaction_block_signature", new_fbh_sig)
1629
+ await _validate_and_add_block(b, block_bad, expected_error=Err.TIMESTAMP_TOO_FAR_IN_FUTURE)
1630
+ return None
1631
+ await _validate_and_add_block(b, blocks[-1])
1632
+
1633
+ @pytest.mark.anyio
1634
+ async def test_height(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1635
+ # 27
1636
+ blocks = bt.get_consecutive_blocks(2)
1637
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1638
+ block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_block.height", 2)
1639
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_HEIGHT)
1640
+
1641
+ @pytest.mark.anyio
1642
+ async def test_height_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1643
+ # 27
1644
+ blocks = bt.get_consecutive_blocks(1)
1645
+ block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_block.height", 1)
1646
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_PREV_BLOCK_HASH)
1647
+
1648
+ @pytest.mark.anyio
1649
+ async def test_weight(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1650
+ # 28
1651
+ blocks = bt.get_consecutive_blocks(2)
1652
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1653
+ block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_block.weight", 22131)
1654
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_WEIGHT)
1655
+
1656
+ @pytest.mark.anyio
1657
+ async def test_weight_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1658
+ # 28
1659
+ blocks = bt.get_consecutive_blocks(1)
1660
+ block_bad: FullBlock = recursive_replace(blocks[-1], "reward_chain_block.weight", 0)
1661
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_WEIGHT)
1662
+
1663
+ @pytest.mark.anyio
1664
+ async def test_bad_cc_ip_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1665
+ # 29
1666
+ blocks = bt.get_consecutive_blocks(1)
1667
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1668
+
1669
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1670
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.challenge_chain_ip_vdf.challenge", std_hash(b"1"))
1671
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_IP_VDF)
1672
+ block_bad = recursive_replace(
1673
+ blocks[-1],
1674
+ "reward_chain_block.challenge_chain_ip_vdf.output",
1675
+ bad_element,
1676
+ )
1677
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_IP_VDF)
1678
+ block_bad = recursive_replace(
1679
+ blocks[-1],
1680
+ "reward_chain_block.challenge_chain_ip_vdf.number_of_iterations",
1681
+ uint64(1111111111111),
1682
+ )
1683
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_IP_VDF)
1684
+ block_bad = recursive_replace(
1685
+ blocks[-1],
1686
+ "challenge_chain_ip_proof",
1687
+ VDFProof(uint8(0), std_hash(b""), False),
1688
+ )
1689
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_CC_IP_VDF)
1690
+
1691
+ @pytest.mark.anyio
1692
+ async def test_bad_rc_ip_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1693
+ # 30
1694
+ blocks = bt.get_consecutive_blocks(1)
1695
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1696
+
1697
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1698
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.reward_chain_ip_vdf.challenge", std_hash(b"1"))
1699
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_IP_VDF)
1700
+ block_bad = recursive_replace(
1701
+ blocks[-1],
1702
+ "reward_chain_block.reward_chain_ip_vdf.output",
1703
+ bad_element,
1704
+ )
1705
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_IP_VDF)
1706
+ block_bad = recursive_replace(
1707
+ blocks[-1],
1708
+ "reward_chain_block.reward_chain_ip_vdf.number_of_iterations",
1709
+ uint64(1111111111111),
1710
+ )
1711
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_IP_VDF)
1712
+ block_bad = recursive_replace(
1713
+ blocks[-1],
1714
+ "reward_chain_ip_proof",
1715
+ VDFProof(uint8(0), std_hash(b""), False),
1716
+ )
1717
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_RC_IP_VDF)
1718
+
1719
+ @pytest.mark.anyio
1720
+ async def test_bad_icc_ip_vdf(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1721
+ # 31
1722
+ blocks = bt.get_consecutive_blocks(1)
1723
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1724
+
1725
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1726
+ block_bad = recursive_replace(
1727
+ blocks[-1], "reward_chain_block.infused_challenge_chain_ip_vdf.challenge", std_hash(b"1")
1728
+ )
1729
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_ICC_VDF)
1730
+ block_bad = recursive_replace(
1731
+ blocks[-1],
1732
+ "reward_chain_block.infused_challenge_chain_ip_vdf.output",
1733
+ bad_element,
1734
+ )
1735
+
1736
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_ICC_VDF)
1737
+ block_bad = recursive_replace(
1738
+ blocks[-1],
1739
+ "reward_chain_block.infused_challenge_chain_ip_vdf.number_of_iterations",
1740
+ uint64(1111111111111),
1741
+ )
1742
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_ICC_VDF)
1743
+ block_bad = recursive_replace(
1744
+ blocks[-1],
1745
+ "infused_challenge_chain_ip_proof",
1746
+ VDFProof(uint8(0), std_hash(b""), False),
1747
+ )
1748
+
1749
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_ICC_VDF)
1750
+
1751
+ @pytest.mark.anyio
1752
+ async def test_reward_block_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1753
+ # 32
1754
+ blocks = bt.get_consecutive_blocks(2)
1755
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1756
+ block_bad: FullBlock = recursive_replace(blocks[-1], "foliage.reward_block_hash", std_hash(b""))
1757
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_REWARD_BLOCK_HASH)
1758
+
1759
+ @pytest.mark.anyio
1760
+ async def test_reward_block_hash_2(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1761
+ # 33
1762
+ blocks = bt.get_consecutive_blocks(1)
1763
+ block_bad: FullBlock = recursive_replace(blocks[0], "reward_chain_block.is_transaction_block", False)
1764
+ block_bad = recursive_replace(block_bad, "foliage.reward_block_hash", block_bad.reward_chain_block.get_hash())
1765
+ await _validate_and_add_block(empty_blockchain, block_bad, expected_error=Err.INVALID_FOLIAGE_BLOCK_PRESENCE)
1766
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1767
+
1768
+ # Test one which should not be a tx block
1769
+ while True:
1770
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
1771
+ if not blocks[-1].is_transaction_block():
1772
+ block_bad = recursive_replace(blocks[-1], "reward_chain_block.is_transaction_block", True)
1773
+ block_bad = recursive_replace(
1774
+ block_bad, "foliage.reward_block_hash", block_bad.reward_chain_block.get_hash()
1775
+ )
1776
+ await _validate_and_add_block(
1777
+ empty_blockchain, block_bad, expected_error=Err.INVALID_FOLIAGE_BLOCK_PRESENCE
1778
+ )
1779
+ return None
1780
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
1781
+
1782
+
1783
+ co = ConditionOpcode
1784
+ rbr = AddBlockResult
1785
+
1786
+
1787
+ class TestPreValidation:
1788
+ @pytest.mark.anyio
1789
+ async def test_pre_validation_fails_bad_blocks(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
1790
+ blocks = bt.get_consecutive_blocks(2)
1791
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1792
+ ssi = empty_blockchain.constants.SUB_SLOT_ITERS_STARTING
1793
+ difficulty = empty_blockchain.constants.DIFFICULTY_STARTING
1794
+ block_bad = recursive_replace(
1795
+ blocks[-1], "reward_chain_block.total_iters", blocks[-1].reward_chain_block.total_iters + 1
1796
+ )
1797
+ futures = []
1798
+ vs = ValidationState(ssi, difficulty, None)
1799
+ chain = AugmentedBlockchain(empty_blockchain)
1800
+ for block in [blocks[0], block_bad]:
1801
+ futures.append(
1802
+ await pre_validate_block(
1803
+ empty_blockchain.constants,
1804
+ chain,
1805
+ block,
1806
+ empty_blockchain.pool,
1807
+ None,
1808
+ vs,
1809
+ )
1810
+ )
1811
+ res: list[PreValidationResult] = list(await asyncio.gather(*futures))
1812
+ assert res[0].error is None
1813
+ assert res[1].error is not None
1814
+
1815
+ @pytest.mark.anyio
1816
+ async def test_pre_validation(
1817
+ self, empty_blockchain: Blockchain, default_1000_blocks: list[FullBlock], bt: BlockTools
1818
+ ) -> None:
1819
+ blocks = default_1000_blocks[:100]
1820
+ start = time.time()
1821
+ ssi = empty_blockchain.constants.SUB_SLOT_ITERS_STARTING
1822
+ difficulty = empty_blockchain.constants.DIFFICULTY_STARTING
1823
+ blockchain = AugmentedBlockchain(empty_blockchain)
1824
+ vs = ValidationState(ssi, difficulty, None)
1825
+ futures: list[Awaitable[PreValidationResult]] = []
1826
+ start = time.monotonic()
1827
+ for block in blocks:
1828
+ futures.append(
1829
+ await pre_validate_block(
1830
+ bt.constants,
1831
+ blockchain,
1832
+ block,
1833
+ empty_blockchain.pool,
1834
+ None,
1835
+ vs,
1836
+ )
1837
+ )
1838
+
1839
+ results = await asyncio.gather(*futures)
1840
+ end = time.monotonic()
1841
+ validation_time = start - end
1842
+ db_start = end
1843
+
1844
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
1845
+ for block, res in zip(blocks, results):
1846
+ assert res.error is None
1847
+ result, err, _ = await empty_blockchain.add_block(block, res, ssi, fork_info=fork_info)
1848
+ assert err is None
1849
+ assert result == AddBlockResult.NEW_PEAK
1850
+ end = time.monotonic()
1851
+ log.info(f"Total time: {end - start} seconds")
1852
+ log.info(f"Average validation: {validation_time / len(blocks)}")
1853
+ log.info(f"Average database: {(end - db_start) / (len(blocks))}")
1854
+
1855
+
1856
+ class TestBodyValidation:
1857
+ # TODO: add test for
1858
+ # ASSERT_COIN_ANNOUNCEMENT,
1859
+ # CREATE_COIN_ANNOUNCEMENT,
1860
+ # CREATE_PUZZLE_ANNOUNCEMENT,
1861
+ # ASSERT_PUZZLE_ANNOUNCEMENT,
1862
+
1863
+ @pytest.mark.anyio
1864
+ @pytest.mark.parametrize(
1865
+ "opcode",
1866
+ [
1867
+ ConditionOpcode.ASSERT_MY_AMOUNT,
1868
+ ConditionOpcode.ASSERT_MY_PUZZLEHASH,
1869
+ ConditionOpcode.ASSERT_MY_COIN_ID,
1870
+ ConditionOpcode.ASSERT_MY_PARENT_ID,
1871
+ ],
1872
+ )
1873
+ @pytest.mark.parametrize("with_garbage", [True, False])
1874
+ async def test_conditions(
1875
+ self, empty_blockchain: Blockchain, opcode: ConditionOpcode, with_garbage: bool, bt: BlockTools
1876
+ ) -> None:
1877
+ b = empty_blockchain
1878
+ blocks = bt.get_consecutive_blocks(
1879
+ 3,
1880
+ guarantee_transaction_block=True,
1881
+ farmer_reward_puzzle_hash=bt.pool_ph,
1882
+ pool_reward_puzzle_hash=bt.pool_ph,
1883
+ genesis_timestamp=uint64(10_000),
1884
+ time_per_block=10,
1885
+ )
1886
+ await _validate_and_add_block(empty_blockchain, blocks[0])
1887
+ await _validate_and_add_block(empty_blockchain, blocks[1])
1888
+ await _validate_and_add_block(empty_blockchain, blocks[2])
1889
+
1890
+ wt: WalletTool = bt.get_pool_wallet_tool()
1891
+
1892
+ tx1 = wt.generate_signed_transaction(
1893
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
1894
+ )
1895
+ coin1: Coin = tx1.additions()[0]
1896
+
1897
+ if opcode == ConditionOpcode.ASSERT_MY_AMOUNT:
1898
+ args = [int_to_bytes(coin1.amount)]
1899
+ elif opcode == ConditionOpcode.ASSERT_MY_PUZZLEHASH:
1900
+ args = [coin1.puzzle_hash]
1901
+ elif opcode == ConditionOpcode.ASSERT_MY_COIN_ID:
1902
+ args = [coin1.name()]
1903
+ elif opcode == ConditionOpcode.ASSERT_MY_PARENT_ID:
1904
+ args = [coin1.parent_coin_info]
1905
+ # elif opcode == ConditionOpcode.RESERVE_FEE:
1906
+ # args = [int_to_bytes(5)]
1907
+ # TODO: since we use the production wallet code, we can't (easily)
1908
+ # create a transaction with fee without also including a valid
1909
+ # RESERVE_FEE condition
1910
+ else:
1911
+ assert False
1912
+
1913
+ conditions: dict[ConditionOpcode, list[ConditionWithArgs]] = {
1914
+ opcode: [ConditionWithArgs(opcode, args + ([b"garbage"] if with_garbage else []))]
1915
+ }
1916
+
1917
+ tx2 = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin1, condition_dic=conditions)
1918
+ assert coin1 in tx2.removals()
1919
+
1920
+ bundles = SpendBundle.aggregate([tx1, tx2])
1921
+ blocks = bt.get_consecutive_blocks(
1922
+ 1,
1923
+ block_list_input=blocks,
1924
+ guarantee_transaction_block=True,
1925
+ transaction_data=bundles,
1926
+ time_per_block=10,
1927
+ )
1928
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
1929
+ diff = b.constants.DIFFICULTY_STARTING
1930
+ block = blocks[-1]
1931
+ future = await pre_validate_block(
1932
+ b.constants,
1933
+ AugmentedBlockchain(b),
1934
+ block,
1935
+ b.pool,
1936
+ None,
1937
+ ValidationState(ssi, diff, None),
1938
+ )
1939
+ pre_validation_result: PreValidationResult = await future
1940
+ # Ignore errors from pre-validation, we are testing block_body_validation
1941
+ repl_preval_results = replace(pre_validation_result, error=None, required_iters=uint64(1))
1942
+ fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
1943
+ code, err, state_change = await b.add_block(block, repl_preval_results, sub_slot_iters=ssi, fork_info=fork_info)
1944
+ assert code == AddBlockResult.NEW_PEAK
1945
+ assert err is None
1946
+ assert state_change is not None
1947
+ assert state_change.fork_height == 2
1948
+
1949
+ @pytest.mark.anyio
1950
+ @pytest.mark.parametrize(
1951
+ "opcode,lock_value,expected",
1952
+ [
1953
+ # the 3 blocks, starting at timestamp 10000 (and height 0).
1954
+ # each block is 10 seconds apart.
1955
+ # the 4th block (height 3, time 10030) spends a coin with the condition specified
1956
+ # by the test case. The coin was born in height 2 at time 10020
1957
+ # MY BIRHT HEIGHT
1958
+ (co.ASSERT_MY_BIRTH_HEIGHT, -1, rbr.INVALID_BLOCK),
1959
+ (co.ASSERT_MY_BIRTH_HEIGHT, 0x100000000, rbr.INVALID_BLOCK),
1960
+ (co.ASSERT_MY_BIRTH_HEIGHT, 2, rbr.NEW_PEAK), # <- coin birth height
1961
+ (co.ASSERT_MY_BIRTH_HEIGHT, 3, rbr.INVALID_BLOCK),
1962
+ # MY BIRHT SECONDS
1963
+ (co.ASSERT_MY_BIRTH_SECONDS, -1, rbr.INVALID_BLOCK),
1964
+ (co.ASSERT_MY_BIRTH_SECONDS, 0x10000000000000000, rbr.INVALID_BLOCK),
1965
+ (co.ASSERT_MY_BIRTH_SECONDS, 10019, rbr.INVALID_BLOCK),
1966
+ (co.ASSERT_MY_BIRTH_SECONDS, 10020, rbr.NEW_PEAK), # <- coin birth time
1967
+ (co.ASSERT_MY_BIRTH_SECONDS, 10021, rbr.INVALID_BLOCK),
1968
+ # SECONDS RELATIVE
1969
+ (co.ASSERT_SECONDS_RELATIVE, -2, rbr.NEW_PEAK),
1970
+ (co.ASSERT_SECONDS_RELATIVE, -1, rbr.NEW_PEAK),
1971
+ (co.ASSERT_SECONDS_RELATIVE, 0, rbr.NEW_PEAK), # <- birth time
1972
+ (co.ASSERT_SECONDS_RELATIVE, 1, rbr.INVALID_BLOCK),
1973
+ (co.ASSERT_SECONDS_RELATIVE, 9, rbr.INVALID_BLOCK),
1974
+ (co.ASSERT_SECONDS_RELATIVE, 10, rbr.INVALID_BLOCK), # <- current block time
1975
+ (co.ASSERT_SECONDS_RELATIVE, 11, rbr.INVALID_BLOCK),
1976
+ # BEFORE SECONDS RELATIVE
1977
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, -2, rbr.INVALID_BLOCK),
1978
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, -1, rbr.INVALID_BLOCK),
1979
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 0, rbr.INVALID_BLOCK), # <- birth time
1980
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 1, rbr.NEW_PEAK),
1981
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 9, rbr.NEW_PEAK),
1982
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 10, rbr.NEW_PEAK), # <- current block time
1983
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 11, rbr.NEW_PEAK),
1984
+ # HEIGHT RELATIVE
1985
+ (co.ASSERT_HEIGHT_RELATIVE, -2, rbr.NEW_PEAK),
1986
+ (co.ASSERT_HEIGHT_RELATIVE, -1, rbr.NEW_PEAK),
1987
+ (co.ASSERT_HEIGHT_RELATIVE, 0, rbr.NEW_PEAK),
1988
+ (co.ASSERT_HEIGHT_RELATIVE, 1, rbr.INVALID_BLOCK),
1989
+ # BEFORE HEIGHT RELATIVE
1990
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -2, rbr.INVALID_BLOCK),
1991
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -1, rbr.INVALID_BLOCK),
1992
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 0, rbr.INVALID_BLOCK),
1993
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 1, rbr.NEW_PEAK),
1994
+ # HEIGHT ABSOLUTE
1995
+ (co.ASSERT_HEIGHT_ABSOLUTE, 1, rbr.NEW_PEAK),
1996
+ (co.ASSERT_HEIGHT_ABSOLUTE, 2, rbr.NEW_PEAK),
1997
+ (co.ASSERT_HEIGHT_ABSOLUTE, 3, rbr.INVALID_BLOCK),
1998
+ (co.ASSERT_HEIGHT_ABSOLUTE, 4, rbr.INVALID_BLOCK),
1999
+ # BEFORE HEIGHT ABSOLUTE
2000
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 1, rbr.INVALID_BLOCK),
2001
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 2, rbr.INVALID_BLOCK),
2002
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 3, rbr.NEW_PEAK),
2003
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 4, rbr.NEW_PEAK),
2004
+ # SECONDS ABSOLUTE
2005
+ # genesis timestamp is 10000 and each block is 10 seconds
2006
+ (co.ASSERT_SECONDS_ABSOLUTE, 10019, rbr.NEW_PEAK),
2007
+ (co.ASSERT_SECONDS_ABSOLUTE, 10020, rbr.NEW_PEAK), # <- previous tx-block
2008
+ (co.ASSERT_SECONDS_ABSOLUTE, 10021, rbr.INVALID_BLOCK),
2009
+ (co.ASSERT_SECONDS_ABSOLUTE, 10029, rbr.INVALID_BLOCK),
2010
+ (co.ASSERT_SECONDS_ABSOLUTE, 10030, rbr.INVALID_BLOCK), # <- current block
2011
+ (co.ASSERT_SECONDS_ABSOLUTE, 10031, rbr.INVALID_BLOCK),
2012
+ (co.ASSERT_SECONDS_ABSOLUTE, 10032, rbr.INVALID_BLOCK),
2013
+ # BEFORE SECONDS ABSOLUTE
2014
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10019, rbr.INVALID_BLOCK),
2015
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10020, rbr.INVALID_BLOCK), # <- previous tx-block
2016
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10021, rbr.NEW_PEAK),
2017
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10029, rbr.NEW_PEAK),
2018
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10030, rbr.NEW_PEAK), # <- current block
2019
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10031, rbr.NEW_PEAK),
2020
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10032, rbr.NEW_PEAK),
2021
+ ],
2022
+ )
2023
+ async def test_timelock_conditions(
2024
+ self, opcode: ConditionOpcode, lock_value: int, expected: AddBlockResult, bt: BlockTools
2025
+ ) -> None:
2026
+ async with make_empty_blockchain(bt.constants) as b:
2027
+ blocks = bt.get_consecutive_blocks(
2028
+ 3,
2029
+ guarantee_transaction_block=True,
2030
+ farmer_reward_puzzle_hash=bt.pool_ph,
2031
+ pool_reward_puzzle_hash=bt.pool_ph,
2032
+ genesis_timestamp=uint64(10_000),
2033
+ time_per_block=10,
2034
+ )
2035
+ for bl in blocks:
2036
+ await _validate_and_add_block(b, bl)
2037
+
2038
+ wt: WalletTool = bt.get_pool_wallet_tool()
2039
+
2040
+ conditions = {opcode: [ConditionWithArgs(opcode, [int_to_bytes(lock_value)])]}
2041
+
2042
+ coin = blocks[-1].get_included_reward_coins()[0]
2043
+ tx = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin, condition_dic=conditions)
2044
+
2045
+ blocks = bt.get_consecutive_blocks(
2046
+ 1,
2047
+ block_list_input=blocks,
2048
+ guarantee_transaction_block=True,
2049
+ transaction_data=tx,
2050
+ time_per_block=10,
2051
+ )
2052
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
2053
+ diff = b.constants.DIFFICULTY_STARTING
2054
+ block = blocks[-1]
2055
+ future = await pre_validate_block(
2056
+ b.constants,
2057
+ AugmentedBlockchain(b),
2058
+ block,
2059
+ b.pool,
2060
+ None,
2061
+ ValidationState(ssi, diff, None),
2062
+ )
2063
+ pre_validation_result: PreValidationResult = await future
2064
+ fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
2065
+ assert (await b.add_block(block, pre_validation_result, sub_slot_iters=ssi, fork_info=fork_info))[
2066
+ 0
2067
+ ] == expected
2068
+
2069
+ if expected == AddBlockResult.NEW_PEAK:
2070
+ # ensure coin was in fact spent
2071
+ c = await b.coin_store.get_coin_record(coin.name())
2072
+ assert c is not None and c.spent
2073
+
2074
+ @pytest.mark.anyio
2075
+ @pytest.mark.parametrize(
2076
+ "opcode",
2077
+ [
2078
+ ConditionOpcode.AGG_SIG_ME,
2079
+ ConditionOpcode.AGG_SIG_UNSAFE,
2080
+ ConditionOpcode.AGG_SIG_PARENT,
2081
+ ConditionOpcode.AGG_SIG_PUZZLE,
2082
+ ConditionOpcode.AGG_SIG_AMOUNT,
2083
+ ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
2084
+ ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
2085
+ ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
2086
+ ],
2087
+ )
2088
+ @pytest.mark.parametrize("with_garbage", [True, False])
2089
+ async def test_aggsig_garbage(
2090
+ self,
2091
+ empty_blockchain: Blockchain,
2092
+ opcode: ConditionOpcode,
2093
+ with_garbage: bool,
2094
+ bt: BlockTools,
2095
+ consensus_mode: ConsensusMode,
2096
+ ) -> None:
2097
+ b = empty_blockchain
2098
+ blocks = bt.get_consecutive_blocks(
2099
+ 3,
2100
+ guarantee_transaction_block=True,
2101
+ farmer_reward_puzzle_hash=bt.pool_ph,
2102
+ pool_reward_puzzle_hash=bt.pool_ph,
2103
+ genesis_timestamp=uint64(10_000),
2104
+ time_per_block=10,
2105
+ )
2106
+ await _validate_and_add_block(empty_blockchain, blocks[0])
2107
+ await _validate_and_add_block(empty_blockchain, blocks[1])
2108
+ await _validate_and_add_block(empty_blockchain, blocks[2])
2109
+
2110
+ wt: WalletTool = bt.get_pool_wallet_tool()
2111
+
2112
+ tx1 = wt.generate_signed_transaction(
2113
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2114
+ )
2115
+ coin1: Coin = tx1.additions()[0]
2116
+ secret_key = wt.get_private_key_for_puzzle_hash(coin1.puzzle_hash)
2117
+ synthetic_secret_key = calculate_synthetic_secret_key(secret_key, DEFAULT_HIDDEN_PUZZLE_HASH)
2118
+ public_key = synthetic_secret_key.get_g1()
2119
+
2120
+ args = [bytes(public_key), b"msg"] + ([b"garbage"] if with_garbage else [])
2121
+ conditions = {opcode: [ConditionWithArgs(opcode, args)]}
2122
+
2123
+ tx2 = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin1, condition_dic=conditions)
2124
+ assert coin1 in tx2.removals()
2125
+
2126
+ bundles = SpendBundle.aggregate([tx1, tx2])
2127
+ blocks = bt.get_consecutive_blocks(
2128
+ 1,
2129
+ block_list_input=blocks,
2130
+ guarantee_transaction_block=True,
2131
+ transaction_data=bundles,
2132
+ time_per_block=10,
2133
+ )
2134
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
2135
+ diff = b.constants.DIFFICULTY_STARTING
2136
+ block = blocks[-1]
2137
+ future = await pre_validate_block(
2138
+ b.constants,
2139
+ AugmentedBlockchain(b),
2140
+ block,
2141
+ b.pool,
2142
+ None,
2143
+ ValidationState(ssi, diff, None),
2144
+ )
2145
+ pre_validation_result: PreValidationResult = await future
2146
+ # Ignore errors from pre-validation, we are testing block_body_validation
2147
+ repl_preval_results = replace(pre_validation_result, error=None, required_iters=uint64(1))
2148
+ fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
2149
+ res, error, state_change = await b.add_block(
2150
+ block, repl_preval_results, sub_slot_iters=ssi, fork_info=fork_info
2151
+ )
2152
+ assert res == AddBlockResult.NEW_PEAK
2153
+ assert error is None
2154
+ assert state_change is not None and state_change.fork_height == uint32(2)
2155
+
2156
+ @pytest.mark.anyio
2157
+ @pytest.mark.parametrize("with_garbage", [True, False])
2158
+ @pytest.mark.parametrize(
2159
+ "opcode,lock_value,expected",
2160
+ [
2161
+ # we don't allow any birth assertions, not
2162
+ # relative time locks on ephemeral coins. This test is only for
2163
+ # ephemeral coins, so these cases should always fail
2164
+ # MY BIRHT HEIGHT
2165
+ (co.ASSERT_MY_BIRTH_HEIGHT, -1, rbr.INVALID_BLOCK),
2166
+ (co.ASSERT_MY_BIRTH_HEIGHT, 0x100000000, rbr.INVALID_BLOCK),
2167
+ (co.ASSERT_MY_BIRTH_HEIGHT, 2, rbr.INVALID_BLOCK),
2168
+ (co.ASSERT_MY_BIRTH_HEIGHT, 3, rbr.INVALID_BLOCK),
2169
+ # MY BIRHT SECONDS
2170
+ (co.ASSERT_MY_BIRTH_SECONDS, -1, rbr.INVALID_BLOCK),
2171
+ (co.ASSERT_MY_BIRTH_SECONDS, 0x10000000000000000, rbr.INVALID_BLOCK),
2172
+ (co.ASSERT_MY_BIRTH_SECONDS, 10029, rbr.INVALID_BLOCK),
2173
+ (co.ASSERT_MY_BIRTH_SECONDS, 10030, rbr.INVALID_BLOCK),
2174
+ (co.ASSERT_MY_BIRTH_SECONDS, 10031, rbr.INVALID_BLOCK),
2175
+ # SECONDS RELATIVE
2176
+ # genesis timestamp is 10000 and each block is 10 seconds
2177
+ (co.ASSERT_SECONDS_RELATIVE, -2, rbr.INVALID_BLOCK),
2178
+ (co.ASSERT_SECONDS_RELATIVE, -1, rbr.INVALID_BLOCK),
2179
+ (co.ASSERT_SECONDS_RELATIVE, 0, rbr.INVALID_BLOCK),
2180
+ (co.ASSERT_SECONDS_RELATIVE, 1, rbr.INVALID_BLOCK),
2181
+ # BEFORE SECONDS RELATIVE
2182
+ # relative conditions are not allowed on ephemeral spends
2183
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, -2, rbr.INVALID_BLOCK),
2184
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, -1, rbr.INVALID_BLOCK),
2185
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 0, rbr.INVALID_BLOCK),
2186
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 10, rbr.INVALID_BLOCK),
2187
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 0x10000000000000000, rbr.INVALID_BLOCK),
2188
+ # HEIGHT RELATIVE
2189
+ (co.ASSERT_HEIGHT_RELATIVE, -2, rbr.INVALID_BLOCK),
2190
+ (co.ASSERT_HEIGHT_RELATIVE, -1, rbr.INVALID_BLOCK),
2191
+ (co.ASSERT_HEIGHT_RELATIVE, 0, rbr.INVALID_BLOCK),
2192
+ (co.ASSERT_HEIGHT_RELATIVE, 1, rbr.INVALID_BLOCK),
2193
+ # BEFORE HEIGHT RELATIVE
2194
+ # relative conditions are not allowed on ephemeral spends
2195
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -2, rbr.INVALID_BLOCK),
2196
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -1, rbr.INVALID_BLOCK),
2197
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 0, rbr.INVALID_BLOCK),
2198
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 1, rbr.INVALID_BLOCK),
2199
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 0x100000000, rbr.INVALID_BLOCK),
2200
+ # HEIGHT ABSOLUTE
2201
+ (co.ASSERT_HEIGHT_ABSOLUTE, 2, rbr.NEW_PEAK),
2202
+ (co.ASSERT_HEIGHT_ABSOLUTE, 3, rbr.INVALID_BLOCK),
2203
+ (co.ASSERT_HEIGHT_ABSOLUTE, 4, rbr.INVALID_BLOCK),
2204
+ # BEFORE HEIGHT ABSOLUTE
2205
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 2, rbr.INVALID_BLOCK),
2206
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 3, rbr.NEW_PEAK),
2207
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 4, rbr.NEW_PEAK),
2208
+ # SECONDS ABSOLUTE
2209
+ # genesis timestamp is 10000 and each block is 10 seconds
2210
+ (co.ASSERT_SECONDS_ABSOLUTE, 10020, rbr.NEW_PEAK), # <- previous tx-block
2211
+ (co.ASSERT_SECONDS_ABSOLUTE, 10021, rbr.INVALID_BLOCK),
2212
+ (co.ASSERT_SECONDS_ABSOLUTE, 10029, rbr.INVALID_BLOCK),
2213
+ (co.ASSERT_SECONDS_ABSOLUTE, 10030, rbr.INVALID_BLOCK), # <- current tx-block
2214
+ (co.ASSERT_SECONDS_ABSOLUTE, 10031, rbr.INVALID_BLOCK),
2215
+ (co.ASSERT_SECONDS_ABSOLUTE, 10032, rbr.INVALID_BLOCK),
2216
+ # BEFORE SECONDS ABSOLUTE
2217
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10020, rbr.INVALID_BLOCK),
2218
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10021, rbr.NEW_PEAK),
2219
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10030, rbr.NEW_PEAK),
2220
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10031, rbr.NEW_PEAK),
2221
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10032, rbr.NEW_PEAK),
2222
+ ],
2223
+ )
2224
+ async def test_ephemeral_timelock(
2225
+ self, opcode: ConditionOpcode, lock_value: int, expected: AddBlockResult, with_garbage: bool, bt: BlockTools
2226
+ ) -> None:
2227
+ async with make_empty_blockchain(bt.constants) as b:
2228
+ blocks = bt.get_consecutive_blocks(
2229
+ 3,
2230
+ guarantee_transaction_block=True,
2231
+ farmer_reward_puzzle_hash=bt.pool_ph,
2232
+ pool_reward_puzzle_hash=bt.pool_ph,
2233
+ genesis_timestamp=uint64(10_000),
2234
+ time_per_block=10,
2235
+ )
2236
+ await _validate_and_add_block(b, blocks[0])
2237
+ await _validate_and_add_block(b, blocks[1])
2238
+ await _validate_and_add_block(b, blocks[2])
2239
+
2240
+ wt: WalletTool = bt.get_pool_wallet_tool()
2241
+
2242
+ conditions = {
2243
+ opcode: [ConditionWithArgs(opcode, [int_to_bytes(lock_value)] + ([b"garbage"] if with_garbage else []))]
2244
+ }
2245
+
2246
+ tx1 = wt.generate_signed_transaction(
2247
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2248
+ )
2249
+ coin1: Coin = tx1.additions()[0]
2250
+ tx2 = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), coin1, condition_dic=conditions)
2251
+ assert coin1 in tx2.removals()
2252
+ coin2: Coin = tx2.additions()[0]
2253
+
2254
+ bundles = SpendBundle.aggregate([tx1, tx2])
2255
+ blocks = bt.get_consecutive_blocks(
2256
+ 1,
2257
+ block_list_input=blocks,
2258
+ guarantee_transaction_block=True,
2259
+ transaction_data=bundles,
2260
+ time_per_block=10,
2261
+ )
2262
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
2263
+ diff = b.constants.DIFFICULTY_STARTING
2264
+ block = blocks[-1]
2265
+ future = await pre_validate_block(
2266
+ b.constants,
2267
+ AugmentedBlockchain(b),
2268
+ block,
2269
+ b.pool,
2270
+ None,
2271
+ ValidationState(ssi, diff, None),
2272
+ )
2273
+ pre_validation_result: PreValidationResult = await future
2274
+ fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
2275
+ assert (await b.add_block(block, pre_validation_result, sub_slot_iters=ssi, fork_info=fork_info))[
2276
+ 0
2277
+ ] == expected
2278
+
2279
+ if expected == AddBlockResult.NEW_PEAK:
2280
+ # ensure coin1 was in fact spent
2281
+ c = await b.coin_store.get_coin_record(coin1.name())
2282
+ assert c is not None and c.spent
2283
+ # ensure coin2 was NOT spent
2284
+ c = await b.coin_store.get_coin_record(coin2.name())
2285
+ assert c is not None and not c.spent
2286
+
2287
+ @pytest.mark.anyio
2288
+ async def test_not_tx_block_but_has_data(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2289
+ # 1
2290
+ blocks = bt.get_consecutive_blocks(1)
2291
+ while blocks[-1].foliage_transaction_block is not None:
2292
+ await _validate_and_add_block(empty_blockchain, blocks[-1])
2293
+ blocks = bt.get_consecutive_blocks(1, block_list_input=blocks)
2294
+ original_block: FullBlock = blocks[-1]
2295
+
2296
+ block = recursive_replace(original_block, "transactions_generator", SerializedProgram.to(None))
2297
+ await _validate_and_add_block(
2298
+ empty_blockchain, block, expected_error=Err.NOT_BLOCK_BUT_HAS_DATA, skip_prevalidation=True
2299
+ )
2300
+ h = std_hash(b"")
2301
+ i = uint64(1)
2302
+ block = recursive_replace(
2303
+ original_block,
2304
+ "transactions_info",
2305
+ TransactionsInfo(h, h, G2Element(), uint64(1), uint64(1), []),
2306
+ )
2307
+ await _validate_and_add_block(
2308
+ empty_blockchain, block, expected_error=Err.NOT_BLOCK_BUT_HAS_DATA, skip_prevalidation=True
2309
+ )
2310
+
2311
+ block = recursive_replace(original_block, "transactions_generator_ref_list", [i])
2312
+ await _validate_and_add_block(
2313
+ empty_blockchain, block, expected_error=Err.NOT_BLOCK_BUT_HAS_DATA, skip_prevalidation=True
2314
+ )
2315
+
2316
+ @pytest.mark.anyio
2317
+ async def test_tx_block_missing_data(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2318
+ # 2
2319
+ b = empty_blockchain
2320
+ blocks = bt.get_consecutive_blocks(2, guarantee_transaction_block=True)
2321
+ await _validate_and_add_block(b, blocks[0])
2322
+ block = recursive_replace(
2323
+ blocks[-1],
2324
+ "foliage_transaction_block",
2325
+ None,
2326
+ )
2327
+ await _validate_and_add_block_multi_error(
2328
+ b, block, [Err.IS_TRANSACTION_BLOCK_BUT_NO_DATA, Err.INVALID_FOLIAGE_BLOCK_PRESENCE]
2329
+ )
2330
+
2331
+ block = recursive_replace(
2332
+ blocks[-1],
2333
+ "transactions_info",
2334
+ None,
2335
+ )
2336
+ with pytest.raises(AssertionError):
2337
+ await _validate_and_add_block_multi_error(
2338
+ b, block, [Err.IS_TRANSACTION_BLOCK_BUT_NO_DATA, Err.INVALID_FOLIAGE_BLOCK_PRESENCE]
2339
+ )
2340
+
2341
+ @pytest.mark.anyio
2342
+ async def test_invalid_transactions_info_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2343
+ # 3
2344
+ b = empty_blockchain
2345
+ blocks = bt.get_consecutive_blocks(2, guarantee_transaction_block=True)
2346
+ await _validate_and_add_block(b, blocks[0])
2347
+ h = std_hash(b"")
2348
+ block = recursive_replace(
2349
+ blocks[-1],
2350
+ "foliage_transaction_block.transactions_info_hash",
2351
+ h,
2352
+ )
2353
+ block = recursive_replace(
2354
+ block, "foliage.foliage_transaction_block_hash", std_hash(block.foliage_transaction_block)
2355
+ )
2356
+ new_m = block.foliage.foliage_transaction_block_hash
2357
+ assert new_m is not None
2358
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
2359
+ block = recursive_replace(block, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2360
+
2361
+ await _validate_and_add_block(b, block, expected_error=Err.INVALID_TRANSACTIONS_INFO_HASH)
2362
+
2363
+ @pytest.mark.anyio
2364
+ async def test_invalid_transactions_block_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2365
+ # 4
2366
+ b = empty_blockchain
2367
+ blocks = bt.get_consecutive_blocks(2, guarantee_transaction_block=True)
2368
+ await _validate_and_add_block(b, blocks[0])
2369
+ h = std_hash(b"")
2370
+ block = recursive_replace(blocks[-1], "foliage.foliage_transaction_block_hash", h)
2371
+ new_m = block.foliage.foliage_transaction_block_hash
2372
+ assert new_m is not None
2373
+ new_fsb_sig = bt.get_plot_signature(new_m, blocks[-1].reward_chain_block.proof_of_space.plot_public_key)
2374
+ block = recursive_replace(block, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2375
+
2376
+ await _validate_and_add_block(b, block, expected_error=Err.INVALID_FOLIAGE_BLOCK_HASH)
2377
+
2378
+ @pytest.mark.anyio
2379
+ async def test_invalid_reward_claims(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2380
+ # 5
2381
+ b = empty_blockchain
2382
+ blocks = bt.get_consecutive_blocks(2, guarantee_transaction_block=True)
2383
+ await _validate_and_add_block(b, blocks[0])
2384
+ block: FullBlock = blocks[-1]
2385
+
2386
+ # Too few
2387
+ assert block.transactions_info is not None
2388
+ too_few_reward_claims = block.transactions_info.reward_claims_incorporated[:-1]
2389
+ block_2: FullBlock = recursive_replace(
2390
+ block, "transactions_info.reward_claims_incorporated", too_few_reward_claims
2391
+ )
2392
+ assert block_2.transactions_info is not None
2393
+ block_2 = recursive_replace(
2394
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2395
+ )
2396
+
2397
+ assert block_2.foliage_transaction_block is not None
2398
+ block_2 = recursive_replace(
2399
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2400
+ )
2401
+ new_m = block_2.foliage.foliage_transaction_block_hash
2402
+ assert new_m is not None
2403
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2404
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2405
+
2406
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_REWARD_COINS, skip_prevalidation=True)
2407
+
2408
+ # Too many
2409
+ h = std_hash(b"")
2410
+ too_many_reward_claims = [
2411
+ *block.transactions_info.reward_claims_incorporated,
2412
+ Coin(h, h, too_few_reward_claims[0].amount),
2413
+ ]
2414
+ block_2 = recursive_replace(block, "transactions_info.reward_claims_incorporated", too_many_reward_claims)
2415
+ assert block_2.transactions_info is not None
2416
+ block_2 = recursive_replace(
2417
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2418
+ )
2419
+ assert block_2.foliage_transaction_block is not None
2420
+ block_2 = recursive_replace(
2421
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2422
+ )
2423
+ new_m = block_2.foliage.foliage_transaction_block_hash
2424
+ assert new_m is not None
2425
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2426
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2427
+
2428
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_REWARD_COINS, skip_prevalidation=True)
2429
+
2430
+ # Duplicates
2431
+ duplicate_reward_claims = [
2432
+ *block.transactions_info.reward_claims_incorporated,
2433
+ block.transactions_info.reward_claims_incorporated[-1],
2434
+ ]
2435
+ block_2 = recursive_replace(block, "transactions_info.reward_claims_incorporated", duplicate_reward_claims)
2436
+ assert block_2.transactions_info is not None
2437
+ block_2 = recursive_replace(
2438
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2439
+ )
2440
+ assert block_2.foliage_transaction_block is not None
2441
+ block_2 = recursive_replace(
2442
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2443
+ )
2444
+ new_m = block_2.foliage.foliage_transaction_block_hash
2445
+ assert new_m is not None
2446
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2447
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2448
+
2449
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_REWARD_COINS, skip_prevalidation=True)
2450
+
2451
+ @pytest.mark.anyio
2452
+ async def test_invalid_transactions_generator_hash(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2453
+ # 7
2454
+ b = empty_blockchain
2455
+ blocks = bt.get_consecutive_blocks(2, guarantee_transaction_block=True)
2456
+ await _validate_and_add_block(b, blocks[0])
2457
+
2458
+ # No tx should have all zeroes
2459
+ block: FullBlock = blocks[-1]
2460
+ block_2 = recursive_replace(block, "transactions_info.generator_root", bytes([1] * 32))
2461
+ block_2 = recursive_replace(
2462
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2463
+ )
2464
+ block_2 = recursive_replace(
2465
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2466
+ )
2467
+ new_m = block_2.foliage.foliage_transaction_block_hash
2468
+ assert new_m is not None
2469
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2470
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2471
+
2472
+ await _validate_and_add_block(
2473
+ b, block_2, expected_error=Err.INVALID_TRANSACTIONS_GENERATOR_HASH, skip_prevalidation=True
2474
+ )
2475
+
2476
+ await _validate_and_add_block(b, blocks[1])
2477
+ blocks = bt.get_consecutive_blocks(
2478
+ 2,
2479
+ block_list_input=blocks,
2480
+ guarantee_transaction_block=True,
2481
+ farmer_reward_puzzle_hash=bt.pool_ph,
2482
+ pool_reward_puzzle_hash=bt.pool_ph,
2483
+ )
2484
+ await _validate_and_add_block(b, blocks[2])
2485
+ await _validate_and_add_block(b, blocks[3])
2486
+
2487
+ wt: WalletTool = bt.get_pool_wallet_tool()
2488
+ tx = wt.generate_signed_transaction(
2489
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2490
+ )
2491
+ blocks = bt.get_consecutive_blocks(
2492
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2493
+ )
2494
+
2495
+ # Non empty generator hash must be correct
2496
+ block = blocks[-1]
2497
+ block_2 = recursive_replace(block, "transactions_info.generator_root", bytes([0] * 32))
2498
+ block_2 = recursive_replace(
2499
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2500
+ )
2501
+ block_2 = recursive_replace(
2502
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2503
+ )
2504
+ new_m = block_2.foliage.foliage_transaction_block_hash
2505
+ assert new_m is not None
2506
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2507
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2508
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_TRANSACTIONS_GENERATOR_HASH)
2509
+
2510
+ @pytest.mark.anyio
2511
+ async def test_invalid_transactions_ref_list(
2512
+ self, empty_blockchain: Blockchain, bt: BlockTools, consensus_mode: ConsensusMode
2513
+ ) -> None:
2514
+ # No generator should have [1]s for the root
2515
+ b = empty_blockchain
2516
+ blocks = bt.get_consecutive_blocks(
2517
+ 3,
2518
+ guarantee_transaction_block=True,
2519
+ farmer_reward_puzzle_hash=bt.pool_ph,
2520
+ pool_reward_puzzle_hash=bt.pool_ph,
2521
+ )
2522
+ await _validate_and_add_block(b, blocks[0])
2523
+ await _validate_and_add_block(b, blocks[1])
2524
+
2525
+ block: FullBlock = blocks[-1]
2526
+ block_2 = recursive_replace(block, "transactions_info.generator_refs_root", bytes([0] * 32))
2527
+ block_2 = recursive_replace(
2528
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2529
+ )
2530
+ block_2 = recursive_replace(
2531
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2532
+ )
2533
+ new_m = block_2.foliage.foliage_transaction_block_hash
2534
+ assert new_m is not None
2535
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2536
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2537
+
2538
+ await _validate_and_add_block(
2539
+ b, block_2, expected_error=Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT, skip_prevalidation=True
2540
+ )
2541
+
2542
+ # No generator should have no refs list
2543
+ block_2 = recursive_replace(block, "transactions_generator_ref_list", [uint32(0)])
2544
+
2545
+ await _validate_and_add_block(
2546
+ b, block_2, expected_error=Err.INVALID_TRANSACTIONS_GENERATOR_REFS_ROOT, skip_prevalidation=True
2547
+ )
2548
+
2549
+ # Hash should be correct when there is a ref list
2550
+ await _validate_and_add_block(b, blocks[-1])
2551
+ wt: WalletTool = bt.get_pool_wallet_tool()
2552
+ tx = wt.generate_signed_transaction(
2553
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2554
+ )
2555
+ blocks = bt.get_consecutive_blocks(5, block_list_input=blocks, guarantee_transaction_block=False)
2556
+ for block in blocks[-5:]:
2557
+ await _validate_and_add_block(b, block)
2558
+
2559
+ blocks = bt.get_consecutive_blocks(
2560
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2561
+ )
2562
+ await _validate_and_add_block(b, blocks[-1])
2563
+ assert blocks[-1].transactions_generator is not None
2564
+
2565
+ blocks = bt.get_consecutive_blocks(
2566
+ 1,
2567
+ block_list_input=blocks,
2568
+ guarantee_transaction_block=True,
2569
+ transaction_data=tx,
2570
+ block_refs=[blocks[-1].height],
2571
+ )
2572
+ block = blocks[-1]
2573
+ # once the hard fork activated, we no longer use this form of block
2574
+ # compression anymore
2575
+ assert len(block.transactions_generator_ref_list) == 0
2576
+
2577
+ @pytest.mark.anyio
2578
+ async def test_cost_exceeds_max(
2579
+ self, empty_blockchain: Blockchain, softfork_height: uint32, bt: BlockTools
2580
+ ) -> None:
2581
+ # 7
2582
+ b = empty_blockchain
2583
+ blocks = bt.get_consecutive_blocks(
2584
+ 3,
2585
+ guarantee_transaction_block=True,
2586
+ farmer_reward_puzzle_hash=bt.pool_ph,
2587
+ pool_reward_puzzle_hash=bt.pool_ph,
2588
+ )
2589
+ await _validate_and_add_block(b, blocks[0])
2590
+ await _validate_and_add_block(b, blocks[1])
2591
+ await _validate_and_add_block(b, blocks[2])
2592
+
2593
+ wt: WalletTool = bt.get_pool_wallet_tool()
2594
+
2595
+ condition_dict: dict[ConditionOpcode, list[ConditionWithArgs]] = {ConditionOpcode.CREATE_COIN: []}
2596
+ for i in range(7_000):
2597
+ output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt.pool_ph, int_to_bytes(i)])
2598
+ condition_dict[ConditionOpcode.CREATE_COIN].append(output)
2599
+
2600
+ tx = wt.generate_signed_transaction(
2601
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0], condition_dic=condition_dict
2602
+ )
2603
+
2604
+ blocks = bt.get_consecutive_blocks(
2605
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2606
+ )
2607
+
2608
+ assert blocks[-1].transactions_generator is not None
2609
+ assert blocks[-1].transactions_info is not None
2610
+ block_generator = BlockGenerator(blocks[-1].transactions_generator, [])
2611
+ npc_result = get_name_puzzle_conditions(
2612
+ block_generator,
2613
+ b.constants.MAX_BLOCK_COST_CLVM * 1000,
2614
+ mempool_mode=False,
2615
+ height=softfork_height,
2616
+ constants=bt.constants,
2617
+ )
2618
+ assert npc_result.conds is not None
2619
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
2620
+ diff = b.constants.DIFFICULTY_STARTING
2621
+ block = blocks[-1]
2622
+ fork_info = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
2623
+ err = (
2624
+ await b.add_block(
2625
+ blocks[-1],
2626
+ PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
2627
+ sub_slot_iters=ssi,
2628
+ fork_info=fork_info,
2629
+ )
2630
+ )[1]
2631
+ assert err == Err.BLOCK_COST_EXCEEDS_MAX
2632
+ future = await pre_validate_block(
2633
+ b.constants,
2634
+ AugmentedBlockchain(b),
2635
+ blocks[-1],
2636
+ b.pool,
2637
+ None,
2638
+ ValidationState(ssi, diff, None),
2639
+ )
2640
+ result: PreValidationResult = await future
2641
+ assert Err(result.error) == Err.BLOCK_COST_EXCEEDS_MAX
2642
+
2643
+ @pytest.mark.anyio
2644
+ async def test_clvm_must_not_fail(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2645
+ # 8
2646
+ pass
2647
+
2648
+ @pytest.mark.anyio
2649
+ async def test_invalid_cost_in_block(
2650
+ self, empty_blockchain: Blockchain, softfork_height: uint32, bt: BlockTools
2651
+ ) -> None:
2652
+ # 9
2653
+ b = empty_blockchain
2654
+ blocks = bt.get_consecutive_blocks(
2655
+ 3,
2656
+ guarantee_transaction_block=True,
2657
+ farmer_reward_puzzle_hash=bt.pool_ph,
2658
+ pool_reward_puzzle_hash=bt.pool_ph,
2659
+ )
2660
+ await _validate_and_add_block(b, blocks[0])
2661
+ await _validate_and_add_block(b, blocks[1])
2662
+ await _validate_and_add_block(b, blocks[2])
2663
+
2664
+ wt: WalletTool = bt.get_pool_wallet_tool()
2665
+
2666
+ tx = wt.generate_signed_transaction(
2667
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2668
+ )
2669
+
2670
+ blocks = bt.get_consecutive_blocks(
2671
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2672
+ )
2673
+ block: FullBlock = blocks[-1]
2674
+
2675
+ # zero
2676
+ block_2: FullBlock = recursive_replace(block, "transactions_info.cost", uint64(0))
2677
+ assert block_2.transactions_info is not None
2678
+ block_2 = recursive_replace(
2679
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2680
+ )
2681
+ assert block_2.foliage_transaction_block is not None
2682
+ block_2 = recursive_replace(
2683
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2684
+ )
2685
+ new_m = block_2.foliage.foliage_transaction_block_hash
2686
+ assert new_m is not None
2687
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2688
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2689
+ assert block_2.transactions_generator is not None
2690
+ block_generator = BlockGenerator(block_2.transactions_generator, [])
2691
+ assert block.transactions_info is not None
2692
+ npc_result = get_name_puzzle_conditions(
2693
+ block_generator,
2694
+ min(b.constants.MAX_BLOCK_COST_CLVM * 1000, block.transactions_info.cost),
2695
+ mempool_mode=False,
2696
+ height=softfork_height,
2697
+ constants=bt.constants,
2698
+ )
2699
+ assert npc_result.conds is not None
2700
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
2701
+ fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
2702
+ _, err, _ = await b.add_block(
2703
+ block_2,
2704
+ PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
2705
+ sub_slot_iters=ssi,
2706
+ fork_info=fork_info,
2707
+ )
2708
+ assert err == Err.INVALID_BLOCK_COST
2709
+
2710
+ # too low
2711
+ block_2 = recursive_replace(block, "transactions_info.cost", uint64(1))
2712
+ assert block_2.transactions_info is not None
2713
+ block_2 = recursive_replace(
2714
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2715
+ )
2716
+ assert block_2.foliage_transaction_block is not None
2717
+ block_2 = recursive_replace(
2718
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2719
+ )
2720
+ new_m = block_2.foliage.foliage_transaction_block_hash
2721
+ assert new_m is not None
2722
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2723
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2724
+ assert block_2.transactions_generator is not None
2725
+ block_generator = BlockGenerator(block_2.transactions_generator, [])
2726
+ assert block.transactions_info is not None
2727
+ npc_result = get_name_puzzle_conditions(
2728
+ block_generator,
2729
+ min(b.constants.MAX_BLOCK_COST_CLVM * 1000, block.transactions_info.cost),
2730
+ mempool_mode=False,
2731
+ height=softfork_height,
2732
+ constants=bt.constants,
2733
+ )
2734
+ assert npc_result.conds is not None
2735
+ fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
2736
+ _, err, _ = await b.add_block(
2737
+ block_2,
2738
+ PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
2739
+ sub_slot_iters=ssi,
2740
+ fork_info=fork_info,
2741
+ )
2742
+ assert err == Err.INVALID_BLOCK_COST
2743
+
2744
+ # too high
2745
+ block_2 = recursive_replace(block, "transactions_info.cost", uint64(1000000))
2746
+ assert block_2.transactions_info is not None
2747
+ block_2 = recursive_replace(
2748
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
2749
+ )
2750
+ assert block_2.foliage_transaction_block is not None
2751
+ block_2 = recursive_replace(
2752
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2753
+ )
2754
+ new_m = block_2.foliage.foliage_transaction_block_hash
2755
+ assert new_m is not None
2756
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2757
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2758
+
2759
+ assert block_2.transactions_generator is not None
2760
+ block_generator = BlockGenerator(block_2.transactions_generator, [])
2761
+ max_cost = (
2762
+ min(b.constants.MAX_BLOCK_COST_CLVM * 1000, block.transactions_info.cost)
2763
+ if block.transactions_info is not None
2764
+ else b.constants.MAX_BLOCK_COST_CLVM * 1000
2765
+ )
2766
+ npc_result = get_name_puzzle_conditions(
2767
+ block_generator,
2768
+ max_cost,
2769
+ mempool_mode=False,
2770
+ height=softfork_height,
2771
+ constants=bt.constants,
2772
+ )
2773
+ assert npc_result.conds is not None
2774
+ fork_info = ForkInfo(block_2.height - 1, block_2.height - 1, block_2.prev_header_hash)
2775
+ _result, err, _ = await b.add_block(
2776
+ block_2,
2777
+ PreValidationResult(None, uint64(1), npc_result.conds.replace(validated_signature=True), uint32(0)),
2778
+ sub_slot_iters=ssi,
2779
+ fork_info=fork_info,
2780
+ )
2781
+ assert err == Err.INVALID_BLOCK_COST
2782
+
2783
+ # when the CLVM program exceeds cost during execution, it will fail with
2784
+ # a general runtime error. The previous test tests this.
2785
+
2786
+ @pytest.mark.anyio
2787
+ async def test_max_coin_amount(self, db_version: int, bt: BlockTools) -> None:
2788
+ # 10
2789
+ # TODO: fix, this is not reaching validation. Because we can't create a block with such amounts due to uint64
2790
+ # limit in Coin
2791
+ pass
2792
+ #
2793
+ # with TempKeyring() as keychain:
2794
+ # new_test_constants = bt.constants.replace(
2795
+ # GENESIS_PRE_FARM_POOL_PUZZLE_HASH=bt.pool_ph,
2796
+ # GENESIS_PRE_FARM_FARMER_PUZZLE_HASH=bt.pool_ph,
2797
+ # )
2798
+ # b, db_wrapper = await create_blockchain(new_test_constants, db_version)
2799
+ # bt_2 = await create_block_tools_async(constants=new_test_constants, keychain=keychain)
2800
+ # bt_2.constants = bt_2.constants.replace(
2801
+ # GENESIS_PRE_FARM_POOL_PUZZLE_HASH=bt.pool_ph,
2802
+ # GENESIS_PRE_FARM_FARMER_PUZZLE_HASH=bt.pool_ph,
2803
+ # )
2804
+ # blocks = bt_2.get_consecutive_blocks(
2805
+ # 3,
2806
+ # guarantee_transaction_block=True,
2807
+ # farmer_reward_puzzle_hash=bt.pool_ph,
2808
+ # pool_reward_puzzle_hash=bt.pool_ph,
2809
+ # )
2810
+ # assert (await b.add_block(blocks[0]))[0] == AddBlockResult.NEW_PEAK
2811
+ # assert (await b.add_block(blocks[1]))[0] == AddBlockResult.NEW_PEAK
2812
+ # assert (await b.add_block(blocks[2]))[0] == AddBlockResult.NEW_PEAK
2813
+
2814
+ # wt: WalletTool = bt_2.get_pool_wallet_tool()
2815
+
2816
+ # condition_dict: dict[ConditionOpcode, list[ConditionWithArgs]] = {ConditionOpcode.CREATE_COIN: []}
2817
+ # output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt_2.pool_ph, int_to_bytes(2 ** 64)])
2818
+ # condition_dict[ConditionOpcode.CREATE_COIN].append(output)
2819
+
2820
+ # tx = wt.generate_signed_transaction_multiple_coins(
2821
+ # uint64(10),
2822
+ # wt.get_new_puzzlehash(),
2823
+ # blocks[1].get_included_reward_coins(),
2824
+ # condition_dic=condition_dict,
2825
+ # )
2826
+ # with pytest.raises(Exception):
2827
+ # blocks = bt_2.get_consecutive_blocks(
2828
+ # 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2829
+ # )
2830
+ # await db_wrapper.close()
2831
+ # b.shut_down()
2832
+
2833
+ @pytest.mark.anyio
2834
+ async def test_invalid_merkle_roots(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2835
+ # 11
2836
+ blocks = bt.get_consecutive_blocks(
2837
+ 3,
2838
+ guarantee_transaction_block=True,
2839
+ farmer_reward_puzzle_hash=bt.pool_ph,
2840
+ pool_reward_puzzle_hash=bt.pool_ph,
2841
+ )
2842
+ await _validate_and_add_block(empty_blockchain, blocks[0])
2843
+ await _validate_and_add_block(empty_blockchain, blocks[1])
2844
+ await _validate_and_add_block(empty_blockchain, blocks[2])
2845
+
2846
+ wt: WalletTool = bt.get_pool_wallet_tool()
2847
+
2848
+ tx = wt.generate_signed_transaction(
2849
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2850
+ )
2851
+
2852
+ blocks = bt.get_consecutive_blocks(
2853
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2854
+ )
2855
+ block: FullBlock = blocks[-1]
2856
+
2857
+ merkle_set = MerkleSet([])
2858
+ # additions
2859
+ block_2 = recursive_replace(block, "foliage_transaction_block.additions_root", merkle_set.get_root())
2860
+ block_2 = recursive_replace(
2861
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2862
+ )
2863
+ new_m = block_2.foliage.foliage_transaction_block_hash
2864
+ assert new_m is not None
2865
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2866
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2867
+
2868
+ await _validate_and_add_block(empty_blockchain, block_2, expected_error=Err.BAD_ADDITION_ROOT)
2869
+
2870
+ # removals
2871
+ merkle_set = MerkleSet([std_hash(b"1")])
2872
+ block_2 = recursive_replace(block, "foliage_transaction_block.removals_root", merkle_set.get_root())
2873
+ block_2 = recursive_replace(
2874
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2875
+ )
2876
+ new_m = block_2.foliage.foliage_transaction_block_hash
2877
+ assert new_m is not None
2878
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2879
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2880
+
2881
+ await _validate_and_add_block(empty_blockchain, block_2, expected_error=Err.BAD_REMOVAL_ROOT)
2882
+
2883
+ @pytest.mark.anyio
2884
+ async def test_invalid_filter(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2885
+ # 12
2886
+ b = empty_blockchain
2887
+ blocks = bt.get_consecutive_blocks(
2888
+ 3,
2889
+ guarantee_transaction_block=True,
2890
+ farmer_reward_puzzle_hash=bt.pool_ph,
2891
+ pool_reward_puzzle_hash=bt.pool_ph,
2892
+ )
2893
+ await _validate_and_add_block(b, blocks[0])
2894
+ await _validate_and_add_block(b, blocks[1])
2895
+ await _validate_and_add_block(b, blocks[2])
2896
+
2897
+ wt: WalletTool = bt.get_pool_wallet_tool()
2898
+
2899
+ tx = wt.generate_signed_transaction(
2900
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2901
+ )
2902
+
2903
+ blocks = bt.get_consecutive_blocks(
2904
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2905
+ )
2906
+ block: FullBlock = blocks[-1]
2907
+ block_2 = recursive_replace(block, "foliage_transaction_block.filter_hash", std_hash(b"3"))
2908
+ block_2 = recursive_replace(
2909
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
2910
+ )
2911
+ new_m = block_2.foliage.foliage_transaction_block_hash
2912
+ assert new_m is not None
2913
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
2914
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
2915
+
2916
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_TRANSACTIONS_FILTER_HASH)
2917
+
2918
+ @pytest.mark.anyio
2919
+ async def test_duplicate_outputs(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2920
+ # 13
2921
+ b = empty_blockchain
2922
+ blocks = bt.get_consecutive_blocks(
2923
+ 3,
2924
+ guarantee_transaction_block=True,
2925
+ farmer_reward_puzzle_hash=bt.pool_ph,
2926
+ pool_reward_puzzle_hash=bt.pool_ph,
2927
+ )
2928
+ await _validate_and_add_block(b, blocks[0])
2929
+ await _validate_and_add_block(b, blocks[1])
2930
+ await _validate_and_add_block(b, blocks[2])
2931
+
2932
+ wt: WalletTool = bt.get_pool_wallet_tool()
2933
+
2934
+ condition_dict: dict[ConditionOpcode, list[ConditionWithArgs]] = {ConditionOpcode.CREATE_COIN: []}
2935
+ for _ in range(2):
2936
+ output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt.pool_ph, int_to_bytes(1)])
2937
+ condition_dict[ConditionOpcode.CREATE_COIN].append(output)
2938
+
2939
+ tx = wt.generate_signed_transaction(
2940
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0], condition_dic=condition_dict
2941
+ )
2942
+
2943
+ blocks = bt.get_consecutive_blocks(
2944
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2945
+ )
2946
+ await _validate_and_add_block(b, blocks[-1], expected_error=Err.DUPLICATE_OUTPUT)
2947
+
2948
+ @pytest.mark.anyio
2949
+ async def test_duplicate_removals(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2950
+ # 14
2951
+ b = empty_blockchain
2952
+ blocks = bt.get_consecutive_blocks(
2953
+ 3,
2954
+ guarantee_transaction_block=True,
2955
+ farmer_reward_puzzle_hash=bt.pool_ph,
2956
+ pool_reward_puzzle_hash=bt.pool_ph,
2957
+ )
2958
+ await _validate_and_add_block(b, blocks[0])
2959
+ await _validate_and_add_block(b, blocks[1])
2960
+ await _validate_and_add_block(b, blocks[2])
2961
+
2962
+ wt: WalletTool = bt.get_pool_wallet_tool()
2963
+
2964
+ tx = wt.generate_signed_transaction(
2965
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2966
+ )
2967
+ tx_2 = wt.generate_signed_transaction(
2968
+ uint64(11), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2969
+ )
2970
+ agg = SpendBundle.aggregate([tx, tx_2])
2971
+
2972
+ blocks = bt.get_consecutive_blocks(
2973
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=agg
2974
+ )
2975
+ await _validate_and_add_block(b, blocks[-1], expected_error=Err.DOUBLE_SPEND)
2976
+
2977
+ @pytest.mark.anyio
2978
+ async def test_double_spent_in_coin_store(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
2979
+ # 15
2980
+ b = empty_blockchain
2981
+ blocks = bt.get_consecutive_blocks(
2982
+ 3,
2983
+ guarantee_transaction_block=True,
2984
+ farmer_reward_puzzle_hash=bt.pool_ph,
2985
+ pool_reward_puzzle_hash=bt.pool_ph,
2986
+ )
2987
+ await _validate_and_add_block(b, blocks[0])
2988
+ await _validate_and_add_block(b, blocks[1])
2989
+ await _validate_and_add_block(b, blocks[2])
2990
+
2991
+ wt: WalletTool = bt.get_pool_wallet_tool()
2992
+
2993
+ tx = wt.generate_signed_transaction(
2994
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
2995
+ )
2996
+
2997
+ blocks = bt.get_consecutive_blocks(
2998
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
2999
+ )
3000
+ await _validate_and_add_block(b, blocks[-1])
3001
+
3002
+ tx_2 = wt.generate_signed_transaction(
3003
+ uint64(10), wt.get_new_puzzlehash(), blocks[-2].get_included_reward_coins()[0]
3004
+ )
3005
+ blocks = bt.get_consecutive_blocks(
3006
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx_2
3007
+ )
3008
+
3009
+ await _validate_and_add_block(b, blocks[-1], expected_error=Err.DOUBLE_SPEND)
3010
+
3011
+ @pytest.mark.anyio
3012
+ async def test_double_spent_in_reorg(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3013
+ # 15
3014
+ b = empty_blockchain
3015
+ blocks = bt.get_consecutive_blocks(
3016
+ 3,
3017
+ guarantee_transaction_block=True,
3018
+ farmer_reward_puzzle_hash=bt.pool_ph,
3019
+ pool_reward_puzzle_hash=bt.pool_ph,
3020
+ )
3021
+ await _validate_and_add_block(b, blocks[0])
3022
+ await _validate_and_add_block(b, blocks[1])
3023
+ await _validate_and_add_block(b, blocks[2])
3024
+
3025
+ wt: WalletTool = bt.get_pool_wallet_tool()
3026
+
3027
+ tx = wt.generate_signed_transaction(
3028
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
3029
+ )
3030
+ blocks = bt.get_consecutive_blocks(
3031
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
3032
+ )
3033
+ await _validate_and_add_block(b, blocks[-1])
3034
+
3035
+ new_coin: Coin = tx.additions()[0]
3036
+ tx_2 = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), new_coin)
3037
+ # This is fine because coin exists
3038
+ blocks = bt.get_consecutive_blocks(
3039
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx_2
3040
+ )
3041
+ await _validate_and_add_block(b, blocks[-1])
3042
+ blocks = bt.get_consecutive_blocks(5, block_list_input=blocks, guarantee_transaction_block=True)
3043
+ for block in blocks[-5:]:
3044
+ await _validate_and_add_block(b, block)
3045
+
3046
+ blocks_reorg = bt.get_consecutive_blocks(2, block_list_input=blocks[:-7], guarantee_transaction_block=True)
3047
+ fork_info = ForkInfo(blocks[-8].height, blocks[-8].height, blocks[-8].header_hash)
3048
+ await _validate_and_add_block(
3049
+ b, blocks_reorg[-2], expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3050
+ )
3051
+ await _validate_and_add_block(
3052
+ b, blocks_reorg[-1], expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3053
+ )
3054
+
3055
+ # Coin does not exist in reorg
3056
+ blocks_reorg = bt.get_consecutive_blocks(
3057
+ 1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_2
3058
+ )
3059
+ peak = b.get_peak()
3060
+ assert peak is not None
3061
+ await _validate_and_add_block(b, blocks_reorg[-1], expected_error=Err.UNKNOWN_UNSPENT, fork_info=fork_info)
3062
+
3063
+ # Finally add the block to the fork (spending both in same bundle, this is ephemeral)
3064
+ agg = SpendBundle.aggregate([tx, tx_2])
3065
+ blocks_reorg = bt.get_consecutive_blocks(
3066
+ 1, block_list_input=blocks_reorg[:-1], guarantee_transaction_block=True, transaction_data=agg
3067
+ )
3068
+
3069
+ peak = b.get_peak()
3070
+ assert peak is not None
3071
+ await _validate_and_add_block(
3072
+ b, blocks_reorg[-1], expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3073
+ )
3074
+
3075
+ blocks_reorg = bt.get_consecutive_blocks(
3076
+ 1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_2
3077
+ )
3078
+ peak = b.get_peak()
3079
+ assert peak is not None
3080
+ await _validate_and_add_block(b, blocks_reorg[-1], expected_error=Err.DOUBLE_SPEND_IN_FORK, fork_info=fork_info)
3081
+
3082
+ rewards_ph = wt.get_new_puzzlehash()
3083
+ blocks_reorg = bt.get_consecutive_blocks(
3084
+ 10,
3085
+ block_list_input=blocks_reorg[:-1],
3086
+ guarantee_transaction_block=True,
3087
+ farmer_reward_puzzle_hash=rewards_ph,
3088
+ )
3089
+
3090
+ peak = b.get_peak()
3091
+ assert peak is not None
3092
+ for block in blocks_reorg[-10:]:
3093
+ await _validate_and_add_block_multi_result(
3094
+ b, block, expected_result=[AddBlockResult.ADDED_AS_ORPHAN, AddBlockResult.NEW_PEAK], fork_info=fork_info
3095
+ )
3096
+
3097
+ # ephemeral coin is spent
3098
+ first_coin = await b.coin_store.get_coin_record(new_coin.name())
3099
+ assert first_coin is not None and first_coin.spent
3100
+ second_coin = await b.coin_store.get_coin_record(tx_2.additions()[0].name())
3101
+ assert second_coin is not None and not second_coin.spent
3102
+
3103
+ farmer_coin = create_farmer_coin(
3104
+ blocks_reorg[-1].height,
3105
+ rewards_ph,
3106
+ calculate_base_farmer_reward(blocks_reorg[-1].height),
3107
+ bt.constants.GENESIS_CHALLENGE,
3108
+ )
3109
+ tx_3 = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), farmer_coin)
3110
+
3111
+ blocks_reorg = bt.get_consecutive_blocks(
3112
+ 1, block_list_input=blocks_reorg, guarantee_transaction_block=True, transaction_data=tx_3
3113
+ )
3114
+ await _validate_and_add_block(b, blocks_reorg[-1])
3115
+
3116
+ farmer_coin_record = await b.coin_store.get_coin_record(farmer_coin.name())
3117
+ assert farmer_coin_record is not None and farmer_coin_record.spent
3118
+
3119
+ @pytest.mark.anyio
3120
+ async def test_minting_coin(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3121
+ # 16 Minting coin check
3122
+ b = empty_blockchain
3123
+ blocks = bt.get_consecutive_blocks(
3124
+ 3,
3125
+ guarantee_transaction_block=True,
3126
+ farmer_reward_puzzle_hash=bt.pool_ph,
3127
+ pool_reward_puzzle_hash=bt.pool_ph,
3128
+ )
3129
+ await _validate_and_add_block(b, blocks[0])
3130
+ await _validate_and_add_block(b, blocks[1])
3131
+ await _validate_and_add_block(b, blocks[2])
3132
+
3133
+ wt: WalletTool = bt.get_pool_wallet_tool()
3134
+
3135
+ spend = blocks[-1].get_included_reward_coins()[0]
3136
+ print("spend=", spend)
3137
+ # this create coin will spend all of the coin, so the 10 mojos below
3138
+ # will be "minted".
3139
+ output = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bt.pool_ph, int_to_bytes(spend.amount)])
3140
+ condition_dict = {ConditionOpcode.CREATE_COIN: [output]}
3141
+
3142
+ tx = wt.generate_signed_transaction(uint64(10), wt.get_new_puzzlehash(), spend, condition_dic=condition_dict)
3143
+
3144
+ blocks = bt.get_consecutive_blocks(
3145
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
3146
+ )
3147
+ await _validate_and_add_block(b, blocks[-1], expected_error=Err.MINTING_COIN)
3148
+ # 17 is tested in mempool tests
3149
+
3150
+ @pytest.mark.anyio
3151
+ async def test_max_coin_amount_fee(self) -> None:
3152
+ # 18 TODO: we can't create a block with such amounts due to uint64
3153
+ pass
3154
+
3155
+ @pytest.mark.anyio
3156
+ async def test_invalid_fees_in_block(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3157
+ # 19
3158
+ b = empty_blockchain
3159
+ blocks = bt.get_consecutive_blocks(
3160
+ 3,
3161
+ guarantee_transaction_block=True,
3162
+ farmer_reward_puzzle_hash=bt.pool_ph,
3163
+ pool_reward_puzzle_hash=bt.pool_ph,
3164
+ )
3165
+ await _validate_and_add_block(b, blocks[0])
3166
+ await _validate_and_add_block(b, blocks[1])
3167
+ await _validate_and_add_block(b, blocks[2])
3168
+
3169
+ wt: WalletTool = bt.get_pool_wallet_tool()
3170
+
3171
+ tx = wt.generate_signed_transaction(
3172
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
3173
+ )
3174
+
3175
+ blocks = bt.get_consecutive_blocks(
3176
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
3177
+ )
3178
+ block: FullBlock = blocks[-1]
3179
+
3180
+ # wrong feees
3181
+ block_2: FullBlock = recursive_replace(block, "transactions_info.fees", uint64(1239))
3182
+ assert block_2.transactions_info is not None
3183
+ block_2 = recursive_replace(
3184
+ block_2, "foliage_transaction_block.transactions_info_hash", block_2.transactions_info.get_hash()
3185
+ )
3186
+ assert block_2.foliage_transaction_block is not None
3187
+ block_2 = recursive_replace(
3188
+ block_2, "foliage.foliage_transaction_block_hash", block_2.foliage_transaction_block.get_hash()
3189
+ )
3190
+ new_m = block_2.foliage.foliage_transaction_block_hash
3191
+ assert new_m is not None
3192
+ new_fsb_sig = bt.get_plot_signature(new_m, block.reward_chain_block.proof_of_space.plot_public_key)
3193
+ block_2 = recursive_replace(block_2, "foliage.foliage_transaction_block_signature", new_fsb_sig)
3194
+
3195
+ await _validate_and_add_block(b, block_2, expected_error=Err.INVALID_BLOCK_FEE_AMOUNT)
3196
+
3197
+ @pytest.mark.anyio
3198
+ async def test_invalid_agg_sig(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3199
+ # 22
3200
+ b = empty_blockchain
3201
+ blocks = bt.get_consecutive_blocks(
3202
+ 3,
3203
+ guarantee_transaction_block=True,
3204
+ farmer_reward_puzzle_hash=bt.pool_ph,
3205
+ pool_reward_puzzle_hash=bt.pool_ph,
3206
+ )
3207
+ await _validate_and_add_block(b, blocks[0])
3208
+ await _validate_and_add_block(b, blocks[1])
3209
+ await _validate_and_add_block(b, blocks[2])
3210
+
3211
+ wt: WalletTool = bt.get_pool_wallet_tool()
3212
+
3213
+ tx = wt.generate_signed_transaction(
3214
+ uint64(10), wt.get_new_puzzlehash(), blocks[-1].get_included_reward_coins()[0]
3215
+ )
3216
+ blocks = bt.get_consecutive_blocks(
3217
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=tx
3218
+ )
3219
+
3220
+ last_block = recursive_replace(blocks[-1], "transactions_info.aggregated_signature", G2Element.generator())
3221
+ assert last_block.transactions_info is not None
3222
+ last_block = recursive_replace(
3223
+ last_block, "foliage_transaction_block.transactions_info_hash", last_block.transactions_info.get_hash()
3224
+ )
3225
+ assert last_block.foliage_transaction_block is not None
3226
+ last_block = recursive_replace(
3227
+ last_block, "foliage.foliage_transaction_block_hash", last_block.foliage_transaction_block.get_hash()
3228
+ )
3229
+ new_m = last_block.foliage.foliage_transaction_block_hash
3230
+ assert new_m is not None
3231
+ new_fsb_sig = bt.get_plot_signature(new_m, last_block.reward_chain_block.proof_of_space.plot_public_key)
3232
+ last_block = recursive_replace(last_block, "foliage.foliage_transaction_block_signature", new_fsb_sig)
3233
+
3234
+ # Bad signature fails during add_block
3235
+ await _validate_and_add_block(b, last_block, expected_error=Err.BAD_AGGREGATE_SIGNATURE)
3236
+
3237
+ # Bad signature also fails in prevalidation
3238
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
3239
+ diff = b.constants.DIFFICULTY_STARTING
3240
+ future = await pre_validate_block(
3241
+ b.constants,
3242
+ AugmentedBlockchain(b),
3243
+ last_block,
3244
+ b.pool,
3245
+ None,
3246
+ ValidationState(ssi, diff, None),
3247
+ )
3248
+ preval_result: PreValidationResult = await future
3249
+ assert preval_result.error == Err.BAD_AGGREGATE_SIGNATURE.value
3250
+
3251
+
3252
+ def maybe_header_hash(block: Optional[BlockRecord]) -> Optional[bytes32]:
3253
+ if block is None:
3254
+ return None
3255
+ return block.header_hash
3256
+
3257
+
3258
+ class TestReorgs:
3259
+ @pytest.mark.anyio
3260
+ async def test_basic_reorg(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3261
+ b = empty_blockchain
3262
+ blocks = bt.get_consecutive_blocks(15)
3263
+
3264
+ for block in blocks:
3265
+ await _validate_and_add_block(b, block)
3266
+ peak = b.get_peak()
3267
+ assert peak is not None
3268
+ assert peak.height == 14
3269
+
3270
+ blocks_reorg_chain = bt.get_consecutive_blocks(7, blocks[:10], seed=b"2")
3271
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
3272
+ for reorg_block in blocks_reorg_chain:
3273
+ if reorg_block.height < 10:
3274
+ await _validate_and_add_block(
3275
+ b, reorg_block, expected_result=AddBlockResult.ALREADY_HAVE_BLOCK, fork_info=fork_info
3276
+ )
3277
+ elif reorg_block.height < 15:
3278
+ await _validate_and_add_block(
3279
+ b, reorg_block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3280
+ )
3281
+ elif reorg_block.height >= 15:
3282
+ await _validate_and_add_block(b, reorg_block, fork_info=fork_info)
3283
+ peak = b.get_peak()
3284
+ assert peak is not None
3285
+ assert peak.height == 16
3286
+
3287
+ @pytest.mark.anyio
3288
+ async def test_get_tx_peak_reorg(
3289
+ self, empty_blockchain: Blockchain, bt: BlockTools, consensus_mode: ConsensusMode
3290
+ ) -> None:
3291
+ b = empty_blockchain
3292
+
3293
+ if consensus_mode < ConsensusMode.HARD_FORK_2_0:
3294
+ reorg_point = 13
3295
+ else:
3296
+ reorg_point = 12
3297
+ blocks = bt.get_consecutive_blocks(reorg_point)
3298
+
3299
+ last_tx_block: Optional[bytes32] = None
3300
+ for block in blocks:
3301
+ assert maybe_header_hash(b.get_tx_peak()) == last_tx_block
3302
+ await _validate_and_add_block(b, block)
3303
+ if block.is_transaction_block():
3304
+ last_tx_block = block.header_hash
3305
+ peak = b.get_peak()
3306
+ assert peak is not None
3307
+ assert peak.height == reorg_point - 1
3308
+ assert maybe_header_hash(b.get_tx_peak()) == last_tx_block
3309
+
3310
+ reorg_last_tx_block: Optional[bytes32] = None
3311
+ fork_block = blocks[9]
3312
+ fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
3313
+ blocks_reorg_chain = bt.get_consecutive_blocks(7, blocks[:10], seed=b"2")
3314
+ assert blocks_reorg_chain[reorg_point].is_transaction_block() is False
3315
+ for reorg_block in blocks_reorg_chain:
3316
+ if reorg_block.height < 10:
3317
+ await _validate_and_add_block(b, reorg_block, expected_result=AddBlockResult.ALREADY_HAVE_BLOCK)
3318
+ elif reorg_block.height < reorg_point:
3319
+ await _validate_and_add_block(
3320
+ b, reorg_block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3321
+ )
3322
+ elif reorg_block.height >= reorg_point:
3323
+ await _validate_and_add_block(b, reorg_block, fork_info=fork_info)
3324
+
3325
+ if reorg_block.is_transaction_block():
3326
+ reorg_last_tx_block = reorg_block.header_hash
3327
+ if reorg_block.height >= reorg_point:
3328
+ last_tx_block = reorg_last_tx_block
3329
+
3330
+ assert maybe_header_hash(b.get_tx_peak()) == last_tx_block
3331
+
3332
+ peak = b.get_peak()
3333
+ assert peak is not None
3334
+ assert peak.height == 16
3335
+
3336
+ @pytest.mark.anyio
3337
+ @pytest.mark.parametrize("light_blocks", [True, False])
3338
+ async def test_long_reorg(
3339
+ self,
3340
+ light_blocks: bool,
3341
+ empty_blockchain: Blockchain,
3342
+ default_10000_blocks: list[FullBlock],
3343
+ test_long_reorg_blocks: list[FullBlock],
3344
+ test_long_reorg_blocks_light: list[FullBlock],
3345
+ ) -> None:
3346
+ if light_blocks:
3347
+ reorg_blocks = test_long_reorg_blocks_light[:1650]
3348
+ else:
3349
+ reorg_blocks = test_long_reorg_blocks[:1200]
3350
+
3351
+ # Reorg longer than a difficulty adjustment
3352
+ # Also tests higher weight chain but lower height
3353
+ b = empty_blockchain
3354
+ num_blocks_chain_1 = 1600
3355
+ num_blocks_chain_2_start = 500
3356
+
3357
+ assert num_blocks_chain_1 < 10000
3358
+ blocks = default_10000_blocks[:num_blocks_chain_1]
3359
+
3360
+ print(f"pre-validating {len(blocks)} blocks")
3361
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
3362
+ diff = b.constants.DIFFICULTY_STARTING
3363
+ chain = AugmentedBlockchain(b)
3364
+ vs = ValidationState(ssi, diff, None)
3365
+ futures = []
3366
+ for block in blocks:
3367
+ futures.append(
3368
+ await pre_validate_block(
3369
+ b.constants,
3370
+ chain,
3371
+ block,
3372
+ b.pool,
3373
+ None,
3374
+ vs,
3375
+ )
3376
+ )
3377
+ pre_validation_results: list[PreValidationResult] = list(await asyncio.gather(*futures))
3378
+ for i, block in enumerate(blocks):
3379
+ if block.height != 0 and len(block.finished_sub_slots) > 0:
3380
+ if block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters is not None:
3381
+ ssi = block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters
3382
+ assert pre_validation_results[i].error is None
3383
+ if (block.height % 100) == 0:
3384
+ print(f"main chain: {block.height:4} weight: {block.weight}")
3385
+
3386
+ fork_info: ForkInfo = ForkInfo(block.height - 1, block.height - 1, block.prev_header_hash)
3387
+ assert fork_info is not None
3388
+ (result, err, _) = await b.add_block(
3389
+ block, pre_validation_results[i], sub_slot_iters=ssi, fork_info=fork_info
3390
+ )
3391
+ await check_block_store_invariant(b)
3392
+ assert err is None
3393
+ assert result == AddBlockResult.NEW_PEAK
3394
+
3395
+ peak = b.get_peak()
3396
+ assert peak is not None
3397
+ chain_1_height = peak.height
3398
+ chain_1_weight = peak.weight
3399
+ assert chain_1_height == (num_blocks_chain_1 - 1)
3400
+
3401
+ # The reorg blocks will have less time between them (timestamp) and therefore will make difficulty go up
3402
+ # This means that the weight will grow faster, and we can get a heavier chain with lower height
3403
+
3404
+ # If these assert fail, you probably need to change the fixture in reorg_blocks to create the
3405
+ # right amount of blocks at the right time
3406
+ assert reorg_blocks[num_blocks_chain_2_start - 1] == default_10000_blocks[num_blocks_chain_2_start - 1]
3407
+ assert reorg_blocks[num_blocks_chain_2_start] != default_10000_blocks[num_blocks_chain_2_start]
3408
+
3409
+ # one aspect of this test is to make sure we can reorg blocks that are
3410
+ # not in the cache. We need to explicitly prune the cache to get that
3411
+ # effect.
3412
+ b.clean_block_records()
3413
+
3414
+ first_peak = b.get_peak()
3415
+ fork_info2 = None
3416
+ for reorg_block in reorg_blocks:
3417
+ if (reorg_block.height % 100) == 0:
3418
+ peak = b.get_peak()
3419
+ assert peak is not None
3420
+ print(
3421
+ f"reorg chain: {reorg_block.height:4} "
3422
+ f"weight: {reorg_block.weight:7} "
3423
+ f"peak: {str(peak.header_hash)[:6]}"
3424
+ )
3425
+
3426
+ if reorg_block.height < num_blocks_chain_2_start:
3427
+ await _validate_and_add_block(b, reorg_block, expected_result=AddBlockResult.ALREADY_HAVE_BLOCK)
3428
+ elif reorg_block.weight <= chain_1_weight:
3429
+ if fork_info2 is None:
3430
+ fork_info2 = ForkInfo(reorg_block.height - 1, reorg_block.height - 1, reorg_block.prev_header_hash)
3431
+ await _validate_and_add_block(
3432
+ b, reorg_block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info2
3433
+ )
3434
+ elif reorg_block.weight > chain_1_weight:
3435
+ await _validate_and_add_block(
3436
+ b, reorg_block, expected_result=AddBlockResult.NEW_PEAK, fork_info=fork_info2
3437
+ )
3438
+
3439
+ # if these asserts fires, there was no reorg
3440
+ peak = b.get_peak()
3441
+ assert peak is not None
3442
+ assert first_peak != peak
3443
+ assert peak is not None
3444
+ assert peak.weight > chain_1_weight
3445
+ second_peak = peak
3446
+
3447
+ if light_blocks:
3448
+ assert peak.height > chain_1_height
3449
+ else:
3450
+ assert peak.height < chain_1_height
3451
+
3452
+ chain_2_weight = peak.weight
3453
+
3454
+ # now reorg back to the original chain
3455
+ # this exercises the case where we have some of the blocks in the DB already
3456
+ b.clean_block_records()
3457
+
3458
+ if light_blocks:
3459
+ blocks = default_10000_blocks[num_blocks_chain_2_start - 100 : 1800]
3460
+ else:
3461
+ blocks = default_10000_blocks[num_blocks_chain_2_start - 100 : 2600]
3462
+
3463
+ # the block validation requires previous block records to be in the
3464
+ # cache
3465
+ br = await b.get_block_record_from_db(blocks[0].prev_header_hash)
3466
+ for i in range(200):
3467
+ assert br is not None
3468
+ b.add_block_record(br)
3469
+ br = await b.get_block_record_from_db(br.prev_hash)
3470
+ assert br is not None
3471
+ b.add_block_record(br)
3472
+
3473
+ # start the fork point a few blocks back, to test that the blockchain
3474
+ # can catch up
3475
+ fork_block = default_10000_blocks[num_blocks_chain_2_start - 101]
3476
+ fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
3477
+ await b.warmup(fork_block.height)
3478
+ for block in blocks:
3479
+ if (block.height % 128) == 0:
3480
+ peak = b.get_peak()
3481
+ assert peak is not None
3482
+ print(
3483
+ f"original chain: {block.height:4} "
3484
+ f"weight: {block.weight:7} "
3485
+ f"peak: {str(peak.header_hash)[:6]}"
3486
+ )
3487
+ if block.height <= chain_1_height:
3488
+ expect = AddBlockResult.ALREADY_HAVE_BLOCK
3489
+ elif block.weight < chain_2_weight:
3490
+ expect = AddBlockResult.ADDED_AS_ORPHAN
3491
+ else:
3492
+ expect = AddBlockResult.NEW_PEAK
3493
+ await _validate_and_add_block(b, block, fork_info=fork_info, expected_result=expect)
3494
+
3495
+ # if these asserts fires, there was no reorg back to the original chain
3496
+ peak = b.get_peak()
3497
+ assert peak is not None
3498
+ assert peak.header_hash != second_peak.header_hash
3499
+ assert peak.weight > chain_2_weight
3500
+
3501
+ @pytest.mark.anyio
3502
+ async def test_long_compact_blockchain(
3503
+ self, empty_blockchain: Blockchain, default_2000_blocks_compact: list[FullBlock]
3504
+ ) -> None:
3505
+ b = empty_blockchain
3506
+ for block in default_2000_blocks_compact:
3507
+ await _validate_and_add_block(b, block, skip_prevalidation=True)
3508
+ peak = b.get_peak()
3509
+ assert peak is not None
3510
+ assert peak.height == len(default_2000_blocks_compact) - 1
3511
+
3512
+ @pytest.mark.anyio
3513
+ async def test_reorg_from_genesis(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3514
+ b = empty_blockchain
3515
+
3516
+ blocks = bt.get_consecutive_blocks(15)
3517
+
3518
+ for block in blocks:
3519
+ await _validate_and_add_block(b, block)
3520
+ peak = b.get_peak()
3521
+ assert peak is not None
3522
+ assert peak.height == 14
3523
+
3524
+ # Reorg to alternate chain that is 1 height longer
3525
+ blocks_reorg_chain = bt.get_consecutive_blocks(16, [], seed=b"2")
3526
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
3527
+ for reorg_block in blocks_reorg_chain:
3528
+ if reorg_block.height < 15:
3529
+ await _validate_and_add_block_multi_result(
3530
+ b,
3531
+ reorg_block,
3532
+ expected_result=[AddBlockResult.ADDED_AS_ORPHAN, AddBlockResult.ALREADY_HAVE_BLOCK],
3533
+ fork_info=fork_info,
3534
+ )
3535
+ elif reorg_block.height >= 15:
3536
+ await _validate_and_add_block(b, reorg_block, fork_info=fork_info)
3537
+
3538
+ # Back to original chain
3539
+ blocks_reorg_chain_2 = bt.get_consecutive_blocks(3, blocks, seed=b"3")
3540
+
3541
+ # we start from the beginning to make sure fork_info is built correctly
3542
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
3543
+ for reorg_block in blocks_reorg_chain_2:
3544
+ if reorg_block.height < 15:
3545
+ await _validate_and_add_block(
3546
+ b, reorg_block, expected_result=AddBlockResult.ALREADY_HAVE_BLOCK, fork_info=fork_info
3547
+ )
3548
+ elif reorg_block.height < 16:
3549
+ await _validate_and_add_block(
3550
+ b, reorg_block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info
3551
+ )
3552
+ else:
3553
+ await _validate_and_add_block(b, reorg_block, fork_info=fork_info)
3554
+
3555
+ peak = b.get_peak()
3556
+ assert peak is not None
3557
+ assert peak.height == 17
3558
+
3559
+ @pytest.mark.anyio
3560
+ async def test_reorg_transaction(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3561
+ b = empty_blockchain
3562
+ wallet_a = WalletTool(b.constants)
3563
+ WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
3564
+ coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
3565
+ receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]
3566
+
3567
+ blocks = bt.get_consecutive_blocks(10, farmer_reward_puzzle_hash=coinbase_puzzlehash)
3568
+ blocks = bt.get_consecutive_blocks(
3569
+ 2, blocks, farmer_reward_puzzle_hash=coinbase_puzzlehash, guarantee_transaction_block=True
3570
+ )
3571
+
3572
+ spend_block = blocks[10]
3573
+ spend_coin = None
3574
+ for coin in spend_block.get_included_reward_coins():
3575
+ if coin.puzzle_hash == coinbase_puzzlehash:
3576
+ spend_coin = coin
3577
+ assert spend_coin is not None
3578
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, spend_coin)
3579
+
3580
+ blocks = bt.get_consecutive_blocks(
3581
+ 2,
3582
+ blocks,
3583
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3584
+ transaction_data=spend_bundle,
3585
+ guarantee_transaction_block=True,
3586
+ )
3587
+
3588
+ blocks_fork = bt.get_consecutive_blocks(
3589
+ 1, blocks[:12], farmer_reward_puzzle_hash=coinbase_puzzlehash, seed=b"123", guarantee_transaction_block=True
3590
+ )
3591
+ blocks_fork = bt.get_consecutive_blocks(
3592
+ 2,
3593
+ blocks_fork,
3594
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3595
+ transaction_data=spend_bundle,
3596
+ guarantee_transaction_block=True,
3597
+ seed=b"1245",
3598
+ )
3599
+ for block in blocks:
3600
+ await _validate_and_add_block(b, block)
3601
+ fork_block = blocks[11]
3602
+ fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
3603
+ for block in blocks_fork[12:]:
3604
+ await _validate_and_add_block_no_error(b, block, fork_info=fork_info)
3605
+
3606
+ @pytest.mark.anyio
3607
+ async def test_get_header_blocks_in_range_tx_filter(self, empty_blockchain: Blockchain, bt: BlockTools) -> None:
3608
+ b = empty_blockchain
3609
+ blocks = bt.get_consecutive_blocks(
3610
+ 3,
3611
+ guarantee_transaction_block=True,
3612
+ pool_reward_puzzle_hash=bt.pool_ph,
3613
+ farmer_reward_puzzle_hash=bt.pool_ph,
3614
+ )
3615
+ await _validate_and_add_block(b, blocks[0])
3616
+ await _validate_and_add_block(b, blocks[1])
3617
+ await _validate_and_add_block(b, blocks[2])
3618
+ wt: WalletTool = bt.get_pool_wallet_tool()
3619
+ tx = wt.generate_signed_transaction(
3620
+ uint64(10), wt.get_new_puzzlehash(), blocks[2].get_included_reward_coins()[0]
3621
+ )
3622
+ blocks = bt.get_consecutive_blocks(
3623
+ 1,
3624
+ block_list_input=blocks,
3625
+ guarantee_transaction_block=True,
3626
+ transaction_data=tx,
3627
+ )
3628
+ await _validate_and_add_block(b, blocks[-1])
3629
+
3630
+ blocks_with_filter = await b.get_header_blocks_in_range(0, 10, tx_filter=True)
3631
+ blocks_without_filter = await b.get_header_blocks_in_range(0, 10, tx_filter=False)
3632
+ header_hash = blocks[-1].header_hash
3633
+ assert (
3634
+ blocks_with_filter[header_hash].transactions_filter
3635
+ != blocks_without_filter[header_hash].transactions_filter
3636
+ )
3637
+ assert blocks_with_filter[header_hash].header_hash == blocks_without_filter[header_hash].header_hash
3638
+
3639
+ @pytest.mark.anyio
3640
+ async def test_get_blocks_at(self, empty_blockchain: Blockchain, default_1000_blocks: list[FullBlock]) -> None:
3641
+ b = empty_blockchain
3642
+ heights = []
3643
+ for block in default_1000_blocks[:200]:
3644
+ heights.append(block.height)
3645
+ await _validate_and_add_block(b, block)
3646
+
3647
+ blocks = await b.get_block_records_at(heights, batch_size=2)
3648
+ assert blocks
3649
+ assert len(blocks) == 200
3650
+ assert blocks[-1].height == 199
3651
+
3652
+
3653
+ @pytest.mark.anyio
3654
+ async def test_reorg_new_ref(empty_blockchain: Blockchain, bt: BlockTools) -> None:
3655
+ b = empty_blockchain
3656
+ wallet_a = WalletTool(b.constants)
3657
+ WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
3658
+ coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
3659
+ receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]
3660
+
3661
+ blocks = bt.get_consecutive_blocks(
3662
+ 5,
3663
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3664
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3665
+ guarantee_transaction_block=True,
3666
+ )
3667
+
3668
+ all_coins = []
3669
+ for spend_block in blocks[:5]:
3670
+ for coin in spend_block.get_included_reward_coins():
3671
+ if coin.puzzle_hash == coinbase_puzzlehash:
3672
+ all_coins.append(coin)
3673
+ spend_bundle_0 = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3674
+ blocks = bt.get_consecutive_blocks(
3675
+ 15,
3676
+ block_list_input=blocks,
3677
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3678
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3679
+ transaction_data=spend_bundle_0,
3680
+ guarantee_transaction_block=True,
3681
+ )
3682
+
3683
+ for block in blocks:
3684
+ await _validate_and_add_block(b, block)
3685
+ peak = b.get_peak()
3686
+ assert peak is not None
3687
+ assert peak.height == 19
3688
+
3689
+ print("first chain done")
3690
+
3691
+ # Make sure a ref back into the reorg chain itself works as expected
3692
+
3693
+ blocks_reorg_chain = bt.get_consecutive_blocks(
3694
+ 1,
3695
+ blocks[:10],
3696
+ seed=b"2",
3697
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3698
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3699
+ )
3700
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3701
+
3702
+ blocks_reorg_chain = bt.get_consecutive_blocks(
3703
+ 2,
3704
+ blocks_reorg_chain,
3705
+ seed=b"2",
3706
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3707
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3708
+ transaction_data=spend_bundle,
3709
+ guarantee_transaction_block=True,
3710
+ )
3711
+
3712
+ spend_bundle2 = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3713
+ blocks_reorg_chain = bt.get_consecutive_blocks(
3714
+ 4, blocks_reorg_chain, seed=b"2", block_refs=[uint32(5), uint32(11)], transaction_data=spend_bundle2
3715
+ )
3716
+ blocks_reorg_chain = bt.get_consecutive_blocks(4, blocks_reorg_chain, seed=b"2")
3717
+
3718
+ fork_info = ForkInfo(-1, -1, b.constants.GENESIS_CHALLENGE)
3719
+ for i, block in enumerate(blocks_reorg_chain):
3720
+ if i < 10:
3721
+ expected = AddBlockResult.ALREADY_HAVE_BLOCK
3722
+ elif i < 19:
3723
+ expected = AddBlockResult.ADDED_AS_ORPHAN
3724
+ elif i == 19:
3725
+ # same height as peak decide by iterations
3726
+ peak = b.get_peak()
3727
+ assert peak is not None
3728
+ # same height as peak should be ADDED_AS_ORPHAN if block.total_iters >= peak.total_iters
3729
+ assert block.total_iters < peak.total_iters
3730
+ expected = AddBlockResult.NEW_PEAK
3731
+ else:
3732
+ expected = AddBlockResult.NEW_PEAK
3733
+ await _validate_and_add_block(b, block, expected_result=expected, fork_info=fork_info)
3734
+ peak = b.get_peak()
3735
+ assert peak is not None
3736
+ assert peak.height == 20
3737
+
3738
+
3739
+ # this test doesn't reorg, but _reconsider_peak() is passed a stale
3740
+ # "fork_height" to make it look like it's in a reorg, but all the same blocks
3741
+ # are just added back.
3742
+ @pytest.mark.anyio
3743
+ async def test_reorg_stale_fork_height(empty_blockchain: Blockchain, bt: BlockTools) -> None:
3744
+ b = empty_blockchain
3745
+ wallet_a = WalletTool(b.constants)
3746
+ WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
3747
+ coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
3748
+ receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]
3749
+
3750
+ blocks = bt.get_consecutive_blocks(
3751
+ 5,
3752
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3753
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3754
+ guarantee_transaction_block=True,
3755
+ )
3756
+
3757
+ all_coins = []
3758
+ for spend_block in blocks:
3759
+ for coin in spend_block.get_included_reward_coins():
3760
+ if coin.puzzle_hash == coinbase_puzzlehash:
3761
+ all_coins.append(coin)
3762
+
3763
+ # Make sure a ref back into the reorg chain itself works as expected
3764
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3765
+
3766
+ # make sure we have a transaction block, with at least one transaction in it
3767
+ blocks = bt.get_consecutive_blocks(
3768
+ 5,
3769
+ blocks,
3770
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3771
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3772
+ transaction_data=spend_bundle,
3773
+ guarantee_transaction_block=True,
3774
+ )
3775
+
3776
+ # this block (height 10) refers back to the generator in block 5
3777
+ spend_bundle2 = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3778
+ blocks = bt.get_consecutive_blocks(4, blocks, block_refs=[uint32(5)], transaction_data=spend_bundle2)
3779
+
3780
+ for block in blocks[:5]:
3781
+ await _validate_and_add_block(b, block, expected_result=AddBlockResult.NEW_PEAK)
3782
+
3783
+ # fake the fork_info to make every new block look like a reorg
3784
+ fork_info = ForkInfo(blocks[4].height, blocks[4].height, blocks[4].header_hash)
3785
+ for block in blocks[5:]:
3786
+ await _validate_and_add_block(b, block, expected_result=AddBlockResult.NEW_PEAK, fork_info=fork_info)
3787
+ peak = b.get_peak()
3788
+ assert peak is not None
3789
+ assert peak.height == 13
3790
+
3791
+
3792
+ @pytest.mark.anyio
3793
+ async def test_chain_failed_rollback(empty_blockchain: Blockchain, bt: BlockTools) -> None:
3794
+ b = empty_blockchain
3795
+ wallet_a = WalletTool(b.constants)
3796
+ WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
3797
+ coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
3798
+ receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]
3799
+
3800
+ blocks = bt.get_consecutive_blocks(
3801
+ 20,
3802
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3803
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3804
+ )
3805
+
3806
+ for block in blocks:
3807
+ await _validate_and_add_block(b, block)
3808
+ peak = b.get_peak()
3809
+ assert peak is not None
3810
+ assert peak.height == 19
3811
+
3812
+ print("first chain done")
3813
+
3814
+ # Make sure a ref back into the reorg chain itself works as expected
3815
+
3816
+ all_coins = []
3817
+ for spend_block in blocks[:10]:
3818
+ for coin in spend_block.get_included_reward_coins():
3819
+ if coin.puzzle_hash == coinbase_puzzlehash:
3820
+ all_coins.append(coin)
3821
+
3822
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3823
+
3824
+ blocks_reorg_chain = bt.get_consecutive_blocks(
3825
+ 11,
3826
+ blocks[:10],
3827
+ seed=b"2",
3828
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3829
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3830
+ transaction_data=spend_bundle,
3831
+ guarantee_transaction_block=True,
3832
+ )
3833
+
3834
+ fork_block = blocks_reorg_chain[9]
3835
+ fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
3836
+ for block in blocks_reorg_chain[10:-1]:
3837
+ await _validate_and_add_block(b, block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info)
3838
+
3839
+ # Incorrectly set the height as spent in DB to trigger an error
3840
+ print(f"{await b.coin_store.get_coin_record(spend_bundle.coin_spends[0].coin.name())}")
3841
+ print(spend_bundle.coin_spends[0].coin.name())
3842
+ # await b.coin_store._set_spent([spend_bundle.coin_spends[0].coin.name()], 8)
3843
+ await b.coin_store.rollback_to_block(2)
3844
+ print(f"{await b.coin_store.get_coin_record(spend_bundle.coin_spends[0].coin.name())}")
3845
+
3846
+ fork_block = blocks_reorg_chain[10 - 1]
3847
+ # fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
3848
+ with pytest.raises(ValueError, match="Invalid operation to set spent"):
3849
+ await _validate_and_add_block(b, blocks_reorg_chain[-1], fork_info=fork_info)
3850
+
3851
+ peak = b.get_peak()
3852
+ assert peak is not None
3853
+ assert peak.height == 19
3854
+
3855
+
3856
+ @pytest.mark.anyio
3857
+ async def test_reorg_flip_flop(empty_blockchain: Blockchain, bt: BlockTools) -> None:
3858
+ b = empty_blockchain
3859
+ wallet_a = WalletTool(b.constants)
3860
+ WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
3861
+ coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
3862
+ receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]
3863
+
3864
+ chain_a = bt.get_consecutive_blocks(
3865
+ 10,
3866
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3867
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3868
+ guarantee_transaction_block=True,
3869
+ )
3870
+
3871
+ all_coins = []
3872
+ for spend_block in chain_a:
3873
+ for coin in spend_block.get_included_reward_coins():
3874
+ if coin.puzzle_hash == coinbase_puzzlehash:
3875
+ all_coins.append(coin)
3876
+
3877
+ # this is a transaction block at height 10
3878
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3879
+ chain_a = bt.get_consecutive_blocks(
3880
+ 5,
3881
+ chain_a,
3882
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3883
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3884
+ transaction_data=spend_bundle,
3885
+ guarantee_transaction_block=True,
3886
+ )
3887
+
3888
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3889
+ chain_a = bt.get_consecutive_blocks(
3890
+ 5,
3891
+ chain_a,
3892
+ block_refs=[uint32(10)],
3893
+ transaction_data=spend_bundle,
3894
+ guarantee_transaction_block=True,
3895
+ )
3896
+
3897
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3898
+ chain_a = bt.get_consecutive_blocks(
3899
+ 20,
3900
+ chain_a,
3901
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3902
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3903
+ transaction_data=spend_bundle,
3904
+ guarantee_transaction_block=True,
3905
+ )
3906
+
3907
+ # chain A is 40 blocks deep
3908
+ # chain B share the first 20 blocks with chain A
3909
+
3910
+ # add 5 blocks on top of the first 20, to form chain B
3911
+ chain_b = bt.get_consecutive_blocks(
3912
+ 5,
3913
+ chain_a[:20],
3914
+ seed=b"2",
3915
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3916
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3917
+ )
3918
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3919
+
3920
+ # this is a transaction block at height 15 (in Chain B)
3921
+ chain_b = bt.get_consecutive_blocks(
3922
+ 5,
3923
+ chain_b,
3924
+ seed=b"2",
3925
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3926
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3927
+ transaction_data=spend_bundle,
3928
+ guarantee_transaction_block=True,
3929
+ )
3930
+
3931
+ spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop())
3932
+ chain_b = bt.get_consecutive_blocks(10, chain_b, seed=b"2", block_refs=[uint32(15)], transaction_data=spend_bundle)
3933
+
3934
+ assert len(chain_a) == len(chain_b)
3935
+
3936
+ counter = 0
3937
+ ssi = b.constants.SUB_SLOT_ITERS_STARTING
3938
+ diff = b.constants.DIFFICULTY_STARTING
3939
+ for b1, b2 in zip(chain_a, chain_b):
3940
+ # alternate the order we add blocks from the two chains, to ensure one
3941
+ # chain overtakes the other one in weight every other time
3942
+ if counter % 2 == 0:
3943
+ block1, block2 = b2, b1
3944
+ else:
3945
+ block1, block2 = b1, b2
3946
+ counter += 1
3947
+
3948
+ preval = await (
3949
+ await pre_validate_block(
3950
+ b.constants,
3951
+ AugmentedBlockchain(b),
3952
+ block1,
3953
+ b.pool,
3954
+ None,
3955
+ ValidationState(ssi, diff, None),
3956
+ )
3957
+ )
3958
+ peak = b.get_peak()
3959
+ if peak is None:
3960
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
3961
+ else:
3962
+ fork_info = await get_fork_info(b, block1, peak)
3963
+ _, err, _ = await b.add_block(block1, preval, sub_slot_iters=ssi, fork_info=fork_info)
3964
+ assert err is None
3965
+ preval = await (
3966
+ await pre_validate_block(
3967
+ b.constants,
3968
+ AugmentedBlockchain(b),
3969
+ block2,
3970
+ b.pool,
3971
+ None,
3972
+ ValidationState(ssi, diff, None),
3973
+ )
3974
+ )
3975
+ peak = b.get_peak()
3976
+ assert peak is not None
3977
+ fork_info = await get_fork_info(b, block2, peak)
3978
+ _, err, _ = await b.add_block(block2, preval, sub_slot_iters=ssi, fork_info=fork_info)
3979
+ assert err is None
3980
+
3981
+ peak = b.get_peak()
3982
+ assert peak is not None
3983
+ assert peak.height == 39
3984
+
3985
+ chain_b = bt.get_consecutive_blocks(
3986
+ 10,
3987
+ chain_b,
3988
+ seed=b"2",
3989
+ farmer_reward_puzzle_hash=coinbase_puzzlehash,
3990
+ pool_reward_puzzle_hash=receiver_puzzlehash,
3991
+ )
3992
+
3993
+ for block in chain_b[40:]:
3994
+ await _validate_and_add_block(b, block)
3995
+
3996
+
3997
+ async def test_get_tx_peak(default_400_blocks: list[FullBlock], empty_blockchain: Blockchain) -> None:
3998
+ bc = empty_blockchain
3999
+ test_blocks = default_400_blocks[:100]
4000
+ ssi = bc.constants.SUB_SLOT_ITERS_STARTING
4001
+ diff = bc.constants.DIFFICULTY_STARTING
4002
+ futures: list[Awaitable[PreValidationResult]] = []
4003
+ chain = AugmentedBlockchain(bc)
4004
+ vs = ValidationState(ssi, diff, None)
4005
+ for block in test_blocks:
4006
+ futures.append(
4007
+ await pre_validate_block(
4008
+ bc.constants,
4009
+ chain,
4010
+ block,
4011
+ bc.pool,
4012
+ None,
4013
+ vs,
4014
+ )
4015
+ )
4016
+
4017
+ res: list[PreValidationResult] = list(await asyncio.gather(*futures))
4018
+
4019
+ last_tx_block_record = None
4020
+ for b, prevalidation_res in zip(test_blocks, res):
4021
+ assert bc.get_tx_peak() == last_tx_block_record
4022
+ fork_info = ForkInfo(b.height - 1, b.height - 1, b.prev_header_hash)
4023
+ _, err, _ = await bc.add_block(b, prevalidation_res, sub_slot_iters=ssi, fork_info=fork_info)
4024
+ assert err is None
4025
+
4026
+ if b.is_transaction_block():
4027
+ assert prevalidation_res.required_iters is not None
4028
+ block_record = block_to_block_record(
4029
+ bc.constants,
4030
+ bc,
4031
+ prevalidation_res.required_iters,
4032
+ b,
4033
+ empty_blockchain.constants.SUB_SLOT_ITERS_STARTING,
4034
+ )
4035
+ last_tx_block_record = block_record
4036
+
4037
+ assert bc.get_tx_peak() == last_tx_block_record
4038
+
4039
+
4040
+ def to_bytes(gen: Optional[SerializedProgram]) -> bytes:
4041
+ assert gen is not None
4042
+ return bytes(gen)
4043
+
4044
+
4045
+ @pytest.mark.anyio
4046
+ @pytest.mark.limit_consensus_modes(reason="block heights for generators differ between test chains in different modes")
4047
+ @pytest.mark.parametrize("clear_cache", [True, False])
4048
+ async def test_lookup_block_generators(
4049
+ default_10000_blocks: list[FullBlock],
4050
+ test_long_reorg_blocks_light: list[FullBlock],
4051
+ bt: BlockTools,
4052
+ empty_blockchain: Blockchain,
4053
+ clear_cache: bool,
4054
+ ) -> None:
4055
+ b = empty_blockchain
4056
+ blocks_1 = default_10000_blocks
4057
+ blocks_2 = test_long_reorg_blocks_light
4058
+
4059
+ # this test blockchain is expected to have block generators at these
4060
+ # heights:
4061
+ # 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
4062
+ # 24, 25, 26, 28
4063
+
4064
+ # default_10000_blocks and test_long_reorg_blocks_light diverge at height
4065
+ # 500. Add blocks from both past the fork to be able to test both
4066
+
4067
+ # fork 1 is expected to have generators at these heights:
4068
+ # 503, 507, 511, 517, 524, 529, 532, 533, 534, 539, 542, 543, 546, 547
4069
+
4070
+ # fork 2 is expected to have generators at these heights:
4071
+ # 507, 516, 527, 535, 539, 543, 547
4072
+
4073
+ # start with adding some blocks to test lookups from the mainchain
4074
+ fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
4075
+ for block in blocks_2[:550]:
4076
+ await _validate_and_add_block(b, block, expected_result=AddBlockResult.NEW_PEAK, fork_info=fork_info)
4077
+
4078
+ fork_info = ForkInfo(blocks_1[500].height - 1, blocks_1[500].height - 1, blocks_1[500].prev_header_hash)
4079
+ for block in blocks_1[500:550]:
4080
+ await _validate_and_add_block(b, block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info)
4081
+
4082
+ # now we have a blockchain with two forks, the peak is at blocks_2[550] and
4083
+ # the leight weight peak is at blocks_1[550]
4084
+ # make sure we can lookup block generators from each fork
4085
+
4086
+ peak_1 = blocks_1[550]
4087
+ peak_2 = blocks_2[550]
4088
+
4089
+ # single generators, from the shared part of the chain
4090
+ for peak in [peak_1, peak_2]:
4091
+ if clear_cache:
4092
+ b.clean_block_records()
4093
+ generators = await b.lookup_block_generators(peak.prev_header_hash, {uint32(2)})
4094
+ assert generators == {
4095
+ uint32(2): to_bytes(blocks_1[2].transactions_generator),
4096
+ }
4097
+
4098
+ # multiple generators from the shared part of the chain
4099
+ for peak in [peak_1, peak_2]:
4100
+ if clear_cache:
4101
+ b.clean_block_records()
4102
+ generators = await b.lookup_block_generators(peak.prev_header_hash, {uint32(2), uint32(10), uint32(26)})
4103
+ assert generators == {
4104
+ uint32(2): to_bytes(blocks_1[2].transactions_generator),
4105
+ uint32(10): to_bytes(blocks_1[10].transactions_generator),
4106
+ uint32(26): to_bytes(blocks_1[26].transactions_generator),
4107
+ }
4108
+
4109
+ # lookups from the past the fork
4110
+ if clear_cache:
4111
+ b.clean_block_records()
4112
+ generators = await b.lookup_block_generators(peak_1.prev_header_hash, {uint32(503)})
4113
+ assert generators == {uint32(503): to_bytes(blocks_1[503].transactions_generator)}
4114
+
4115
+ if clear_cache:
4116
+ b.clean_block_records()
4117
+ generators = await b.lookup_block_generators(peak_2.prev_header_hash, {uint32(516)})
4118
+ assert generators == {uint32(516): to_bytes(blocks_2[516].transactions_generator)}
4119
+
4120
+ # make sure we don't cross the forks
4121
+ if clear_cache:
4122
+ b.clean_block_records()
4123
+ with pytest.raises(ValueError, match="Err.GENERATOR_REF_HAS_NO_GENERATOR"):
4124
+ await b.lookup_block_generators(peak_1.prev_header_hash, {uint32(516)})
4125
+
4126
+ if clear_cache:
4127
+ b.clean_block_records()
4128
+ with pytest.raises(ValueError, match="Err.GENERATOR_REF_HAS_NO_GENERATOR"):
4129
+ await b.lookup_block_generators(peak_2.prev_header_hash, {uint32(503)})
4130
+
4131
+ # make sure we fail when looking up a non-transaction block from the main
4132
+ # chain, regardless of which chain we start at
4133
+ if clear_cache:
4134
+ b.clean_block_records()
4135
+ with pytest.raises(ValueError, match="Err.GENERATOR_REF_HAS_NO_GENERATOR"):
4136
+ await b.lookup_block_generators(peak_1.prev_header_hash, {uint32(8)})
4137
+
4138
+ if clear_cache:
4139
+ b.clean_block_records()
4140
+ with pytest.raises(ValueError, match="Err.GENERATOR_REF_HAS_NO_GENERATOR"):
4141
+ await b.lookup_block_generators(peak_2.prev_header_hash, {uint32(8)})
4142
+
4143
+ # if we try to look up generators starting from a disconnected block, we
4144
+ # fail
4145
+ if clear_cache:
4146
+ b.clean_block_records()
4147
+ with pytest.raises(AssertionError):
4148
+ await b.lookup_block_generators(blocks_2[600].prev_header_hash, {uint32(3)})
4149
+
4150
+ if clear_cache:
4151
+ b.clean_block_records()
4152
+ with pytest.raises(AssertionError):
4153
+ await b.lookup_block_generators(blocks_1[600].prev_header_hash, {uint32(3)})
4154
+
4155
+
4156
+ async def get_fork_info(blockchain: Blockchain, block: FullBlock, peak: BlockRecord) -> ForkInfo:
4157
+ fork_chain, fork_hash = await lookup_fork_chain(
4158
+ blockchain,
4159
+ (peak.height, peak.header_hash),
4160
+ (block.height - 1, block.prev_header_hash),
4161
+ blockchain.constants,
4162
+ )
4163
+ # now we know how long the fork is, and can compute the fork
4164
+ # height.
4165
+ fork_height = block.height - len(fork_chain) - 1
4166
+ fork_info = ForkInfo(fork_height, fork_height, fork_hash)
4167
+
4168
+ # now run all the blocks of the fork to compute the additions
4169
+ # and removals. They are recorded in the fork_info object
4170
+ counter = 0
4171
+ start = time.monotonic()
4172
+ for height in range(fork_info.fork_height + 1, block.height):
4173
+ fork_block: Optional[FullBlock] = await blockchain.block_store.get_full_block(fork_chain[uint32(height)])
4174
+ assert fork_block is not None
4175
+ assert fork_block.height - 1 == fork_info.peak_height
4176
+ assert fork_block.height == 0 or fork_block.prev_header_hash == fork_info.peak_hash
4177
+ await blockchain.run_single_block(fork_block, fork_info)
4178
+ counter += 1
4179
+ end = time.monotonic()
4180
+ log.info(
4181
+ f"executed {counter} block generators in {end - start:2f} s. "
4182
+ f"{len(fork_info.additions_since_fork)} additions, "
4183
+ f"{len(fork_info.removals_since_fork)} removals"
4184
+ )
4185
+
4186
+ return fork_info
4187
+
4188
+
4189
+ @pytest.mark.anyio
4190
+ async def test_get_header_blocks_in_range_tx_filter_non_tx_block(empty_blockchain: Blockchain, bt: BlockTools) -> None:
4191
+ """
4192
+ Covers the case of calling `get_header_blocks_in_range`, requesting
4193
+ transactions filter, on a non transaction block.
4194
+ """
4195
+ b = empty_blockchain
4196
+ blocks = bt.get_consecutive_blocks(2)
4197
+ for block in blocks:
4198
+ await _validate_and_add_block(b, block)
4199
+ non_tx_block = next(block for block in blocks if not block.is_transaction_block())
4200
+ blocks_with_filter = await b.get_header_blocks_in_range(0, 42, tx_filter=True)
4201
+ empty_tx_filter = b"\x00"
4202
+ assert blocks_with_filter[non_tx_block.header_hash].transactions_filter == empty_tx_filter