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,2084 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import logging
5
+ from collections.abc import Awaitable, Collection
6
+ from typing import Any, Callable, ClassVar, Optional
7
+
8
+ import pytest
9
+ from chia_rs import ELIGIBLE_FOR_DEDUP, ELIGIBLE_FOR_FF, AugSchemeMPL, G2Element, get_conditions_from_spendbundle
10
+ from chiabip158 import PyBIP158
11
+
12
+ from chia._tests.conftest import ConsensusMode
13
+ from chia._tests.util.misc import invariant_check_mempool
14
+ from chia._tests.util.setup_nodes import OldSimulatorsAndWallets, setup_simulators_and_wallets
15
+ from chia.consensus.condition_costs import ConditionCost
16
+ from chia.consensus.constants import ConsensusConstants
17
+ from chia.consensus.default_constants import DEFAULT_CONSTANTS
18
+ from chia.full_node.mempool import MAX_SKIPPED_ITEMS, PRIORITY_TX_THRESHOLD
19
+ from chia.full_node.mempool_check_conditions import mempool_check_time_locks
20
+ from chia.full_node.mempool_manager import (
21
+ MEMPOOL_MIN_FEE_INCREASE,
22
+ QUOTE_BYTES,
23
+ QUOTE_EXECUTION_COST,
24
+ MempoolManager,
25
+ TimelockConditions,
26
+ can_replace,
27
+ compute_assert_height,
28
+ optional_max,
29
+ optional_min,
30
+ )
31
+ from chia.protocols import wallet_protocol
32
+ from chia.protocols.full_node_protocol import RequestBlock, RespondBlock
33
+ from chia.protocols.protocol_message_types import ProtocolMessageTypes
34
+ from chia.simulator.full_node_simulator import FullNodeSimulator
35
+ from chia.simulator.simulator_protocol import FarmNewBlockProtocol
36
+ from chia.types.blockchain_format.coin import Coin
37
+ from chia.types.blockchain_format.program import INFINITE_COST, Program
38
+ from chia.types.blockchain_format.serialized_program import SerializedProgram
39
+ from chia.types.blockchain_format.sized_bytes import bytes32
40
+ from chia.types.clvm_cost import CLVMCost
41
+ from chia.types.coin_record import CoinRecord
42
+ from chia.types.coin_spend import CoinSpend, make_spend
43
+ from chia.types.condition_opcodes import ConditionOpcode
44
+ from chia.types.eligible_coin_spends import (
45
+ DedupCoinSpend,
46
+ EligibilityAndAdditions,
47
+ EligibleCoinSpends,
48
+ UnspentLineageInfo,
49
+ run_for_cost,
50
+ )
51
+ from chia.types.mempool_inclusion_status import MempoolInclusionStatus
52
+ from chia.types.mempool_item import BundleCoinSpend, MempoolItem
53
+ from chia.types.peer_info import PeerInfo
54
+ from chia.types.spend_bundle import SpendBundle
55
+ from chia.types.spend_bundle_conditions import SpendBundleConditions, SpendConditions
56
+ from chia.util.errors import Err, ValidationError
57
+ from chia.util.ints import uint8, uint32, uint64
58
+ from chia.wallet.conditions import AssertCoinAnnouncement
59
+ from chia.wallet.payment import Payment
60
+ from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
61
+ from chia.wallet.wallet import Wallet
62
+ from chia.wallet.wallet_coin_record import WalletCoinRecord
63
+ from chia.wallet.wallet_node import WalletNode
64
+
65
+ IDENTITY_PUZZLE = SerializedProgram.to(1)
66
+ IDENTITY_PUZZLE_HASH = IDENTITY_PUZZLE.get_tree_hash()
67
+
68
+ TEST_TIMESTAMP = uint64(10040)
69
+ TEST_COIN_AMOUNT = uint64(1000000000)
70
+ TEST_COIN = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT)
71
+ TEST_COIN_ID = TEST_COIN.name()
72
+ TEST_COIN_RECORD = CoinRecord(TEST_COIN, uint32(0), uint32(0), False, TEST_TIMESTAMP)
73
+ TEST_COIN_AMOUNT2 = uint64(2000000000)
74
+ TEST_COIN2 = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT2)
75
+ TEST_COIN_ID2 = TEST_COIN2.name()
76
+ TEST_COIN_RECORD2 = CoinRecord(TEST_COIN2, uint32(0), uint32(0), False, TEST_TIMESTAMP)
77
+ TEST_COIN_AMOUNT3 = uint64(3000000000)
78
+ TEST_COIN3 = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT3)
79
+ TEST_COIN_ID3 = TEST_COIN3.name()
80
+ TEST_COIN_RECORD3 = CoinRecord(TEST_COIN3, uint32(0), uint32(0), False, TEST_TIMESTAMP)
81
+ TEST_HEIGHT = uint32(5)
82
+
83
+
84
+ @dataclasses.dataclass(frozen=True)
85
+ class TestBlockRecord:
86
+ """
87
+ This is a subset of BlockRecord that the mempool manager uses for peak.
88
+ """
89
+
90
+ header_hash: bytes32
91
+ height: uint32
92
+ timestamp: Optional[uint64]
93
+ prev_transaction_block_height: uint32
94
+ prev_transaction_block_hash: Optional[bytes32]
95
+
96
+ @property
97
+ def is_transaction_block(self) -> bool:
98
+ return self.timestamp is not None
99
+
100
+
101
+ async def zero_calls_get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
102
+ assert len(coin_ids) == 0
103
+ return []
104
+
105
+
106
+ async def get_coin_records_for_test_coins(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
107
+ test_coin_records = {
108
+ TEST_COIN_ID: TEST_COIN_RECORD,
109
+ TEST_COIN_ID2: TEST_COIN_RECORD2,
110
+ TEST_COIN_ID3: TEST_COIN_RECORD3,
111
+ }
112
+
113
+ ret: list[CoinRecord] = []
114
+ for name in coin_ids:
115
+ r = test_coin_records.get(name)
116
+ if r is not None:
117
+ ret.append(r)
118
+ return ret
119
+
120
+
121
+ def height_hash(height: int) -> bytes32:
122
+ return bytes32(height.to_bytes(32, byteorder="big"))
123
+
124
+
125
+ def create_test_block_record(*, height: uint32 = TEST_HEIGHT, timestamp: uint64 = TEST_TIMESTAMP) -> TestBlockRecord:
126
+ return TestBlockRecord(
127
+ header_hash=height_hash(height),
128
+ height=height,
129
+ timestamp=timestamp,
130
+ prev_transaction_block_height=uint32(height - 1),
131
+ prev_transaction_block_hash=height_hash(height - 1),
132
+ )
133
+
134
+
135
+ async def instantiate_mempool_manager(
136
+ get_coin_records: Callable[[Collection[bytes32]], Awaitable[list[CoinRecord]]],
137
+ *,
138
+ block_height: uint32 = TEST_HEIGHT,
139
+ block_timestamp: uint64 = TEST_TIMESTAMP,
140
+ constants: ConsensusConstants = DEFAULT_CONSTANTS,
141
+ max_tx_clvm_cost: Optional[uint64] = None,
142
+ ) -> MempoolManager:
143
+ mempool_manager = MempoolManager(get_coin_records, constants, max_tx_clvm_cost=max_tx_clvm_cost)
144
+ test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
145
+ await mempool_manager.new_peak(test_block_record, None)
146
+ invariant_check_mempool(mempool_manager.mempool)
147
+ return mempool_manager
148
+
149
+
150
+ async def setup_mempool_with_coins(
151
+ *,
152
+ coin_amounts: list[int],
153
+ max_block_clvm_cost: Optional[int] = None,
154
+ max_tx_clvm_cost: Optional[uint64] = None,
155
+ mempool_block_buffer: Optional[int] = None,
156
+ ) -> tuple[MempoolManager, list[Coin]]:
157
+ coins = []
158
+ test_coin_records = {}
159
+ for amount in coin_amounts:
160
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount))
161
+ coins.append(coin)
162
+ test_coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
163
+
164
+ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
165
+ ret: list[CoinRecord] = []
166
+ for name in coin_ids:
167
+ r = test_coin_records.get(name)
168
+ if r is not None:
169
+ ret.append(r)
170
+ return ret
171
+
172
+ constants = DEFAULT_CONSTANTS
173
+ if max_block_clvm_cost is not None:
174
+ constants = constants.replace(MAX_BLOCK_COST_CLVM=uint64(max_block_clvm_cost + TEST_BLOCK_OVERHEAD))
175
+ if mempool_block_buffer is not None:
176
+ constants = constants.replace(MEMPOOL_BLOCK_BUFFER=uint8(mempool_block_buffer))
177
+ mempool_manager = await instantiate_mempool_manager(
178
+ get_coin_records, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
179
+ )
180
+ return (mempool_manager, coins)
181
+
182
+
183
+ def make_test_conds(
184
+ *,
185
+ birth_height: Optional[int] = None,
186
+ birth_seconds: Optional[int] = None,
187
+ height_relative: Optional[int] = None,
188
+ height_absolute: int = 0,
189
+ seconds_relative: Optional[int] = None,
190
+ seconds_absolute: int = 0,
191
+ before_height_relative: Optional[int] = None,
192
+ before_height_absolute: Optional[int] = None,
193
+ before_seconds_relative: Optional[int] = None,
194
+ before_seconds_absolute: Optional[int] = None,
195
+ cost: int = 0,
196
+ spend_ids: list[bytes32] = [TEST_COIN_ID],
197
+ ) -> SpendBundleConditions:
198
+ return SpendBundleConditions(
199
+ [
200
+ SpendConditions(
201
+ spend_id,
202
+ IDENTITY_PUZZLE_HASH,
203
+ IDENTITY_PUZZLE_HASH,
204
+ TEST_COIN_AMOUNT,
205
+ None if height_relative is None else uint32(height_relative),
206
+ None if seconds_relative is None else uint64(seconds_relative),
207
+ None if before_height_relative is None else uint32(before_height_relative),
208
+ None if before_seconds_relative is None else uint64(before_seconds_relative),
209
+ None if birth_height is None else uint32(birth_height),
210
+ None if birth_seconds is None else uint64(birth_seconds),
211
+ [],
212
+ [],
213
+ [],
214
+ [],
215
+ [],
216
+ [],
217
+ [],
218
+ [],
219
+ 0,
220
+ )
221
+ for spend_id in spend_ids
222
+ ],
223
+ 0,
224
+ uint32(height_absolute),
225
+ uint64(seconds_absolute),
226
+ None if before_height_absolute is None else uint32(before_height_absolute),
227
+ None if before_seconds_absolute is None else uint64(before_seconds_absolute),
228
+ [],
229
+ cost,
230
+ 0,
231
+ 0,
232
+ False,
233
+ 0,
234
+ 0,
235
+ )
236
+
237
+
238
+ class TestCheckTimeLocks:
239
+ COIN_CONFIRMED_HEIGHT: ClassVar[uint32] = uint32(10)
240
+ COIN_TIMESTAMP: ClassVar[uint64] = uint64(10000)
241
+ PREV_BLOCK_HEIGHT: ClassVar[uint32] = uint32(15)
242
+ PREV_BLOCK_TIMESTAMP: ClassVar[uint64] = uint64(10150)
243
+
244
+ COIN_RECORD: ClassVar[CoinRecord] = CoinRecord(
245
+ TEST_COIN,
246
+ confirmed_block_index=uint32(COIN_CONFIRMED_HEIGHT),
247
+ spent_block_index=uint32(0),
248
+ coinbase=False,
249
+ timestamp=COIN_TIMESTAMP,
250
+ )
251
+ REMOVALS: ClassVar[dict[bytes32, CoinRecord]] = {TEST_COIN.name(): COIN_RECORD}
252
+
253
+ @pytest.mark.parametrize(
254
+ "conds,expected",
255
+ [
256
+ (make_test_conds(height_relative=5), None),
257
+ (make_test_conds(height_relative=6), Err.ASSERT_HEIGHT_RELATIVE_FAILED),
258
+ (make_test_conds(height_absolute=PREV_BLOCK_HEIGHT), None),
259
+ (make_test_conds(height_absolute=uint32(PREV_BLOCK_HEIGHT + 1)), Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
260
+ (make_test_conds(seconds_relative=150), None),
261
+ (make_test_conds(seconds_relative=151), Err.ASSERT_SECONDS_RELATIVE_FAILED),
262
+ (make_test_conds(seconds_absolute=PREV_BLOCK_TIMESTAMP), None),
263
+ (make_test_conds(seconds_absolute=uint64(PREV_BLOCK_TIMESTAMP + 1)), Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
264
+ # the coin's confirmed height is 10
265
+ (make_test_conds(birth_height=9), Err.ASSERT_MY_BIRTH_HEIGHT_FAILED),
266
+ (make_test_conds(birth_height=10), None),
267
+ (make_test_conds(birth_height=11), Err.ASSERT_MY_BIRTH_HEIGHT_FAILED),
268
+ # coin timestamp is 10000
269
+ (make_test_conds(birth_seconds=uint64(COIN_TIMESTAMP - 1)), Err.ASSERT_MY_BIRTH_SECONDS_FAILED),
270
+ (make_test_conds(birth_seconds=COIN_TIMESTAMP), None),
271
+ (make_test_conds(birth_seconds=uint64(COIN_TIMESTAMP + 1)), Err.ASSERT_MY_BIRTH_SECONDS_FAILED),
272
+ # the coin is 5 blocks old in this test
273
+ (make_test_conds(before_height_relative=5), Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
274
+ (make_test_conds(before_height_relative=6), None),
275
+ # The block height is 15
276
+ (make_test_conds(before_height_absolute=PREV_BLOCK_HEIGHT), Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
277
+ (make_test_conds(before_height_absolute=uint64(PREV_BLOCK_HEIGHT + 1)), None),
278
+ # the coin is 150 seconds old in this test
279
+ (make_test_conds(before_seconds_relative=150), Err.ASSERT_BEFORE_SECONDS_RELATIVE_FAILED),
280
+ (make_test_conds(before_seconds_relative=151), None),
281
+ # The block timestamp is 10150
282
+ (make_test_conds(before_seconds_absolute=PREV_BLOCK_TIMESTAMP), Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
283
+ (make_test_conds(before_seconds_absolute=uint64(PREV_BLOCK_TIMESTAMP + 1)), None),
284
+ ],
285
+ )
286
+ def test_conditions(
287
+ self,
288
+ conds: SpendBundleConditions,
289
+ expected: Optional[Err],
290
+ ) -> None:
291
+ assert (
292
+ mempool_check_time_locks(
293
+ dict(self.REMOVALS),
294
+ conds,
295
+ self.PREV_BLOCK_HEIGHT,
296
+ self.PREV_BLOCK_TIMESTAMP,
297
+ )
298
+ == expected
299
+ )
300
+
301
+
302
+ def expect(
303
+ *, height: int = 0, seconds: int = 0, before_height: Optional[int] = None, before_seconds: Optional[int] = None
304
+ ) -> TimelockConditions:
305
+ ret = TimelockConditions(uint32(height), uint64(seconds))
306
+ if before_height is not None:
307
+ ret.assert_before_height = uint32(before_height)
308
+ if before_seconds is not None:
309
+ ret.assert_before_seconds = uint64(before_seconds)
310
+ return ret
311
+
312
+
313
+ @pytest.mark.parametrize(
314
+ "conds,expected",
315
+ [
316
+ # ASSERT_HEIGHT_*
317
+ # coin birth height is 12
318
+ (make_test_conds(), expect()),
319
+ (make_test_conds(height_absolute=42), expect(height=42)),
320
+ # 1 is a relative height, but that only amounts to 13, so the absolute
321
+ # height is more restrictive
322
+ (make_test_conds(height_relative=1), expect(height=13)),
323
+ # 100 is a relative height, and since the coin was confirmed at height 12,
324
+ # that's 112
325
+ (make_test_conds(height_absolute=42, height_relative=100), expect(height=112)),
326
+ # Same thing but without the absolute height
327
+ (make_test_conds(height_relative=100), expect(height=112)),
328
+ (make_test_conds(height_relative=0), expect(height=12)),
329
+ # 42 is more restrictive than 13
330
+ (make_test_conds(height_absolute=42, height_relative=1), expect(height=42)),
331
+ # ASSERT_BEFORE_HEIGHT_*
332
+ (make_test_conds(before_height_absolute=100), expect(before_height=100)),
333
+ # coin is created at 12 + 1 relative height = 13
334
+ (make_test_conds(before_height_relative=1), expect(before_height=13)),
335
+ # coin is created at 12 + 0 relative height = 12
336
+ (make_test_conds(before_height_relative=0), expect(before_height=12)),
337
+ # 13 is more restrictive than 42
338
+ (make_test_conds(before_height_absolute=42, before_height_relative=1), expect(before_height=13)),
339
+ # 100 is a relative height, and since the coin was confirmed at height 12,
340
+ # that's 112
341
+ (make_test_conds(before_height_absolute=200, before_height_relative=100), expect(before_height=112)),
342
+ # Same thing but without the absolute height
343
+ (make_test_conds(before_height_relative=100), expect(before_height=112)),
344
+ # ASSERT_BEFORE_SECONDS_*
345
+ # coin timestamp is 10000
346
+ # single absolute assert before seconds
347
+ (make_test_conds(before_seconds_absolute=20000), expect(before_seconds=20000)),
348
+ # coin is created at 10000 + 100 relative seconds = 10100
349
+ (make_test_conds(before_seconds_relative=100), expect(before_seconds=10100)),
350
+ # coin is created at 10000 + 0 relative seconds = 10000
351
+ (make_test_conds(before_seconds_relative=0), expect(before_seconds=10000)),
352
+ # 10100 is more restrictive than 20000
353
+ (make_test_conds(before_seconds_absolute=20000, before_seconds_relative=100), expect(before_seconds=10100)),
354
+ # 20000 is a relative seconds, and since the coin was confirmed at seconds
355
+ # 10000 that's 300000
356
+ (make_test_conds(before_seconds_absolute=20000, before_seconds_relative=20000), expect(before_seconds=20000)),
357
+ # Same thing but without the absolute seconds
358
+ (make_test_conds(before_seconds_relative=20000), expect(before_seconds=30000)),
359
+ # ASSERT_SECONDS_*
360
+ # coin timestamp is 10000
361
+ # single absolute assert seconds
362
+ (make_test_conds(seconds_absolute=20000), expect(seconds=20000)),
363
+ # coin is created at 10000 + 100 relative seconds = 10100
364
+ (make_test_conds(seconds_relative=100), expect(seconds=10100)),
365
+ # coin is created at 10000 + 0 relative seconds = 10000
366
+ (make_test_conds(seconds_relative=0), expect(seconds=10000)),
367
+ # 20000 is more restrictive than 10100
368
+ (make_test_conds(seconds_absolute=20000, seconds_relative=100), expect(seconds=20000)),
369
+ # 20000 is a relative seconds, and since the coin was confirmed at seconds
370
+ # 10000 that's 300000
371
+ (make_test_conds(seconds_absolute=20000, seconds_relative=20000), expect(seconds=30000)),
372
+ # Same thing but without the absolute seconds
373
+ (make_test_conds(seconds_relative=20000), expect(seconds=30000)),
374
+ ],
375
+ )
376
+ def test_compute_assert_height(conds: SpendBundleConditions, expected: TimelockConditions) -> None:
377
+ coin_id = TEST_COIN.name()
378
+
379
+ confirmed_height = uint32(12)
380
+ coin_records = {coin_id: CoinRecord(TEST_COIN, confirmed_height, uint32(0), False, uint64(10000))}
381
+
382
+ assert compute_assert_height(coin_records, conds) == expected
383
+
384
+
385
+ def spend_bundle_from_conditions(
386
+ conditions: list[list[Any]], coin: Coin = TEST_COIN, aggsig: G2Element = G2Element()
387
+ ) -> SpendBundle:
388
+ solution = SerializedProgram.to(conditions)
389
+ coin_spend = make_spend(coin, IDENTITY_PUZZLE, solution)
390
+ return SpendBundle([coin_spend], aggsig)
391
+
392
+
393
+ async def add_spendbundle(
394
+ mempool_manager: MempoolManager, sb: SpendBundle, sb_name: bytes32
395
+ ) -> tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]:
396
+ sbc = await mempool_manager.pre_validate_spendbundle(sb, sb_name)
397
+ ret = await mempool_manager.add_spend_bundle(sb, sbc, sb_name, TEST_HEIGHT)
398
+ invariant_check_mempool(mempool_manager.mempool)
399
+ return ret.cost, ret.status, ret.error
400
+
401
+
402
+ async def generate_and_add_spendbundle(
403
+ mempool_manager: MempoolManager,
404
+ conditions: list[list[Any]],
405
+ coin: Coin = TEST_COIN,
406
+ aggsig: G2Element = G2Element(),
407
+ ) -> tuple[SpendBundle, bytes32, tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]]:
408
+ sb = spend_bundle_from_conditions(conditions, coin, aggsig)
409
+ sb_name = sb.name()
410
+ result = await add_spendbundle(mempool_manager, sb, sb_name)
411
+ return (sb, sb_name, result)
412
+
413
+
414
+ def make_bundle_spends_map_and_fee(
415
+ spend_bundle: SpendBundle, conds: SpendBundleConditions
416
+ ) -> tuple[dict[bytes32, BundleCoinSpend], uint64]:
417
+ bundle_coin_spends: dict[bytes32, BundleCoinSpend] = {}
418
+ eligibility_and_additions: dict[bytes32, EligibilityAndAdditions] = {}
419
+ removals_amount = 0
420
+ additions_amount = 0
421
+ for spend in conds.spends:
422
+ coin_id = bytes32(spend.coin_id)
423
+ spend_additions = []
424
+ for puzzle_hash, amount, _ in spend.create_coin:
425
+ spend_additions.append(Coin(coin_id, puzzle_hash, uint64(amount)))
426
+ additions_amount += amount
427
+ eligibility_and_additions[coin_id] = EligibilityAndAdditions(
428
+ is_eligible_for_dedup=bool(spend.flags & ELIGIBLE_FOR_DEDUP),
429
+ spend_additions=spend_additions,
430
+ is_eligible_for_ff=bool(spend.flags & ELIGIBLE_FOR_FF),
431
+ )
432
+ for coin_spend in spend_bundle.coin_spends:
433
+ coin_id = coin_spend.coin.name()
434
+ removals_amount += coin_spend.coin.amount
435
+ eligibility_info = eligibility_and_additions.get(
436
+ coin_id, EligibilityAndAdditions(is_eligible_for_dedup=False, spend_additions=[], is_eligible_for_ff=False)
437
+ )
438
+ bundle_coin_spends[coin_id] = BundleCoinSpend(
439
+ coin_spend=coin_spend,
440
+ eligible_for_dedup=eligibility_info.is_eligible_for_dedup,
441
+ eligible_for_fast_forward=eligibility_info.is_eligible_for_ff,
442
+ additions=eligibility_info.spend_additions,
443
+ )
444
+ fee = uint64(removals_amount - additions_amount)
445
+ return bundle_coin_spends, fee
446
+
447
+
448
+ def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem:
449
+ conds = get_conditions_from_spendbundle(spend_bundle, INFINITE_COST, DEFAULT_CONSTANTS, uint32(0))
450
+ bundle_coin_spends, fee = make_bundle_spends_map_and_fee(spend_bundle, conds)
451
+ return MempoolItem(
452
+ spend_bundle=spend_bundle,
453
+ fee=fee,
454
+ conds=conds,
455
+ spend_bundle_name=spend_bundle.name(),
456
+ height_added_to_mempool=TEST_HEIGHT,
457
+ bundle_coin_spends=bundle_coin_spends,
458
+ )
459
+
460
+
461
+ @pytest.mark.anyio
462
+ async def test_empty_spend_bundle() -> None:
463
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
464
+ sb = SpendBundle([], G2Element())
465
+ with pytest.raises(ValidationError, match="INVALID_SPEND_BUNDLE"):
466
+ await mempool_manager.pre_validate_spendbundle(sb)
467
+
468
+
469
+ @pytest.mark.anyio
470
+ async def test_negative_addition_amount() -> None:
471
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
472
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, -1]]
473
+ sb = spend_bundle_from_conditions(conditions)
474
+ with pytest.raises(ValidationError, match="COIN_AMOUNT_NEGATIVE"):
475
+ await mempool_manager.pre_validate_spendbundle(sb)
476
+
477
+
478
+ @pytest.mark.anyio
479
+ async def test_valid_addition_amount() -> None:
480
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
481
+ max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
482
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount]]
483
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, max_amount)
484
+ sb = spend_bundle_from_conditions(conditions, coin)
485
+ # ensure this does not throw
486
+ _ = await mempool_manager.pre_validate_spendbundle(sb)
487
+
488
+
489
+ @pytest.mark.anyio
490
+ async def test_too_big_addition_amount() -> None:
491
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
492
+ max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
493
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount + 1]]
494
+ sb = spend_bundle_from_conditions(conditions)
495
+ with pytest.raises(ValidationError, match="COIN_AMOUNT_EXCEEDS_MAXIMUM"):
496
+ await mempool_manager.pre_validate_spendbundle(sb)
497
+
498
+
499
+ @pytest.mark.anyio
500
+ async def test_duplicate_output() -> None:
501
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
502
+ conditions = [
503
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
504
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
505
+ ]
506
+ sb = spend_bundle_from_conditions(conditions)
507
+ with pytest.raises(ValidationError, match="DUPLICATE_OUTPUT"):
508
+ await mempool_manager.pre_validate_spendbundle(sb)
509
+
510
+
511
+ @pytest.mark.anyio
512
+ async def test_block_cost_exceeds_max() -> None:
513
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
514
+ conditions = []
515
+ for i in range(2400):
516
+ conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i])
517
+ sb = spend_bundle_from_conditions(conditions)
518
+ with pytest.raises(ValidationError, match="BLOCK_COST_EXCEEDS_MAX"):
519
+ await mempool_manager.pre_validate_spendbundle(sb)
520
+
521
+
522
+ @pytest.mark.anyio
523
+ async def test_double_spend_prevalidation() -> None:
524
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
525
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
526
+ sb = spend_bundle_from_conditions(conditions)
527
+ sb_twice = SpendBundle.aggregate([sb, sb])
528
+ with pytest.raises(ValidationError, match="DOUBLE_SPEND"):
529
+ await mempool_manager.pre_validate_spendbundle(sb_twice)
530
+
531
+
532
+ @pytest.mark.anyio
533
+ async def test_minting_coin() -> None:
534
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
535
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT]]
536
+ sb = spend_bundle_from_conditions(conditions)
537
+ _ = await mempool_manager.pre_validate_spendbundle(sb)
538
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT + 1]]
539
+ sb = spend_bundle_from_conditions(conditions)
540
+ with pytest.raises(ValidationError, match="MINTING_COIN"):
541
+ await mempool_manager.pre_validate_spendbundle(sb)
542
+
543
+
544
+ @pytest.mark.anyio
545
+ async def test_reserve_fee_condition() -> None:
546
+ mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
547
+ conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT]]
548
+ sb = spend_bundle_from_conditions(conditions)
549
+ _ = await mempool_manager.pre_validate_spendbundle(sb)
550
+ conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT + 1]]
551
+ sb = spend_bundle_from_conditions(conditions)
552
+ with pytest.raises(ValidationError, match="RESERVE_FEE_CONDITION_FAILED"):
553
+ await mempool_manager.pre_validate_spendbundle(sb)
554
+
555
+
556
+ @pytest.mark.anyio
557
+ async def test_unknown_unspent() -> None:
558
+ async def get_coin_records(_: Collection[bytes32]) -> list[CoinRecord]:
559
+ return []
560
+
561
+ mempool_manager = await instantiate_mempool_manager(get_coin_records)
562
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
563
+ _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
564
+ assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
565
+
566
+
567
+ @pytest.mark.anyio
568
+ async def test_same_sb_twice_with_eligible_coin() -> None:
569
+ mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
570
+ sb1_conditions = [
571
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
572
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
573
+ ]
574
+ sb1 = spend_bundle_from_conditions(sb1_conditions)
575
+ sk = AugSchemeMPL.key_gen(b"5" * 32)
576
+ g1 = sk.get_g1()
577
+ sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
578
+ sb2_conditions = [
579
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3],
580
+ [ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH],
581
+ ]
582
+ sb2 = spend_bundle_from_conditions(sb2_conditions, TEST_COIN2, sig)
583
+ sb = SpendBundle.aggregate([sb1, sb2])
584
+ sb_name = sb.name()
585
+ result = await add_spendbundle(mempool_manager, sb, sb_name)
586
+ expected_cost = uint64(10_236_088)
587
+ assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
588
+ assert mempool_manager.get_spendbundle(sb_name) == sb
589
+ result = await add_spendbundle(mempool_manager, sb, sb_name)
590
+ assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
591
+ assert mempool_manager.get_spendbundle(sb_name) == sb
592
+
593
+
594
+ @pytest.mark.anyio
595
+ async def test_sb_twice_with_eligible_coin_and_different_spends_order() -> None:
596
+ mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
597
+ sb1_conditions = [
598
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
599
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
600
+ ]
601
+ sb1 = spend_bundle_from_conditions(sb1_conditions)
602
+ sk = AugSchemeMPL.key_gen(b"6" * 32)
603
+ g1 = sk.get_g1()
604
+ sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
605
+ sb2_conditions: list[list[Any]] = [
606
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3],
607
+ [ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), IDENTITY_PUZZLE_HASH],
608
+ ]
609
+ sb2 = spend_bundle_from_conditions(sb2_conditions, TEST_COIN2, sig)
610
+ sb3_conditions = [[ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), IDENTITY_PUZZLE_HASH]]
611
+ sb3 = spend_bundle_from_conditions(sb3_conditions, TEST_COIN3, sig)
612
+ sb = SpendBundle.aggregate([sb1, sb2, sb3])
613
+ sb_name = sb.name()
614
+ reordered_sb = SpendBundle.aggregate([sb3, sb1, sb2])
615
+ reordered_sb_name = reordered_sb.name()
616
+ assert mempool_manager.get_spendbundle(sb_name) is None
617
+ assert mempool_manager.get_spendbundle(reordered_sb_name) is None
618
+ result = await add_spendbundle(mempool_manager, sb, sb_name)
619
+ expected_cost = uint64(13_056_132)
620
+ assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
621
+ assert mempool_manager.get_spendbundle(sb_name) == sb
622
+ assert mempool_manager.get_spendbundle(reordered_sb_name) is None
623
+ # This reordered spend bundle should generate conflicting coin spends with
624
+ # the previously added spend bundle
625
+ result = await add_spendbundle(mempool_manager, reordered_sb, reordered_sb_name)
626
+ assert result == (expected_cost, MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
627
+ assert mempool_manager.get_spendbundle(sb_name) == sb
628
+ assert mempool_manager.get_spendbundle(reordered_sb_name) is None
629
+
630
+
631
+ co = ConditionOpcode
632
+ mis = MempoolInclusionStatus
633
+
634
+
635
+ @pytest.mark.anyio
636
+ @pytest.mark.parametrize(
637
+ "opcode,lock_value,expected_status,expected_error",
638
+ [
639
+ # the mempool rules don't allow relative height- or time conditions on
640
+ # ephemeral spends
641
+ # SECONDS RELATIVE
642
+ (co.ASSERT_SECONDS_RELATIVE, -2, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
643
+ (co.ASSERT_SECONDS_RELATIVE, -1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
644
+ (co.ASSERT_SECONDS_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
645
+ (co.ASSERT_SECONDS_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
646
+ (co.ASSERT_SECONDS_RELATIVE, 9, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
647
+ (co.ASSERT_SECONDS_RELATIVE, 10, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
648
+ # HEIGHT RELATIVE
649
+ (co.ASSERT_HEIGHT_RELATIVE, -2, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
650
+ (co.ASSERT_HEIGHT_RELATIVE, -1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
651
+ (co.ASSERT_HEIGHT_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
652
+ (co.ASSERT_HEIGHT_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
653
+ (co.ASSERT_HEIGHT_RELATIVE, 5, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
654
+ (co.ASSERT_HEIGHT_RELATIVE, 6, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
655
+ (co.ASSERT_HEIGHT_RELATIVE, 7, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
656
+ (co.ASSERT_HEIGHT_RELATIVE, 10, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
657
+ (co.ASSERT_HEIGHT_RELATIVE, 11, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
658
+ # BEFORE HEIGHT RELATIVE
659
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -2, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
660
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, -1, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
661
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
662
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
663
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 5, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
664
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 6, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
665
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 7, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
666
+ # HEIGHT ABSOLUTE
667
+ (co.ASSERT_HEIGHT_ABSOLUTE, 4, mis.SUCCESS, None),
668
+ (co.ASSERT_HEIGHT_ABSOLUTE, 5, mis.SUCCESS, None),
669
+ (co.ASSERT_HEIGHT_ABSOLUTE, 6, mis.PENDING, Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
670
+ (co.ASSERT_HEIGHT_ABSOLUTE, 7, mis.PENDING, Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
671
+ # BEFORE HEIGHT ABSOLUTE
672
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 4, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
673
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 5, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
674
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 6, mis.SUCCESS, None),
675
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 7, mis.SUCCESS, None),
676
+ # SECONDS ABSOLUTE
677
+ # Current block timestamp is 10050
678
+ (co.ASSERT_SECONDS_ABSOLUTE, 10049, mis.SUCCESS, None),
679
+ (co.ASSERT_SECONDS_ABSOLUTE, 10050, mis.SUCCESS, None),
680
+ (co.ASSERT_SECONDS_ABSOLUTE, 10051, mis.FAILED, Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
681
+ (co.ASSERT_SECONDS_ABSOLUTE, 10052, mis.FAILED, Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
682
+ # BEFORE SECONDS ABSOLUTE
683
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10049, mis.FAILED, Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
684
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10050, mis.FAILED, Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
685
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10051, mis.SUCCESS, None),
686
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10052, mis.SUCCESS, None),
687
+ ],
688
+ )
689
+ async def test_ephemeral_timelock(
690
+ opcode: ConditionOpcode,
691
+ lock_value: int,
692
+ expected_status: MempoolInclusionStatus,
693
+ expected_error: Optional[Err],
694
+ ) -> None:
695
+ mempool_manager = await instantiate_mempool_manager(
696
+ get_coin_records=get_coin_records_for_test_coins,
697
+ block_height=uint32(5),
698
+ block_timestamp=uint64(10050),
699
+ constants=DEFAULT_CONSTANTS,
700
+ )
701
+
702
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
703
+ created_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))
704
+ sb1 = spend_bundle_from_conditions(conditions)
705
+ sb2 = spend_bundle_from_conditions([[opcode, lock_value]], created_coin)
706
+ # sb spends TEST_COIN and creates created_coin which gets spent too
707
+ sb = SpendBundle.aggregate([sb1, sb2])
708
+ # We shouldn't have a record of this ephemeral coin
709
+ assert await get_coin_records_for_test_coins([created_coin.name()]) == []
710
+ try:
711
+ _, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
712
+ assert (status, error) == (expected_status, expected_error)
713
+ except ValidationError as e:
714
+ assert expected_status == mis.FAILED
715
+ assert expected_error == e.code
716
+
717
+
718
+ def test_optional_min() -> None:
719
+ assert optional_min(uint32(100), None) == uint32(100)
720
+ assert optional_min(None, uint32(100)) == uint32(100)
721
+ assert optional_min(None, None) is None
722
+ assert optional_min(uint32(123), uint32(234)) == uint32(123)
723
+
724
+
725
+ def test_optional_max() -> None:
726
+ assert optional_max(uint32(100), None) == uint32(100)
727
+ assert optional_max(None, uint32(100)) == uint32(100)
728
+ assert optional_max(None, None) is None
729
+ assert optional_max(uint32(123), uint32(234)) == uint32(234)
730
+
731
+
732
+ def mk_item(
733
+ coins: list[Coin],
734
+ *,
735
+ cost: int = 1,
736
+ fee: int = 0,
737
+ assert_height: Optional[int] = None,
738
+ assert_before_height: Optional[int] = None,
739
+ assert_before_seconds: Optional[int] = None,
740
+ ) -> MempoolItem:
741
+ # we don't actually care about the puzzle and solutions for the purpose of
742
+ # can_replace()
743
+ spend_ids = []
744
+ coin_spends = []
745
+ bundle_coin_spends = {}
746
+ for c in coins:
747
+ coin_id = c.name()
748
+ spend_ids.append(coin_id)
749
+ spend = make_spend(c, SerializedProgram.to(None), SerializedProgram.to(None))
750
+ coin_spends.append(spend)
751
+ bundle_coin_spends[coin_id] = BundleCoinSpend(
752
+ coin_spend=spend, eligible_for_dedup=False, eligible_for_fast_forward=False, additions=[]
753
+ )
754
+ spend_bundle = SpendBundle(coin_spends, G2Element())
755
+ conds = make_test_conds(cost=cost, spend_ids=spend_ids)
756
+ return MempoolItem(
757
+ spend_bundle=spend_bundle,
758
+ fee=uint64(fee),
759
+ conds=conds,
760
+ spend_bundle_name=spend_bundle.name(),
761
+ height_added_to_mempool=uint32(0),
762
+ assert_height=None if assert_height is None else uint32(assert_height),
763
+ assert_before_height=None if assert_before_height is None else uint32(assert_before_height),
764
+ assert_before_seconds=None if assert_before_seconds is None else uint64(assert_before_seconds),
765
+ bundle_coin_spends=bundle_coin_spends,
766
+ )
767
+
768
+
769
+ def make_test_coins() -> list[Coin]:
770
+ ret: list[Coin] = []
771
+ for i in range(5):
772
+ ret.append(Coin(height_hash(i), height_hash(i + 100), uint64(i * 100)))
773
+ return ret
774
+
775
+
776
+ coins = make_test_coins()
777
+
778
+
779
+ @pytest.mark.parametrize(
780
+ "existing_items,new_item,expected",
781
+ [
782
+ # FEE RULE
783
+ # the new item must pay a higher fee, in absolute terms
784
+ # replacing exactly the same spend is fine, as long as we increment the fee
785
+ ([mk_item(coins[0:1])], mk_item(coins[0:1]), False),
786
+ # this is less than the minimum fee increase
787
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=9999999), False),
788
+ # this is the minimum fee increase
789
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000), True),
790
+ # FEE RATE RULE
791
+ # the new item must pay a higher fee per cost than the existing item(s)
792
+ # the existing fee rate is 2 and the new fee rate is 2
793
+ ([mk_item(coins[0:1], cost=1000, fee=2000)], mk_item(coins[0:1], cost=10000000, fee=20000000), False),
794
+ # the new rate is >2
795
+ ([mk_item(coins[0:1], cost=1000, fee=2000)], mk_item(coins[0:1], cost=10000000, fee=20000001), True),
796
+ # SUPERSET RULE
797
+ # we can't replace an item spending coin 0 and 1 with an
798
+ # item that just spends coin 0
799
+ ([mk_item(coins[0:2])], mk_item(coins[0:1], fee=10000000), False),
800
+ # or just spends coin 1
801
+ ([mk_item(coins[0:2])], mk_item(coins[1:2], fee=10000000), False),
802
+ # but if we spend the same coins
803
+ ([mk_item(coins[0:2])], mk_item(coins[0:2], fee=10000000), True),
804
+ # or if we spend the same coins with additional coins
805
+ ([mk_item(coins[0:2])], mk_item(coins[0:3], fee=10000000), True),
806
+ # FEE- AND FEE RATE RULES
807
+ # if we're replacing two items, each paying a fee of 100, we need to
808
+ # spend (at least) the same coins and pay at least 10000000 higher fee
809
+ (
810
+ [mk_item(coins[0:1], fee=100, cost=100), mk_item(coins[1:2], fee=100, cost=100)],
811
+ mk_item(coins[0:2], fee=10000200, cost=200),
812
+ True,
813
+ ),
814
+ # if the fee rate is exactly the same, we won't allow the replacement
815
+ (
816
+ [mk_item(coins[0:1], fee=100, cost=100), mk_item(coins[1:2], fee=100, cost=100)],
817
+ mk_item(coins[0:2], fee=10000200, cost=10000200),
818
+ False,
819
+ ),
820
+ # TIMELOCK RULE
821
+ # the new item must not have different time lock than the existing item(s)
822
+ # the assert height time lock condition was introduced in the new item
823
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_height=1000), False),
824
+ # the assert before height time lock condition was introduced in the new item
825
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_before_height=1000), False),
826
+ # the assert before seconds time lock condition was introduced in the new item
827
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000), False),
828
+ # if we don't alter any time locks, we are allowed to replace
829
+ ([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000), True),
830
+ # ASSERT_HEIGHT
831
+ # the assert height time lock condition was removed in the new item
832
+ ([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000), False),
833
+ # different assert height constraint
834
+ ([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=100), False),
835
+ ([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=2000), False),
836
+ # the same assert height is OK
837
+ ([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=1000), True),
838
+ # The new spend just have to match the most restrictive condition
839
+ (
840
+ [mk_item(coins[0:1], assert_height=200), mk_item(coins[1:2], assert_height=400)],
841
+ mk_item(coins[0:2], fee=10000000, assert_height=400),
842
+ True,
843
+ ),
844
+ # ASSERT_BEFORE_HEIGHT
845
+ # the assert before height time lock condition was removed in the new item
846
+ ([mk_item(coins[0:1], assert_before_height=1000)], mk_item(coins[0:1], fee=10000000), False),
847
+ # different assert before height constraint
848
+ (
849
+ [mk_item(coins[0:1], assert_before_height=1000)],
850
+ mk_item(coins[0:1], fee=10000000, assert_before_height=100),
851
+ False,
852
+ ),
853
+ (
854
+ [mk_item(coins[0:1], assert_before_height=1000)],
855
+ mk_item(coins[0:1], fee=10000000, assert_before_height=2000),
856
+ False,
857
+ ),
858
+ # The new spend just have to match the most restrictive condition
859
+ (
860
+ [mk_item(coins[0:1], assert_before_height=200), mk_item(coins[1:2], assert_before_height=400)],
861
+ mk_item(coins[0:2], fee=10000000, assert_before_height=200),
862
+ True,
863
+ ),
864
+ # ASSERT_BEFORE_SECONDS
865
+ # the assert before height time lock condition was removed in the new item
866
+ ([mk_item(coins[0:1], assert_before_seconds=1000)], mk_item(coins[0:1], fee=10000000), False),
867
+ # different assert before seconds constraint
868
+ (
869
+ [mk_item(coins[0:1], assert_before_seconds=1000)],
870
+ mk_item(coins[0:1], fee=10000000, assert_before_seconds=100),
871
+ False,
872
+ ),
873
+ (
874
+ [mk_item(coins[0:1], assert_before_seconds=1000)],
875
+ mk_item(coins[0:1], fee=10000000, assert_before_seconds=2000),
876
+ False,
877
+ ),
878
+ # the assert before height time lock condition was introduced in the new item
879
+ (
880
+ [mk_item(coins[0:1], assert_before_seconds=1000)],
881
+ mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000),
882
+ True,
883
+ ),
884
+ # The new spend just have to match the most restrictive condition
885
+ (
886
+ [mk_item(coins[0:1], assert_before_seconds=200), mk_item(coins[1:2], assert_before_seconds=400)],
887
+ mk_item(coins[0:2], fee=10000000, assert_before_seconds=200),
888
+ True,
889
+ ),
890
+ # MIXED CONDITIONS
891
+ # we can't replace an assert_before_seconds with assert_before_height
892
+ (
893
+ [mk_item(coins[0:1], assert_before_seconds=1000)],
894
+ mk_item(coins[0:1], fee=10000000, assert_before_height=2000),
895
+ False,
896
+ ),
897
+ # we added another condition
898
+ (
899
+ [mk_item(coins[0:1], assert_before_seconds=1000)],
900
+ mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000, assert_height=200),
901
+ False,
902
+ ),
903
+ # we removed assert before height
904
+ (
905
+ [mk_item(coins[0:1], assert_height=200, assert_before_height=1000)],
906
+ mk_item(coins[0:1], fee=10000000, assert_height=200),
907
+ False,
908
+ ),
909
+ ],
910
+ )
911
+ def test_can_replace(existing_items: list[MempoolItem], new_item: MempoolItem, expected: bool) -> None:
912
+ removals = {c.name() for c in new_item.spend_bundle.removals()}
913
+ assert can_replace(existing_items, removals, new_item) == expected
914
+
915
+
916
+ @pytest.mark.anyio
917
+ async def test_get_items_not_in_filter() -> None:
918
+ mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
919
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
920
+ sb1, sb1_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions)
921
+ conditions2 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
922
+ sb2, sb2_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions2, TEST_COIN2)
923
+ conditions3 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
924
+ sb3, sb3_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions3, TEST_COIN3)
925
+
926
+ # Don't filter anything
927
+ empty_filter = PyBIP158([])
928
+ result = mempool_manager.get_items_not_in_filter(empty_filter)
929
+ assert result == [sb3, sb2, sb1]
930
+
931
+ # Filter everything
932
+ full_filter = PyBIP158([bytearray(sb1_name), bytearray(sb2_name), bytearray(sb3_name)])
933
+ result = mempool_manager.get_items_not_in_filter(full_filter)
934
+ assert result == []
935
+
936
+ # Negative limit
937
+ with pytest.raises(AssertionError):
938
+ mempool_manager.get_items_not_in_filter(empty_filter, limit=-1)
939
+
940
+ # Zero limit
941
+ with pytest.raises(AssertionError):
942
+ mempool_manager.get_items_not_in_filter(empty_filter, limit=0)
943
+
944
+ # Filter only one of the spend bundles
945
+ sb3_filter = PyBIP158([bytearray(sb3_name)])
946
+
947
+ # With a limit of one, sb2 has the highest FPC
948
+ result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=1)
949
+ assert result == [sb2]
950
+
951
+ # With a higher limit, all bundles aside from sb3 get included
952
+ result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=5)
953
+ assert result == [sb2, sb1]
954
+
955
+ # Filter two of the spend bundles
956
+ sb2_and_3_filter = PyBIP158([bytearray(sb2_name), bytearray(sb3_name)])
957
+ result = mempool_manager.get_items_not_in_filter(sb2_and_3_filter)
958
+ assert result == [sb1]
959
+
960
+
961
+ @pytest.mark.anyio
962
+ async def test_total_mempool_fees() -> None:
963
+ coin_records: dict[bytes32, CoinRecord] = {}
964
+
965
+ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
966
+ ret: list[CoinRecord] = []
967
+ for name in coin_ids:
968
+ r = coin_records.get(name)
969
+ if r is not None:
970
+ ret.append(r)
971
+ return ret
972
+
973
+ mempool_manager = await instantiate_mempool_manager(get_coin_records)
974
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
975
+
976
+ # the limit of total fees in the mempool is 2^63
977
+ # the limit per mempool item is 2^50, that lets us add 8192 items with the
978
+ # maximum amount of fee before reaching the total mempool limit
979
+ amount = uint64(2**50)
980
+ total_fee = 0
981
+ for i in range(8192):
982
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
983
+ coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
984
+ amount = uint64(amount - 1)
985
+ # the fee is 1 less than the amount because we create a coin of 1 mojo
986
+ total_fee += amount
987
+ _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
988
+ assert result[1] == MempoolInclusionStatus.SUCCESS
989
+ assert mempool_manager.mempool.total_mempool_fees() == total_fee
990
+
991
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
992
+ coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
993
+ _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
994
+ assert result[1] == MempoolInclusionStatus.FAILED
995
+ assert result[2] == Err.INVALID_BLOCK_FEE_AMOUNT
996
+
997
+
998
+ @pytest.mark.parametrize("reverse_tx_order", [True, False])
999
+ @pytest.mark.anyio
1000
+ async def test_create_bundle_from_mempool(reverse_tx_order: bool) -> None:
1001
+ async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
1002
+ assert False # pragma: no cover
1003
+
1004
+ async def make_coin_spends(coins: list[Coin], *, high_fees: bool = True) -> list[CoinSpend]:
1005
+ spends_list = []
1006
+ for i in range(0, len(coins)):
1007
+ coin_spend = make_spend(
1008
+ coins[i],
1009
+ IDENTITY_PUZZLE,
1010
+ Program.to(
1011
+ [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i if high_fees else (coins[i].amount - 1)]]
1012
+ ),
1013
+ )
1014
+ spends_list.append(coin_spend)
1015
+ return spends_list
1016
+
1017
+ async def send_spends_to_mempool(coin_spends: list[CoinSpend]) -> None:
1018
+ g2 = G2Element()
1019
+ for cs in coin_spends:
1020
+ sb = SpendBundle([cs], g2)
1021
+ result = await add_spendbundle(mempool_manager, sb, sb.name())
1022
+ assert result[1] == MempoolInclusionStatus.SUCCESS
1023
+
1024
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(2000000000, 2000002200)))
1025
+ high_rate_spends = await make_coin_spends(coins[0:2200])
1026
+ low_rate_spends = await make_coin_spends(coins[2200:2400], high_fees=False)
1027
+ spends = low_rate_spends + high_rate_spends if reverse_tx_order else high_rate_spends + low_rate_spends
1028
+ await send_spends_to_mempool(spends)
1029
+ assert mempool_manager.peak is not None
1030
+ result = await mempool_manager.create_bundle_from_mempool(
1031
+ mempool_manager.peak.header_hash, get_unspent_lineage_info_for_puzzle_hash
1032
+ )
1033
+ assert result is not None
1034
+ # Make sure we filled the block with only high rate spends
1035
+ assert len([s for s in high_rate_spends if s in result[0].coin_spends]) == len(result[0].coin_spends)
1036
+ assert len([s for s in low_rate_spends if s in result[0].coin_spends]) == 0
1037
+
1038
+
1039
+ @pytest.mark.parametrize("num_skipped_items", [PRIORITY_TX_THRESHOLD, MAX_SKIPPED_ITEMS])
1040
+ @pytest.mark.anyio
1041
+ async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, caplog: pytest.LogCaptureFixture) -> None:
1042
+ """
1043
+ This test exercises the path where an item's inclusion would exceed the
1044
+ maximum cumulative cost, so it gets skipped as a result.
1045
+
1046
+ NOTE:
1047
+ 1. After PRIORITY_TX_THRESHOLD, we skip items with eligible coins.
1048
+ 2. After skipping MAX_SKIPPED_ITEMS, we stop processing further items.
1049
+ """
1050
+
1051
+ async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
1052
+ assert False # pragma: no cover
1053
+
1054
+ MAX_BLOCK_CLVM_COST = 550_000_000
1055
+
1056
+ mempool_manager, coins = await setup_mempool_with_coins(
1057
+ coin_amounts=list(range(1_000_000_000, 1_000_000_030)),
1058
+ max_block_clvm_cost=MAX_BLOCK_CLVM_COST,
1059
+ max_tx_clvm_cost=uint64(MAX_BLOCK_CLVM_COST),
1060
+ mempool_block_buffer=20,
1061
+ )
1062
+
1063
+ async def make_and_send_big_cost_sb(coin: Coin) -> None:
1064
+ """
1065
+ Creates a spend bundle with a big enough cost that gets it close to the
1066
+ maximum block clvm cost limit.
1067
+ """
1068
+ conditions = []
1069
+ sk = AugSchemeMPL.key_gen(b"7" * 32)
1070
+ g1 = sk.get_g1()
1071
+ sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
1072
+ aggsig = G2Element()
1073
+ # Let's get as close to `MAX_BLOCK_CLVM_COST` (550_000_000) as possible.
1074
+ # We start by accounting for execution cost
1075
+ spend_bundle_cost = 44
1076
+ # And then the created coin
1077
+ conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - 10_000_000])
1078
+ TEST_CREATE_COIN_SPEND_BYTESIZE = 93
1079
+ TEST_CREATE_COIN_CONDITION_COST = (
1080
+ ConditionCost.CREATE_COIN.value + TEST_CREATE_COIN_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1081
+ )
1082
+ spend_bundle_cost += TEST_CREATE_COIN_CONDITION_COST
1083
+ # We're using agg sig conditions to increase the spend bundle's cost
1084
+ # and reach our target cost.
1085
+ TEST_AGG_SIG_SPEND_BYTESIZE = 88
1086
+ TEST_AGGSIG_CONDITION_COST = (
1087
+ ConditionCost.AGG_SIG.value + TEST_AGG_SIG_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1088
+ )
1089
+ while spend_bundle_cost + TEST_AGGSIG_CONDITION_COST < MAX_BLOCK_CLVM_COST:
1090
+ conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH])
1091
+ aggsig += sig
1092
+ spend_bundle_cost += TEST_AGGSIG_CONDITION_COST
1093
+ # We now have a spend bundle with a big enough cost that gets it close to the limit
1094
+ _, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coin, aggsig)
1095
+ cost, status, _ = res
1096
+ assert status == MempoolInclusionStatus.SUCCESS
1097
+ assert cost == spend_bundle_cost
1098
+
1099
+ # Create the spend bundles with a big enough cost that they get close to the limit
1100
+ for i in range(num_skipped_items):
1101
+ await make_and_send_big_cost_sb(coins[i])
1102
+
1103
+ # Create a spend bundle with a relatively smaller cost.
1104
+ # Combined with a big cost spend bundle, we'd exceed the maximum block clvm cost
1105
+ sb2_coin = coins[num_skipped_items]
1106
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, sb2_coin.amount - 200_000]]
1107
+ sb2, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, sb2_coin)
1108
+ assert res[1] == MempoolInclusionStatus.SUCCESS
1109
+ sb2_addition = Coin(sb2_coin.name(), IDENTITY_PUZZLE_HASH, uint64(sb2_coin.amount - 200_000))
1110
+ # Create 4 extra spend bundles with smaller FPC and smaller costs
1111
+ extra_sbs = []
1112
+ extra_additions = []
1113
+ sk = AugSchemeMPL.key_gen(b"8" * 32)
1114
+ g1 = sk.get_g1()
1115
+ sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1116
+ for i in range(num_skipped_items + 1, num_skipped_items + 5):
1117
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coins[i].amount - 30_000]]
1118
+ # Make the first of these without eligible coins
1119
+ if i == num_skipped_items + 1:
1120
+ conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"])
1121
+ aggsig = sig
1122
+ else:
1123
+ aggsig = G2Element()
1124
+ sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
1125
+ extra_sbs.append(sb)
1126
+ coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount - 30_000))
1127
+ extra_additions.append(coin)
1128
+ assert res[1] == MempoolInclusionStatus.SUCCESS
1129
+
1130
+ assert mempool_manager.peak is not None
1131
+ caplog.set_level(logging.DEBUG)
1132
+ result = await mempool_manager.create_bundle_from_mempool(
1133
+ mempool_manager.peak.header_hash, get_unspent_lineage_info_for_puzzle_hash
1134
+ )
1135
+ assert result is not None
1136
+ agg, additions = result
1137
+ skipped_due_to_eligible_coins = sum(
1138
+ 1
1139
+ for line in caplog.text.split("\n")
1140
+ if "DEBUG Exception while checking a mempool item for deduplication: Skipping transaction with eligible coin(s)"
1141
+ in line
1142
+ )
1143
+ if num_skipped_items == PRIORITY_TX_THRESHOLD:
1144
+ # We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
1145
+ # so the first from the extra 4 (the one without eligible coins) went in,
1146
+ # and the other 3 were skipped (they have eligible coins)
1147
+ assert skipped_due_to_eligible_coins == 3
1148
+ assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
1149
+ assert additions == [sb2_addition, extra_additions[0]]
1150
+ assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
1151
+ elif num_skipped_items == MAX_SKIPPED_ITEMS:
1152
+ # We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
1153
+ # we didn't process any of the extra items
1154
+ assert skipped_due_to_eligible_coins == 0
1155
+ assert agg == SpendBundle.aggregate([sb2])
1156
+ assert additions == [sb2_addition]
1157
+ assert agg.removals() == [sb2_coin]
1158
+ else:
1159
+ raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
1160
+
1161
+
1162
+ @pytest.mark.parametrize(
1163
+ "opcode,arg,expect_eviction, expect_limit",
1164
+ [
1165
+ # current height: 10 current_time: 10000
1166
+ # we step the chain forward 1 block and 19 seconds
1167
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10001, True, None),
1168
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10019, True, None),
1169
+ (co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10020, False, 10020),
1170
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 11, True, None),
1171
+ (co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 12, False, 12),
1172
+ # the coin was created at height: 5 timestamp: 9900
1173
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 6, True, None),
1174
+ (co.ASSERT_BEFORE_HEIGHT_RELATIVE, 7, False, 5 + 7),
1175
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 119, True, None),
1176
+ (co.ASSERT_BEFORE_SECONDS_RELATIVE, 120, False, 9900 + 120),
1177
+ ],
1178
+ )
1179
+ @pytest.mark.anyio
1180
+ async def test_assert_before_expiration(
1181
+ opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: Optional[int]
1182
+ ) -> None:
1183
+ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
1184
+ all_coins = {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}
1185
+ ret: list[CoinRecord] = []
1186
+ for name in coin_ids:
1187
+ r = all_coins.get(name)
1188
+ if r is not None:
1189
+ ret.append(r)
1190
+ return ret
1191
+
1192
+ mempool_manager = await instantiate_mempool_manager(
1193
+ get_coin_records,
1194
+ block_height=uint32(10),
1195
+ block_timestamp=uint64(10000),
1196
+ constants=DEFAULT_CONSTANTS,
1197
+ )
1198
+
1199
+ bundle = spend_bundle_from_conditions(
1200
+ [
1201
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1202
+ [opcode, arg],
1203
+ ],
1204
+ coin=TEST_COIN,
1205
+ )
1206
+ bundle_name = bundle.name()
1207
+ assert (await add_spendbundle(mempool_manager, bundle, bundle_name))[1] == mis.SUCCESS
1208
+ # make sure the spend was added correctly
1209
+ assert mempool_manager.get_spendbundle(bundle_name) == bundle
1210
+
1211
+ block_record = create_test_block_record(height=uint32(11), timestamp=uint64(10019))
1212
+ await mempool_manager.new_peak(block_record, None)
1213
+ invariant_check_mempool(mempool_manager.mempool)
1214
+
1215
+ still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
1216
+ assert still_in_pool != expect_eviction
1217
+ if still_in_pool:
1218
+ assert expect_limit is not None
1219
+ item = mempool_manager.get_mempool_item(bundle_name)
1220
+ assert item is not None
1221
+ if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
1222
+ assert item.assert_before_seconds == expect_limit
1223
+ elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
1224
+ assert item.assert_before_height == expect_limit
1225
+ else:
1226
+ assert False
1227
+
1228
+
1229
+ def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = False) -> SpendBundle:
1230
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, uint64(coin.amount - fee)]]
1231
+ sig = G2Element()
1232
+ if not eligible_spend:
1233
+ sk = AugSchemeMPL.key_gen(b"2" * 32)
1234
+ g1 = sk.get_g1()
1235
+ sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1236
+ conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"])
1237
+ return spend_bundle_from_conditions(conditions, coin, sig)
1238
+
1239
+
1240
+ async def send_spendbundle(
1241
+ mempool_manager: MempoolManager,
1242
+ sb: SpendBundle,
1243
+ expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
1244
+ ) -> None:
1245
+ result = await add_spendbundle(mempool_manager, sb, sb.name())
1246
+ assert (result[1], result[2]) == expected_result
1247
+
1248
+
1249
+ async def make_and_send_spendbundle(
1250
+ mempool_manager: MempoolManager,
1251
+ coin: Coin,
1252
+ *,
1253
+ fee: int = 0,
1254
+ expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
1255
+ ) -> SpendBundle:
1256
+ sb = make_test_spendbundle(coin, fee=fee)
1257
+ await send_spendbundle(mempool_manager, sb, expected_result)
1258
+ return sb
1259
+
1260
+
1261
+ def assert_sb_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
1262
+ assert sb == mempool_manager.get_spendbundle(sb.name())
1263
+
1264
+
1265
+ def assert_sb_not_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
1266
+ assert mempool_manager.get_spendbundle(sb.name()) is None
1267
+
1268
+
1269
+ @pytest.mark.anyio
1270
+ async def test_insufficient_fee_increase() -> None:
1271
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1272
+ sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1273
+ sb1_2 = await make_and_send_spendbundle(
1274
+ mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1275
+ )
1276
+ # The old spendbundle must stay
1277
+ assert_sb_in_pool(mempool_manager, sb1_1)
1278
+ assert_sb_not_in_pool(mempool_manager, sb1_2)
1279
+
1280
+
1281
+ @pytest.mark.anyio
1282
+ async def test_sufficient_fee_increase() -> None:
1283
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1284
+ sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1285
+ sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
1286
+ # sb1_1 gets replaced with sb1_2
1287
+ assert_sb_not_in_pool(mempool_manager, sb1_1)
1288
+ assert_sb_in_pool(mempool_manager, sb1_2)
1289
+
1290
+
1291
+ @pytest.mark.anyio
1292
+ async def test_superset() -> None:
1293
+ # Aggregated spendbundle sb12 replaces sb1 since it spends a superset
1294
+ # of coins spent in sb1
1295
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1296
+ sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1297
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
1298
+ sb12 = SpendBundle.aggregate([sb2, sb1])
1299
+ await send_spendbundle(mempool_manager, sb12)
1300
+ assert_sb_in_pool(mempool_manager, sb12)
1301
+ assert_sb_not_in_pool(mempool_manager, sb1)
1302
+
1303
+
1304
+ @pytest.mark.anyio
1305
+ async def test_superset_violation() -> None:
1306
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1307
+ sb1 = make_test_spendbundle(coins[0])
1308
+ sb2 = make_test_spendbundle(coins[1])
1309
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1310
+ await send_spendbundle(mempool_manager, sb12)
1311
+ assert_sb_in_pool(mempool_manager, sb12)
1312
+ # sb23 must not replace existing sb12 as the former does not spend all
1313
+ # coins that are spent in the latter (specifically, the first coin)
1314
+ sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
1315
+ sb23 = SpendBundle.aggregate([sb2, sb3])
1316
+ await send_spendbundle(
1317
+ mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1318
+ )
1319
+ assert_sb_in_pool(mempool_manager, sb12)
1320
+ assert_sb_not_in_pool(mempool_manager, sb23)
1321
+
1322
+
1323
+ @pytest.mark.anyio
1324
+ async def test_total_fpc_decrease() -> None:
1325
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1326
+ sb1 = make_test_spendbundle(coins[0])
1327
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1328
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1329
+ await send_spendbundle(mempool_manager, sb12)
1330
+ sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1331
+ assert_sb_in_pool(mempool_manager, sb12)
1332
+ assert_sb_in_pool(mempool_manager, sb3)
1333
+ # sb1234 should not be in pool as it decreases total fees per cost
1334
+ sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
1335
+ sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1336
+ await send_spendbundle(
1337
+ mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1338
+ )
1339
+ assert_sb_not_in_pool(mempool_manager, sb1234)
1340
+
1341
+
1342
+ @pytest.mark.anyio
1343
+ async def test_sufficient_total_fpc_increase() -> None:
1344
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1345
+ sb1 = make_test_spendbundle(coins[0])
1346
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1347
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1348
+ await send_spendbundle(mempool_manager, sb12)
1349
+ sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1350
+ assert_sb_in_pool(mempool_manager, sb12)
1351
+ assert_sb_in_pool(mempool_manager, sb3)
1352
+ # sb1234 has a higher fee per cost than its conflicts and should get
1353
+ # into the mempool
1354
+ sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
1355
+ sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1356
+ await send_spendbundle(mempool_manager, sb1234)
1357
+ assert_sb_in_pool(mempool_manager, sb1234)
1358
+ assert_sb_not_in_pool(mempool_manager, sb12)
1359
+ assert_sb_not_in_pool(mempool_manager, sb3)
1360
+
1361
+
1362
+ @pytest.mark.anyio
1363
+ async def test_replace_with_extra_eligible_coin() -> None:
1364
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1365
+ sb1234 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(4)])
1366
+ await send_spendbundle(mempool_manager, sb1234)
1367
+ assert_sb_in_pool(mempool_manager, sb1234)
1368
+ # Replace sb1234 with sb1234_2 which spends an eligible coin additionally
1369
+ eligible_sb = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE, eligible_spend=True)
1370
+ sb1234_2 = SpendBundle.aggregate([sb1234, eligible_sb])
1371
+ await send_spendbundle(mempool_manager, sb1234_2)
1372
+ assert_sb_not_in_pool(mempool_manager, sb1234)
1373
+ assert_sb_in_pool(mempool_manager, sb1234_2)
1374
+
1375
+
1376
+ @pytest.mark.anyio
1377
+ async def test_replacing_one_with_an_eligible_coin() -> None:
1378
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1379
+ sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1380
+ eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1381
+ sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1382
+ await send_spendbundle(mempool_manager, sb123e)
1383
+ assert_sb_in_pool(mempool_manager, sb123e)
1384
+ # Replace sb123e with sb123e4
1385
+ sb4 = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE)
1386
+ sb123e4 = SpendBundle.aggregate([sb123e, sb4])
1387
+ await send_spendbundle(mempool_manager, sb123e4)
1388
+ assert_sb_not_in_pool(mempool_manager, sb123e)
1389
+ assert_sb_in_pool(mempool_manager, sb123e4)
1390
+
1391
+
1392
+ @pytest.mark.parametrize("amount", [0, 1])
1393
+ def test_run_for_cost(amount: int) -> None:
1394
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, amount]]
1395
+ solution = SerializedProgram.to(conditions)
1396
+ cost = run_for_cost(IDENTITY_PUZZLE, solution, additions_count=1, max_cost=uint64(10000000))
1397
+ assert cost == uint64(1800044)
1398
+
1399
+
1400
+ def test_run_for_cost_max_cost() -> None:
1401
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
1402
+ solution = SerializedProgram.to(conditions)
1403
+ with pytest.raises(ValueError, match="cost exceeded"):
1404
+ run_for_cost(IDENTITY_PUZZLE, solution, additions_count=1, max_cost=uint64(43))
1405
+
1406
+
1407
+ def test_dedup_info_nothing_to_do() -> None:
1408
+ # No eligible coins, nothing to deduplicate, item gets considered normally
1409
+
1410
+ sk = AugSchemeMPL.key_gen(b"3" * 32)
1411
+ g1 = sk.get_g1()
1412
+ sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1413
+
1414
+ conditions = [
1415
+ [ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"],
1416
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1417
+ ]
1418
+ sb = spend_bundle_from_conditions(conditions, TEST_COIN, sig)
1419
+ mempool_item = mempool_item_from_spendbundle(sb)
1420
+ eligible_coin_spends = EligibleCoinSpends()
1421
+ unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
1422
+ bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
1423
+ )
1424
+ assert unique_coin_spends == sb.coin_spends
1425
+ assert cost_saving == 0
1426
+ assert unique_additions == [Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))]
1427
+ assert eligible_coin_spends == EligibleCoinSpends()
1428
+
1429
+
1430
+ def test_dedup_info_eligible_1st_time() -> None:
1431
+ # Eligible coin encountered for the first time
1432
+ conditions = [
1433
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1434
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
1435
+ ]
1436
+ sb = spend_bundle_from_conditions(conditions, TEST_COIN)
1437
+ mempool_item = mempool_item_from_spendbundle(sb)
1438
+ assert mempool_item.conds is not None
1439
+ eligible_coin_spends = EligibleCoinSpends()
1440
+ solution = SerializedProgram.to(conditions)
1441
+ unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
1442
+ bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
1443
+ )
1444
+ assert unique_coin_spends == sb.coin_spends
1445
+ assert cost_saving == 0
1446
+ assert set(unique_additions) == {
1447
+ Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1)),
1448
+ Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(2)),
1449
+ }
1450
+ assert eligible_coin_spends == EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=solution, cost=None)})
1451
+
1452
+
1453
+ def test_dedup_info_eligible_but_different_solution() -> None:
1454
+ # Eligible coin but different solution from the one we encountered
1455
+ initial_conditions = [
1456
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1457
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
1458
+ ]
1459
+ initial_solution = SerializedProgram.to(initial_conditions)
1460
+ eligible_coin_spends = EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=None)})
1461
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
1462
+ sb = spend_bundle_from_conditions(conditions, TEST_COIN)
1463
+ mempool_item = mempool_item_from_spendbundle(sb)
1464
+ with pytest.raises(ValueError, match="Solution is different from what we're deduplicating on"):
1465
+ eligible_coin_spends.get_deduplication_info(
1466
+ bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
1467
+ )
1468
+
1469
+
1470
+ def test_dedup_info_eligible_2nd_time_and_another_1st_time() -> None:
1471
+ # Eligible coin encountered a second time, and another for the first time
1472
+ initial_conditions = [
1473
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1474
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
1475
+ ]
1476
+ initial_solution = SerializedProgram.to(initial_conditions)
1477
+ eligible_coin_spends = EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=None)})
1478
+ sb1 = spend_bundle_from_conditions(initial_conditions, TEST_COIN)
1479
+ second_conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
1480
+ second_solution = SerializedProgram.to(second_conditions)
1481
+ sb2 = spend_bundle_from_conditions(second_conditions, TEST_COIN2)
1482
+ sb = SpendBundle.aggregate([sb1, sb2])
1483
+ mempool_item = mempool_item_from_spendbundle(sb)
1484
+ assert mempool_item.conds is not None
1485
+ unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
1486
+ bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
1487
+ )
1488
+ # Only the eligible one that we encountered more than once gets deduplicated
1489
+ assert unique_coin_spends == sb2.coin_spends
1490
+ saved_cost = uint64(3600044)
1491
+ assert cost_saving == saved_cost
1492
+ assert unique_additions == [Coin(TEST_COIN_ID2, IDENTITY_PUZZLE_HASH, uint64(3))]
1493
+ # The coin we encountered a second time has its cost and additions properly updated
1494
+ # The coin we encountered for the first time gets cost None and an empty set of additions
1495
+ expected_eligible_spends = EligibleCoinSpends(
1496
+ {
1497
+ TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=saved_cost),
1498
+ TEST_COIN_ID2: DedupCoinSpend(solution=second_solution, cost=None),
1499
+ }
1500
+ )
1501
+ assert eligible_coin_spends == expected_eligible_spends
1502
+
1503
+
1504
+ def test_dedup_info_eligible_3rd_time_another_2nd_time_and_one_non_eligible() -> None:
1505
+ # Eligible coin encountered a third time, another for the second time and one non eligible
1506
+ initial_conditions = [
1507
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1508
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
1509
+ ]
1510
+ initial_solution = SerializedProgram.to(initial_conditions)
1511
+ second_conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
1512
+ second_solution = SerializedProgram.to(second_conditions)
1513
+ saved_cost = uint64(3600044)
1514
+ eligible_coin_spends = EligibleCoinSpends(
1515
+ {
1516
+ TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=saved_cost),
1517
+ TEST_COIN_ID2: DedupCoinSpend(solution=second_solution, cost=None),
1518
+ }
1519
+ )
1520
+ sb1 = spend_bundle_from_conditions(initial_conditions, TEST_COIN)
1521
+ sb2 = spend_bundle_from_conditions(second_conditions, TEST_COIN2)
1522
+ sk = AugSchemeMPL.key_gen(b"4" * 32)
1523
+ g1 = sk.get_g1()
1524
+ sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1525
+ sb3_conditions = [
1526
+ [ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"],
1527
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 4],
1528
+ ]
1529
+ sb3 = spend_bundle_from_conditions(sb3_conditions, TEST_COIN3, sig)
1530
+ sb = SpendBundle.aggregate([sb1, sb2, sb3])
1531
+ mempool_item = mempool_item_from_spendbundle(sb)
1532
+ assert mempool_item.conds is not None
1533
+ unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
1534
+ bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
1535
+ )
1536
+ assert unique_coin_spends == sb3.coin_spends
1537
+ saved_cost2 = uint64(1800044)
1538
+ assert cost_saving == saved_cost + saved_cost2
1539
+ assert unique_additions == [Coin(TEST_COIN_ID3, IDENTITY_PUZZLE_HASH, uint64(4))]
1540
+ expected_eligible_spends = EligibleCoinSpends(
1541
+ {
1542
+ TEST_COIN_ID: DedupCoinSpend(initial_solution, saved_cost),
1543
+ TEST_COIN_ID2: DedupCoinSpend(second_solution, saved_cost2),
1544
+ }
1545
+ )
1546
+ assert eligible_coin_spends == expected_eligible_spends
1547
+
1548
+
1549
+ @pytest.mark.anyio
1550
+ @pytest.mark.parametrize("new_height_step", [1, 2, -1])
1551
+ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(new_height_step: int) -> None:
1552
+ """
1553
+ This test makes sure all mempool items that spend a coin (in different ways)
1554
+ that shows up as spent in a block, get removed properly.
1555
+ NOTE: `new_height_step` parameter allows us to cover both the optimized and
1556
+ the reorg code paths
1557
+ """
1558
+ new_height = uint32(TEST_HEIGHT + new_height_step)
1559
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(100))
1560
+ coin_id = coin.name()
1561
+ test_coin_records = {coin_id: CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))}
1562
+
1563
+ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
1564
+ ret: list[CoinRecord] = []
1565
+ for name in coin_ids:
1566
+ r = test_coin_records.get(name)
1567
+ if r is not None:
1568
+ ret.append(r)
1569
+ return ret
1570
+
1571
+ mempool_manager = await instantiate_mempool_manager(get_coin_records)
1572
+ # Create a bunch of mempool items that spend the coin in different ways
1573
+ for i in range(3):
1574
+ _, _, result = await generate_and_add_spendbundle(
1575
+ mempool_manager, [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i]], coin
1576
+ )
1577
+ assert result[1] == MempoolInclusionStatus.SUCCESS
1578
+ assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 3
1579
+ assert mempool_manager.mempool.size() == 3
1580
+ assert len(list(mempool_manager.mempool.items_by_feerate())) == 3
1581
+ # Setup a new peak where the incoming block has spent the coin
1582
+ # Mark this coin as spent
1583
+ test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
1584
+ block_record = create_test_block_record(height=new_height)
1585
+ await mempool_manager.new_peak(block_record, [coin_id])
1586
+ invariant_check_mempool(mempool_manager.mempool)
1587
+ # As the coin was a spend in all the mempool items we had, nothing should be left now
1588
+ assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 0
1589
+ assert mempool_manager.mempool.size() == 0
1590
+ assert len(list(mempool_manager.mempool.items_by_feerate())) == 0
1591
+
1592
+
1593
+ @pytest.mark.anyio
1594
+ async def test_bundle_coin_spends() -> None:
1595
+ # This tests the construction of bundle_coin_spends map for mempool items
1596
+ # We're creating sb123e with 4 coins, one of them being eligible
1597
+ mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000005)))
1598
+ sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1599
+ eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1600
+ sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1601
+ await send_spendbundle(mempool_manager, sb123e)
1602
+ mi123e = mempool_manager.get_mempool_item(sb123e.name())
1603
+ assert mi123e is not None
1604
+ for i in range(3):
1605
+ assert mi123e.bundle_coin_spends[coins[i].name()] == BundleCoinSpend(
1606
+ coin_spend=sb123.coin_spends[i],
1607
+ eligible_for_dedup=False,
1608
+ eligible_for_fast_forward=False,
1609
+ additions=[Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, coins[i].amount)],
1610
+ )
1611
+ assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
1612
+ coin_spend=eligible_sb.coin_spends[0],
1613
+ eligible_for_dedup=True,
1614
+ eligible_for_fast_forward=False,
1615
+ additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
1616
+ )
1617
+
1618
+
1619
+ @pytest.mark.anyio
1620
+ async def test_identical_spend_aggregation_e2e(
1621
+ simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str
1622
+ ) -> None:
1623
+ def get_sb_names_by_coin_id(
1624
+ full_node_api: FullNodeSimulator,
1625
+ spent_coin_id: bytes32,
1626
+ ) -> set[bytes32]:
1627
+ return {
1628
+ i.spend_bundle_name
1629
+ for i in full_node_api.full_node.mempool_manager.mempool.get_items_by_coin_id(spent_coin_id)
1630
+ }
1631
+
1632
+ async def send_to_mempool(
1633
+ full_node: FullNodeSimulator, spend_bundle: SpendBundle, *, expecting_conflict: bool = False
1634
+ ) -> None:
1635
+ res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle))
1636
+ assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
1637
+ res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
1638
+ if expecting_conflict:
1639
+ assert res_parsed.status == MempoolInclusionStatus.PENDING.value
1640
+ assert res_parsed.error == "MEMPOOL_CONFLICT"
1641
+ else:
1642
+ assert res_parsed.status == MempoolInclusionStatus.SUCCESS.value
1643
+
1644
+ async def farm_a_block(full_node_api: FullNodeSimulator, wallet_node: WalletNode, ph: bytes32) -> None:
1645
+ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
1646
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
1647
+
1648
+ async def make_setup_and_coins(
1649
+ full_node_api: FullNodeSimulator, wallet_node: WalletNode
1650
+ ) -> tuple[Wallet, list[WalletCoinRecord], bytes32]:
1651
+ wallet = wallet_node.wallet_state_manager.main_wallet
1652
+ ph = await wallet.get_new_puzzlehash()
1653
+ phs = [await wallet.get_new_puzzlehash() for _ in range(3)]
1654
+ for _ in range(2):
1655
+ await farm_a_block(full_node_api, wallet_node, ph)
1656
+ other_recipients = [Payment(puzzle_hash=p, amount=uint64(200), memos=[]) for p in phs[1:]]
1657
+ async with wallet.wallet_state_manager.new_action_scope(
1658
+ DEFAULT_TX_CONFIG, push=False, sign=True
1659
+ ) as action_scope:
1660
+ await wallet.generate_signed_transaction(uint64(200), phs[0], action_scope, primaries=other_recipients)
1661
+ [tx] = action_scope.side_effects.transactions
1662
+ assert tx.spend_bundle is not None
1663
+ await send_to_mempool(full_node_api, tx.spend_bundle)
1664
+ await farm_a_block(full_node_api, wallet_node, ph)
1665
+ coins = list(await wallet_node.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(1))
1666
+ # Two blocks farmed plus 3 transactions
1667
+ assert len(coins) == 7
1668
+ return (wallet, coins, ph)
1669
+
1670
+ [[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
1671
+ server = full_node_api.full_node.server
1672
+ await wallet_server.start_client(PeerInfo(self_hostname, server.get_port()), None)
1673
+ wallet, coins, ph = await make_setup_and_coins(full_node_api, wallet_node)
1674
+
1675
+ # Make sure spending AB then BC would generate a conflict for the latter
1676
+ async with wallet.wallet_state_manager.new_action_scope(
1677
+ DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
1678
+ ) as action_scope:
1679
+ await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[0].coin})
1680
+ await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[1].coin})
1681
+ await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[2].coin})
1682
+ [tx_a, tx_b, tx_c] = action_scope.side_effects.transactions
1683
+ assert tx_a.spend_bundle is not None
1684
+ assert tx_b.spend_bundle is not None
1685
+ assert tx_c.spend_bundle is not None
1686
+ ab_bundle = SpendBundle.aggregate([tx_a.spend_bundle, tx_b.spend_bundle])
1687
+ await send_to_mempool(full_node_api, ab_bundle)
1688
+ # BC should conflict here (on B)
1689
+ bc_bundle = SpendBundle.aggregate([tx_b.spend_bundle, tx_c.spend_bundle])
1690
+ await send_to_mempool(full_node_api, bc_bundle, expecting_conflict=True)
1691
+ await farm_a_block(full_node_api, wallet_node, ph)
1692
+
1693
+ # Make sure DE and EF would aggregate on E when E is eligible for deduplication
1694
+
1695
+ # Create a coin with the identity puzzle hash
1696
+ async with wallet.wallet_state_manager.new_action_scope(
1697
+ DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
1698
+ ) as action_scope:
1699
+ await wallet.generate_signed_transaction(uint64(200), IDENTITY_PUZZLE_HASH, action_scope, coins={coins[3].coin})
1700
+ [tx] = action_scope.side_effects.transactions
1701
+ assert tx.spend_bundle is not None
1702
+ await send_to_mempool(full_node_api, tx.spend_bundle)
1703
+ await farm_a_block(full_node_api, wallet_node, ph)
1704
+ # Grab the coin we created and make an eligible coin out of it
1705
+ coins_with_identity_ph = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
1706
+ False, IDENTITY_PUZZLE_HASH
1707
+ )
1708
+ sb = spend_bundle_from_conditions(
1709
+ [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 110]], coins_with_identity_ph[0].coin
1710
+ )
1711
+ await send_to_mempool(full_node_api, sb)
1712
+ await farm_a_block(full_node_api, wallet_node, ph)
1713
+ # Grab the eligible coin to spend as E in DE and EF transactions
1714
+ e_coin = (await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, IDENTITY_PUZZLE_HASH))[
1715
+ 0
1716
+ ].coin
1717
+ e_coin_id = e_coin.name()
1718
+ # Restrict spending E with an announcement to consume
1719
+ message = b"Identical spend aggregation test"
1720
+ e_announcement = AssertCoinAnnouncement(asserted_id=e_coin_id, asserted_msg=message)
1721
+ # Create transactions D and F that consume an announcement created by E
1722
+ async with wallet.wallet_state_manager.new_action_scope(
1723
+ DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
1724
+ ) as action_scope:
1725
+ await wallet.generate_signed_transaction(
1726
+ uint64(100),
1727
+ ph,
1728
+ action_scope,
1729
+ fee=uint64(0),
1730
+ coins={coins[4].coin},
1731
+ extra_conditions=(e_announcement,),
1732
+ )
1733
+ await wallet.generate_signed_transaction(
1734
+ uint64(150),
1735
+ ph,
1736
+ action_scope,
1737
+ fee=uint64(0),
1738
+ coins={coins[5].coin},
1739
+ extra_conditions=(e_announcement,),
1740
+ )
1741
+ [tx_d, tx_f] = action_scope.side_effects.transactions
1742
+ assert tx_d.spend_bundle is not None
1743
+ assert tx_f.spend_bundle is not None
1744
+ # Create transaction E now that spends e_coin to create another eligible
1745
+ # coin as well as the announcement consumed by D and F
1746
+ conditions: list[list[Any]] = [
1747
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42],
1748
+ [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, message],
1749
+ ]
1750
+ sb_e = spend_bundle_from_conditions(conditions, e_coin)
1751
+ # Send DE and EF combinations to the mempool
1752
+ sb_de = SpendBundle.aggregate([tx_d.spend_bundle, sb_e])
1753
+ sb_de_name = sb_de.name()
1754
+ await send_to_mempool(full_node_api, sb_de)
1755
+ sb_ef = SpendBundle.aggregate([sb_e, tx_f.spend_bundle])
1756
+ sb_ef_name = sb_ef.name()
1757
+ await send_to_mempool(full_node_api, sb_ef)
1758
+ # Send also a transaction EG that spends E differently from DE and EF,
1759
+ # so that it doesn't get deduplicated on E with them
1760
+ conditions = [
1761
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, e_coin.amount - 1],
1762
+ [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, message],
1763
+ ]
1764
+ sb_e2 = spend_bundle_from_conditions(conditions, e_coin)
1765
+ g_coin = coins[6].coin
1766
+ g_coin_id = g_coin.name()
1767
+ async with wallet.wallet_state_manager.new_action_scope(
1768
+ DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
1769
+ ) as action_scope:
1770
+ await wallet.generate_signed_transaction(
1771
+ uint64(13), ph, action_scope, coins={g_coin}, extra_conditions=(e_announcement,)
1772
+ )
1773
+ [tx_g] = action_scope.side_effects.transactions
1774
+ assert tx_g.spend_bundle is not None
1775
+ sb_e2g = SpendBundle.aggregate([sb_e2, tx_g.spend_bundle])
1776
+ sb_e2g_name = sb_e2g.name()
1777
+ await send_to_mempool(full_node_api, sb_e2g)
1778
+
1779
+ # Make sure our coin IDs to spend bundles mappings are correct
1780
+ assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == {sb_de_name}
1781
+ assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == {sb_de_name, sb_ef_name, sb_e2g_name}
1782
+ assert get_sb_names_by_coin_id(full_node_api, coins[5].coin.name()) == {sb_ef_name}
1783
+ assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == {sb_e2g_name}
1784
+
1785
+ await farm_a_block(full_node_api, wallet_node, ph)
1786
+
1787
+ # Make sure sb_de and sb_ef coins, including the deduplicated one, are removed
1788
+ # from the coin IDs to spend bundles mappings with the creation of a new block
1789
+ assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == set()
1790
+ assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == set()
1791
+ assert get_sb_names_by_coin_id(full_node_api, coins[5].coin.name()) == set()
1792
+ assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == set()
1793
+
1794
+ # Make sure coin G remains because E2G was removed as E got spent differently (by DE and EF)
1795
+ coins_set = await wallet_node.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(1)
1796
+ assert g_coin in (c.coin for c in coins_set)
1797
+ # Only the newly created eligible coin is left now
1798
+ eligible_coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
1799
+ False, IDENTITY_PUZZLE_HASH
1800
+ )
1801
+ assert len(eligible_coins) == 1
1802
+ assert eligible_coins[0].coin.amount == 42
1803
+
1804
+
1805
+ # we have two coins in this test. They have different birth heights (and
1806
+ # timestamps)
1807
+ # coin1: amount=1, confirmed_height=10, timestamp=1000
1808
+ # coin2: amount=2, confirmed_height=20, timestamp=2000
1809
+ # the mempool is at height 21 and timestamp 2010
1810
+ @pytest.mark.anyio
1811
+ @pytest.mark.parametrize(
1812
+ "cond1,cond2,expected",
1813
+ [
1814
+ # ASSERT HEIGHT ABSOLUTE
1815
+ (
1816
+ [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 30],
1817
+ [co.ASSERT_HEIGHT_ABSOLUTE, 30],
1818
+ Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
1819
+ ),
1820
+ (
1821
+ [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 31],
1822
+ [co.ASSERT_HEIGHT_ABSOLUTE, 30],
1823
+ None,
1824
+ ),
1825
+ (
1826
+ [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 21],
1827
+ [co.ASSERT_HEIGHT_ABSOLUTE, 20],
1828
+ Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED,
1829
+ ),
1830
+ # ASSERT SECONDS ABSOLUTE
1831
+ (
1832
+ [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3000],
1833
+ [co.ASSERT_SECONDS_ABSOLUTE, 3000],
1834
+ Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
1835
+ ),
1836
+ (
1837
+ [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3001],
1838
+ [co.ASSERT_SECONDS_ABSOLUTE, 3000],
1839
+ Err.ASSERT_SECONDS_ABSOLUTE_FAILED,
1840
+ ),
1841
+ (
1842
+ [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 2001],
1843
+ [co.ASSERT_SECONDS_ABSOLUTE, 2000],
1844
+ Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED,
1845
+ ),
1846
+ # ASSERT HEIGHT RELATIVE
1847
+ # coin1: height=10
1848
+ # coin2: height=20
1849
+ (
1850
+ [co.ASSERT_BEFORE_HEIGHT_RELATIVE, 15],
1851
+ [co.ASSERT_HEIGHT_RELATIVE, 5],
1852
+ Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
1853
+ ),
1854
+ (
1855
+ [co.ASSERT_BEFORE_HEIGHT_RELATIVE, 26],
1856
+ [co.ASSERT_HEIGHT_RELATIVE, 15],
1857
+ None,
1858
+ ),
1859
+ (
1860
+ [co.ASSERT_BEFORE_HEIGHT_RELATIVE, 16],
1861
+ [co.ASSERT_HEIGHT_RELATIVE, 5],
1862
+ None,
1863
+ ),
1864
+ # ASSERT SECONDS RELATIVE
1865
+ # coin1: timestamp=1000
1866
+ # coin2: timestamp=2000
1867
+ (
1868
+ [co.ASSERT_BEFORE_SECONDS_RELATIVE, 1500],
1869
+ [co.ASSERT_SECONDS_RELATIVE, 500],
1870
+ Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
1871
+ ),
1872
+ # we don't have a pending cache for seconds timelocks, so these fail
1873
+ # immediately
1874
+ (
1875
+ [co.ASSERT_BEFORE_SECONDS_RELATIVE, 2501],
1876
+ [co.ASSERT_SECONDS_RELATIVE, 1500],
1877
+ Err.ASSERT_SECONDS_RELATIVE_FAILED,
1878
+ ),
1879
+ (
1880
+ [co.ASSERT_BEFORE_SECONDS_RELATIVE, 1501],
1881
+ [co.ASSERT_SECONDS_RELATIVE, 500],
1882
+ Err.ASSERT_SECONDS_RELATIVE_FAILED,
1883
+ ),
1884
+ # ASSERT HEIGHT RELATIVE and ASSERT HEIGHT ABSOLUTE
1885
+ # coin1: height=10
1886
+ # coin2: height=20
1887
+ (
1888
+ [co.ASSERT_BEFORE_HEIGHT_RELATIVE, 20],
1889
+ [co.ASSERT_HEIGHT_ABSOLUTE, 30],
1890
+ Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
1891
+ ),
1892
+ (
1893
+ [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 30],
1894
+ [co.ASSERT_HEIGHT_RELATIVE, 10],
1895
+ Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
1896
+ ),
1897
+ (
1898
+ [co.ASSERT_BEFORE_HEIGHT_RELATIVE, 21],
1899
+ [co.ASSERT_HEIGHT_ABSOLUTE, 30],
1900
+ None,
1901
+ ),
1902
+ (
1903
+ [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 31],
1904
+ [co.ASSERT_HEIGHT_RELATIVE, 10],
1905
+ None,
1906
+ ),
1907
+ # ASSERT SECONDS ABSOLUTE and ASSERT SECONDS RELATIVE
1908
+ (
1909
+ [co.ASSERT_BEFORE_SECONDS_RELATIVE, 2000],
1910
+ [co.ASSERT_SECONDS_ABSOLUTE, 3000],
1911
+ Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
1912
+ ),
1913
+ (
1914
+ [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3000],
1915
+ [co.ASSERT_SECONDS_RELATIVE, 1000],
1916
+ Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
1917
+ ),
1918
+ # we don't have a pending cache for seconds timelocks, so these fail
1919
+ # immediately
1920
+ (
1921
+ [co.ASSERT_BEFORE_SECONDS_RELATIVE, 2001],
1922
+ [co.ASSERT_SECONDS_ABSOLUTE, 3000],
1923
+ Err.ASSERT_SECONDS_ABSOLUTE_FAILED,
1924
+ ),
1925
+ (
1926
+ [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3001],
1927
+ [co.ASSERT_SECONDS_RELATIVE, 1000],
1928
+ Err.ASSERT_SECONDS_RELATIVE_FAILED,
1929
+ ),
1930
+ ],
1931
+ )
1932
+ async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected: Optional[Err]) -> None:
1933
+ coins = []
1934
+ test_coin_records = {}
1935
+
1936
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(1))
1937
+ coins.append(coin)
1938
+ test_coin_records[coin.name()] = CoinRecord(coin, uint32(10), uint32(0), False, uint64(1000))
1939
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(2))
1940
+ coins.append(coin)
1941
+ test_coin_records[coin.name()] = CoinRecord(coin, uint32(20), uint32(0), False, uint64(2000))
1942
+
1943
+ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
1944
+ ret: list[CoinRecord] = []
1945
+ for name in coin_ids:
1946
+ r = test_coin_records.get(name)
1947
+ if r is not None:
1948
+ ret.append(r)
1949
+ return ret
1950
+
1951
+ mempool_manager = await instantiate_mempool_manager(
1952
+ get_coin_records, block_height=uint32(21), block_timestamp=uint64(2010)
1953
+ )
1954
+
1955
+ coin_spends = [
1956
+ make_spend(coins[0], IDENTITY_PUZZLE, Program.to([cond1])),
1957
+ make_spend(coins[1], IDENTITY_PUZZLE, Program.to([cond2])),
1958
+ ]
1959
+
1960
+ bundle = SpendBundle(coin_spends, G2Element())
1961
+ bundle_name = bundle.name()
1962
+ try:
1963
+ result = await add_spendbundle(mempool_manager, bundle, bundle_name)
1964
+ print(result)
1965
+ if expected is not None:
1966
+ assert result == (None, MempoolInclusionStatus.FAILED, expected)
1967
+ else:
1968
+ assert result[0] is not None
1969
+ assert result[1] != MempoolInclusionStatus.FAILED
1970
+ except ValidationError as e:
1971
+ assert e.code == expected
1972
+
1973
+
1974
+ TEST_FILL_RATE_ITEM_COST = 144_720_020
1975
+ TEST_COST_PER_BYTE = 12_000
1976
+ TEST_BLOCK_OVERHEAD = QUOTE_BYTES * TEST_COST_PER_BYTE + QUOTE_EXECUTION_COST
1977
+
1978
+
1979
+ @pytest.mark.anyio
1980
+ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.HARD_FORK_2_0])
1981
+ @pytest.mark.parametrize(
1982
+ "max_block_clvm_cost, expected_block_items, expected_block_cost",
1983
+ [
1984
+ # Here we set the block cost limit to twice the test items' cost, so we
1985
+ # expect both test items to get included in the block.
1986
+ # NOTE: The expected block cost is smaller than the sum of items' costs
1987
+ # because of the spend bundle aggregation that creates the block
1988
+ # bundle, in addition to a small block compression effect that we
1989
+ # can't completely avoid.
1990
+ (TEST_FILL_RATE_ITEM_COST * 2, 2, TEST_FILL_RATE_ITEM_COST * 2 - 107_980),
1991
+ # Here we set the block cost limit to twice the test items' cost - 1,
1992
+ # so we expect only one of the two test items to get included in the block.
1993
+ # NOTE: The cost difference here is because get_conditions_from_spendbundle
1994
+ # does not include the block overhead.
1995
+ (TEST_FILL_RATE_ITEM_COST * 2 - 1, 1, TEST_FILL_RATE_ITEM_COST + TEST_BLOCK_OVERHEAD),
1996
+ ],
1997
+ )
1998
+ async def test_fill_rate_block_validation(
1999
+ blockchain_constants: ConsensusConstants,
2000
+ max_block_clvm_cost: uint64,
2001
+ expected_block_items: int,
2002
+ expected_block_cost: uint64,
2003
+ ) -> None:
2004
+ """
2005
+ This test covers the case where we set the fill rate to 100% and ensure
2006
+ that we wouldn't generate a block that exceed the maximum block cost limit.
2007
+ In the first scenario, we set the block cost limit to match the test items'
2008
+ costs sum, expecting both test items to get included in the block.
2009
+ In the second scenario, we reduce the maximum block cost limit by one,
2010
+ expecting only one of the two test items to get included in the block.
2011
+ """
2012
+
2013
+ async def send_to_mempool(full_node: FullNodeSimulator, spend_bundle: SpendBundle) -> None:
2014
+ res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle))
2015
+ assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
2016
+ res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
2017
+ assert res_parsed.status == MempoolInclusionStatus.SUCCESS.value
2018
+
2019
+ async def fill_mempool_with_test_sbs(
2020
+ full_node_api: FullNodeSimulator,
2021
+ ) -> list[tuple[bytes32, SerializedProgram, bytes32]]:
2022
+ coins_and_puzzles = []
2023
+ # Create different puzzles and use different (parent) coins to reduce
2024
+ # the effects of block compression as much as possible.
2025
+ for i in (1, 2):
2026
+ puzzle = SerializedProgram.to((1, [[ConditionOpcode.REMARK, bytes([i] * 12_000)]]))
2027
+ ph = puzzle.get_tree_hash()
2028
+ for _ in range(2):
2029
+ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
2030
+ coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, ph)
2031
+ coin = next(cr.coin for cr in coin_records if cr.coin.amount == 250_000_000_000)
2032
+ coins_and_puzzles.append((coin, puzzle))
2033
+ sbs_info = []
2034
+ for coin, puzzle in coins_and_puzzles:
2035
+ coin_spend = make_spend(coin, puzzle, SerializedProgram.to([]))
2036
+ sb = SpendBundle([coin_spend], G2Element())
2037
+ await send_to_mempool(full_node_api, sb)
2038
+ sbs_info.append((coin.name(), puzzle, sb.name()))
2039
+ return sbs_info
2040
+
2041
+ constants = blockchain_constants.replace(MAX_BLOCK_COST_CLVM=max_block_clvm_cost)
2042
+ async with setup_simulators_and_wallets(1, 0, constants) as setup:
2043
+ full_node_api = setup.simulators[0].peer_api
2044
+ assert full_node_api.full_node._mempool_manager is not None
2045
+ # We have to alter the following values here as they're not exposed elsewhere
2046
+ # and without them we won't be able to get the test bundle in.
2047
+ # This defaults to `MAX_BLOCK_COST_CLVM // 2`
2048
+ full_node_api.full_node._mempool_manager.max_tx_clvm_cost = max_block_clvm_cost
2049
+ # This defaults to `MAX_BLOCK_COST_CLVM - BLOCK_OVERHEAD`
2050
+ full_node_api.full_node._mempool_manager.mempool.mempool_info = dataclasses.replace(
2051
+ full_node_api.full_node._mempool_manager.mempool.mempool_info,
2052
+ max_block_clvm_cost=CLVMCost(max_block_clvm_cost),
2053
+ )
2054
+ sbs_info = await fill_mempool_with_test_sbs(full_node_api)
2055
+ # This check is here just to make sure our bundles have the expected cost
2056
+ for sb_info in sbs_info:
2057
+ _, _, sb_name = sb_info
2058
+ mi = full_node_api.full_node.mempool_manager.get_mempool_item(sb_name)
2059
+ assert mi is not None
2060
+ assert mi.cost == TEST_FILL_RATE_ITEM_COST
2061
+ # Farm the block to make sure we're passing block validation
2062
+ current_peak = full_node_api.full_node.blockchain.get_peak()
2063
+ assert current_peak is not None
2064
+ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(IDENTITY_PUZZLE_HASH))
2065
+ # Check that our resulting block is what we expect
2066
+ peak = full_node_api.full_node.blockchain.get_peak()
2067
+ assert peak is not None
2068
+ # Check for the peak change after farming the block
2069
+ assert peak.prev_hash == current_peak.header_hash
2070
+ # Check our coin(s)
2071
+ for i in range(expected_block_items):
2072
+ coin_name, puzzle, _ = sbs_info[i]
2073
+ rps_res = await full_node_api.request_puzzle_solution(
2074
+ wallet_protocol.RequestPuzzleSolution(coin_name, peak.height)
2075
+ )
2076
+ assert rps_res is not None
2077
+ rps_res_parsed = wallet_protocol.RespondPuzzleSolution.from_bytes(rps_res.data)
2078
+ assert rps_res_parsed.response.puzzle == puzzle
2079
+ # Check the block cost
2080
+ rb_res = await full_node_api.request_block(RequestBlock(peak.height, True))
2081
+ assert rb_res is not None
2082
+ rb_res_parsed = RespondBlock.from_bytes(rb_res.data)
2083
+ assert rb_res_parsed.block.transactions_info is not None
2084
+ assert rb_res_parsed.block.transactions_info.cost == expected_block_cost