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,3255 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import logging
5
+ import random
6
+ from typing import Callable, Optional
7
+
8
+ import pytest
9
+ from chia_rs import G1Element, G2Element, get_flags_for_height_and_constants
10
+ from clvm.casts import int_to_bytes
11
+ from clvm_tools import binutils
12
+ from clvm_tools.binutils import assemble
13
+
14
+ from chia._tests.blockchain.blockchain_test_utils import _validate_and_add_block
15
+ from chia._tests.connection_utils import add_dummy_connection, connect_and_get_peer
16
+ from chia._tests.core.mempool.test_mempool_manager import (
17
+ IDENTITY_PUZZLE_HASH,
18
+ TEST_COIN,
19
+ assert_sb_in_pool,
20
+ assert_sb_not_in_pool,
21
+ make_test_coins,
22
+ mempool_item_from_spendbundle,
23
+ mk_item,
24
+ spend_bundle_from_conditions,
25
+ )
26
+ from chia._tests.core.node_height import node_height_at_least
27
+ from chia._tests.util.get_name_puzzle_conditions import get_name_puzzle_conditions
28
+ from chia._tests.util.misc import BenchmarkRunner, invariant_check_mempool
29
+ from chia._tests.util.time_out_assert import time_out_assert
30
+ from chia.consensus.condition_costs import ConditionCost
31
+ from chia.consensus.cost_calculator import NPCResult
32
+ from chia.consensus.default_constants import DEFAULT_CONSTANTS
33
+ from chia.full_node.bitcoin_fee_estimator import create_bitcoin_fee_estimator
34
+ from chia.full_node.fee_estimation import EmptyMempoolInfo, MempoolInfo
35
+ from chia.full_node.full_node_api import FullNodeAPI
36
+ from chia.full_node.mempool import Mempool
37
+ from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
38
+ from chia.full_node.mempool_manager import MEMPOOL_MIN_FEE_INCREASE
39
+ from chia.full_node.pending_tx_cache import ConflictTxCache, PendingTxCache
40
+ from chia.protocols import full_node_protocol, wallet_protocol
41
+ from chia.protocols.wallet_protocol import TransactionAck
42
+ from chia.server.api_protocol import ApiMetadata
43
+ from chia.server.outbound_message import Message
44
+ from chia.server.server import ChiaServer
45
+ from chia.server.ws_connection import WSChiaConnection
46
+ from chia.simulator.add_blocks_in_batches import add_blocks_in_batches
47
+ from chia.simulator.block_tools import BlockTools, test_constants
48
+ from chia.simulator.full_node_simulator import FullNodeSimulator
49
+ from chia.simulator.simulator_protocol import FarmNewBlockProtocol
50
+ from chia.simulator.wallet_tools import WalletTool
51
+ from chia.types.blockchain_format.coin import Coin
52
+ from chia.types.blockchain_format.program import Program
53
+ from chia.types.blockchain_format.serialized_program import SerializedProgram
54
+ from chia.types.blockchain_format.sized_bytes import bytes32
55
+ from chia.types.clvm_cost import CLVMCost
56
+ from chia.types.coin_spend import CoinSpend, make_spend
57
+ from chia.types.condition_opcodes import ConditionOpcode
58
+ from chia.types.condition_with_args import ConditionWithArgs
59
+ from chia.types.eligible_coin_spends import UnspentLineageInfo, run_for_cost
60
+ from chia.types.fee_rate import FeeRate
61
+ from chia.types.full_block import FullBlock
62
+ from chia.types.generator_types import BlockGenerator
63
+ from chia.types.mempool_inclusion_status import MempoolInclusionStatus
64
+ from chia.types.mempool_item import MempoolItem
65
+ from chia.types.spend_bundle import SpendBundle, estimate_fees
66
+ from chia.types.spend_bundle_conditions import SpendBundleConditions
67
+ from chia.util.errors import Err
68
+ from chia.util.hash import std_hash
69
+ from chia.util.ints import uint32, uint64
70
+ from chia.util.recursive_replace import recursive_replace
71
+ from chia.wallet.conditions import AssertCoinAnnouncement, AssertPuzzleAnnouncement
72
+
73
+ BURN_PUZZLE_HASH = bytes32(b"0" * 32)
74
+ BURN_PUZZLE_HASH_2 = bytes32(b"1" * 32)
75
+
76
+ log = logging.getLogger(__name__)
77
+
78
+
79
+ def new_mi(mi: MempoolInfo, max_mempool_cost: int, min_replace_fee_per_cost: int) -> MempoolInfo:
80
+ return dataclasses.replace(
81
+ mi,
82
+ minimum_fee_per_cost_to_replace=FeeRate(uint64(min_replace_fee_per_cost)),
83
+ max_size_in_cost=CLVMCost(uint64(max_mempool_cost)),
84
+ )
85
+
86
+
87
+ @pytest.fixture(scope="module")
88
+ def wallet_a(bt: BlockTools) -> WalletTool:
89
+ return bt.get_pool_wallet_tool()
90
+
91
+
92
+ def generate_test_spend_bundle(
93
+ wallet: WalletTool,
94
+ coin: Coin,
95
+ condition_dic: Optional[dict[ConditionOpcode, list[ConditionWithArgs]]] = None,
96
+ fee: uint64 = uint64(0),
97
+ amount: uint64 = uint64(1000),
98
+ new_puzzle_hash: bytes32 = BURN_PUZZLE_HASH,
99
+ ) -> SpendBundle:
100
+ if condition_dic is None:
101
+ condition_dic = {}
102
+ transaction = wallet.generate_signed_transaction(amount, new_puzzle_hash, coin, condition_dic, fee)
103
+ assert transaction is not None
104
+ return transaction
105
+
106
+
107
+ def make_item(
108
+ idx: int, cost: uint64 = uint64(80), assert_height: uint32 = uint32(100), fee: uint64 = uint64(0)
109
+ ) -> MempoolItem:
110
+ spend_bundle_name = bytes32([idx] * 32)
111
+ return MempoolItem(
112
+ SpendBundle([], G2Element()),
113
+ fee,
114
+ SpendBundleConditions([], 0, 0, 0, None, None, [], cost, 0, 0, False, 0, 0),
115
+ spend_bundle_name,
116
+ uint32(0),
117
+ assert_height,
118
+ )
119
+
120
+
121
+ class TestConflictTxCache:
122
+ def test_recall(self) -> None:
123
+ c = ConflictTxCache(100)
124
+ item = make_item(1)
125
+ c.add(item)
126
+ assert c.get(item.name) == item
127
+ tx = c.drain()
128
+ assert tx == {item.spend_bundle_name: item}
129
+
130
+ def test_fifo_limit(self) -> None:
131
+ c = ConflictTxCache(200)
132
+ # each item has cost 80
133
+ items = [make_item(i) for i in range(1, 4)]
134
+ for i in items:
135
+ c.add(i)
136
+ # the max cost is 200, only two transactions will fit
137
+ # we evict items FIFO, so the to most recently added will be left
138
+ tx = c.drain()
139
+ assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
140
+
141
+ def test_item_limit(self) -> None:
142
+ c = ConflictTxCache(1000000, 2)
143
+ # each item has cost 80
144
+ items = [make_item(i) for i in range(1, 4)]
145
+ for i in items:
146
+ c.add(i)
147
+ # the max size is 2, only two transactions will fit
148
+ # we evict items FIFO, so the to most recently added will be left
149
+ tx = c.drain()
150
+ assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
151
+
152
+ def test_drain(self) -> None:
153
+ c = ConflictTxCache(100)
154
+ item = make_item(1)
155
+ c.add(item)
156
+ tx = c.drain()
157
+ assert tx == {item.spend_bundle_name: item}
158
+
159
+ # drain will clear the cache, so a second call will be empty
160
+ tx = c.drain()
161
+ assert tx == {}
162
+
163
+ def test_cost(self) -> None:
164
+ c = ConflictTxCache(200)
165
+ assert c.cost() == 0
166
+ item1 = make_item(1)
167
+ c.add(item1)
168
+ # each item has cost 80
169
+ assert c.cost() == 80
170
+
171
+ item2 = make_item(2)
172
+ c.add(item2)
173
+ assert c.cost() == 160
174
+
175
+ # the first item is evicted, so the cost stays the same
176
+ item3 = make_item(3)
177
+ c.add(item3)
178
+ assert c.cost() == 160
179
+
180
+ tx = c.drain()
181
+ assert tx == {item2.spend_bundle_name: item2, item3.spend_bundle_name: item3}
182
+
183
+ assert c.cost() == 0
184
+ item4 = make_item(4)
185
+ c.add(item4)
186
+ assert c.cost() == 80
187
+
188
+ tx = c.drain()
189
+ assert tx == {item4.spend_bundle_name: item4}
190
+
191
+
192
+ class TestPendingTxCache:
193
+ def test_recall(self) -> None:
194
+ c = PendingTxCache(100)
195
+ item = make_item(1)
196
+ c.add(item)
197
+ assert c.get(item.name) == item
198
+ tx = c.drain(uint32(101))
199
+ assert tx == {item.spend_bundle_name: item}
200
+
201
+ def test_fifo_limit(self) -> None:
202
+ c = PendingTxCache(200)
203
+ # each item has cost 80
204
+ items = [make_item(i) for i in range(1, 4)]
205
+ for i in items:
206
+ c.add(i)
207
+ # the max cost is 200, only two transactions will fit
208
+ # the eviction is FIFO because all items have the same assert_height
209
+ tx = c.drain(uint32(101))
210
+ assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
211
+
212
+ def test_add_eviction(self) -> None:
213
+ c = PendingTxCache(160)
214
+ item = make_item(1)
215
+ c.add(item)
216
+
217
+ for i in range(3):
218
+ item = make_item(i + 1, assert_height=uint32(50))
219
+ c.add(item)
220
+
221
+ txs = c.drain(uint32(161))
222
+ assert len(txs) == 2
223
+ for tx in txs.values():
224
+ assert tx.assert_height == 50
225
+
226
+ def test_item_limit(self) -> None:
227
+ c = PendingTxCache(1000000, 2)
228
+ # each item has cost 80
229
+ items = [make_item(i) for i in range(1, 4)]
230
+ for i in items:
231
+ c.add(i)
232
+ # the max size is 2, only two transactions will fit
233
+ # the eviction is FIFO because all items have the same assert_height
234
+ tx = c.drain(uint32(101))
235
+ assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
236
+
237
+ def test_drain(self) -> None:
238
+ c = PendingTxCache(100)
239
+ item = make_item(1)
240
+ c.add(item)
241
+ tx = c.drain(uint32(101))
242
+ assert tx == {item.spend_bundle_name: item}
243
+
244
+ # drain will clear the cache, so a second call will be empty
245
+ tx = c.drain(uint32(101))
246
+ assert tx == {}
247
+
248
+ def test_cost(self) -> None:
249
+ c = PendingTxCache(200)
250
+ assert c.cost() == 0
251
+ item1 = make_item(1)
252
+ c.add(item1)
253
+ # each item has cost 80
254
+ assert c.cost() == 80
255
+
256
+ item2 = make_item(2)
257
+ c.add(item2)
258
+ assert c.cost() == 160
259
+
260
+ # the first item is evicted, so the cost stays the same
261
+ item3 = make_item(3)
262
+ c.add(item3)
263
+ assert c.cost() == 160
264
+
265
+ tx = c.drain(uint32(101))
266
+ assert tx == {item2.spend_bundle_name: item2, item3.spend_bundle_name: item3}
267
+
268
+ assert c.cost() == 0
269
+ item4 = make_item(4)
270
+ c.add(item4)
271
+ assert c.cost() == 80
272
+
273
+ tx = c.drain(uint32(101))
274
+ assert tx == {item4.spend_bundle_name: item4}
275
+
276
+ def test_drain_height(self) -> None:
277
+ c = PendingTxCache(20000, 1000)
278
+
279
+ # each item has cost 80
280
+ # heights are 100-109
281
+ items = [make_item(i, assert_height=uint32(100 + i)) for i in range(10)]
282
+ for i in items:
283
+ c.add(i)
284
+
285
+ tx = c.drain(uint32(101))
286
+ assert tx == {items[0].spend_bundle_name: items[0]}
287
+
288
+ tx = c.drain(uint32(105))
289
+ assert tx == {
290
+ items[1].spend_bundle_name: items[1],
291
+ items[2].spend_bundle_name: items[2],
292
+ items[3].spend_bundle_name: items[3],
293
+ items[4].spend_bundle_name: items[4],
294
+ }
295
+
296
+ tx = c.drain(uint32(105))
297
+ assert tx == {}
298
+
299
+ tx = c.drain(uint32(110))
300
+ assert tx == {
301
+ items[5].spend_bundle_name: items[5],
302
+ items[6].spend_bundle_name: items[6],
303
+ items[7].spend_bundle_name: items[7],
304
+ items[8].spend_bundle_name: items[8],
305
+ items[9].spend_bundle_name: items[9],
306
+ }
307
+
308
+
309
+ class TestMempool:
310
+ @pytest.mark.anyio
311
+ async def test_basic_mempool(
312
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
313
+ ) -> None:
314
+ full_node_1, _server_1, bt = one_node_one_block
315
+
316
+ _ = await next_block(full_node_1, wallet_a, bt)
317
+ _ = await next_block(full_node_1, wallet_a, bt)
318
+
319
+ max_block_cost_clvm = uint64(40000000)
320
+ max_mempool_cost = max_block_cost_clvm * 5
321
+ mempool_info = new_mi(EmptyMempoolInfo, max_mempool_cost, uint64(5))
322
+ fee_estimator = create_bitcoin_fee_estimator(max_block_cost_clvm)
323
+ mempool = Mempool(mempool_info, fee_estimator)
324
+ assert mempool.get_min_fee_rate(104000) == 0
325
+
326
+ assert mempool.get_min_fee_rate(max_mempool_cost + 1) is None
327
+
328
+ coin = await next_block(full_node_1, wallet_a, bt)
329
+ spend_bundle = generate_test_spend_bundle(wallet_a, coin)
330
+ assert spend_bundle is not None
331
+
332
+
333
+ metadata = ApiMetadata()
334
+
335
+
336
+ # this (method'ish) function is not designed per normal uses so allowing the ignore
337
+ # for the different return type. normal is Optional[Message]
338
+ @metadata.request(peer_required=True, bytes_required=True) # type: ignore[type-var]
339
+ async def respond_transaction(
340
+ self: FullNodeAPI,
341
+ tx: full_node_protocol.RespondTransaction,
342
+ peer: WSChiaConnection,
343
+ tx_bytes: bytes = b"",
344
+ test: bool = False,
345
+ ) -> tuple[MempoolInclusionStatus, Optional[Err]]:
346
+ """
347
+ Receives a full transaction from peer.
348
+ If tx is added to mempool, send tx_id to others. (new_transaction)
349
+ """
350
+ assert tx_bytes != b""
351
+ spend_name = std_hash(tx_bytes)
352
+ if spend_name in self.full_node.full_node_store.pending_tx_request:
353
+ self.full_node.full_node_store.pending_tx_request.pop(spend_name)
354
+ if spend_name in self.full_node.full_node_store.peers_with_tx:
355
+ self.full_node.full_node_store.peers_with_tx.pop(spend_name)
356
+ ret = await self.full_node.add_transaction(tx.transaction, spend_name, peer, test)
357
+ invariant_check_mempool(self.full_node.mempool_manager.mempool)
358
+ return ret
359
+
360
+
361
+ async def next_block(full_node_1: FullNodeSimulator, wallet_a: WalletTool, bt: BlockTools) -> Coin:
362
+ blocks = await full_node_1.get_all_full_blocks()
363
+ # we have to farm a new block here, to ensure every test has a unique coin to test spending.
364
+ # all this could be simplified if the tests did not share a simulation
365
+ start_height = blocks[-1].height
366
+ reward_ph = wallet_a.get_new_puzzlehash()
367
+ blocks = bt.get_consecutive_blocks(
368
+ 1,
369
+ block_list_input=blocks,
370
+ guarantee_transaction_block=True,
371
+ farmer_reward_puzzle_hash=reward_ph,
372
+ pool_reward_puzzle_hash=reward_ph,
373
+ genesis_timestamp=uint64(10_000),
374
+ time_per_block=10,
375
+ )
376
+
377
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
378
+
379
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 1)
380
+ return blocks[-1].get_included_reward_coins()[0]
381
+
382
+
383
+ co = ConditionOpcode
384
+ mis = MempoolInclusionStatus
385
+
386
+
387
+ async def send_sb(node: FullNodeAPI, sb: SpendBundle) -> Optional[Message]:
388
+ tx = wallet_protocol.SendTransaction(sb)
389
+ return await node.send_transaction(tx, test=True)
390
+
391
+
392
+ async def gen_and_send_sb(node: FullNodeAPI, wallet: WalletTool, coin: Coin, fee: uint64 = uint64(0)) -> SpendBundle:
393
+ sb = generate_test_spend_bundle(wallet=wallet, coin=coin, fee=fee)
394
+ assert sb is not None
395
+ await send_sb(node, sb)
396
+ return sb
397
+
398
+
399
+ class TestMempoolManager:
400
+ @pytest.mark.anyio
401
+ async def test_basic_mempool_manager(
402
+ self,
403
+ two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
404
+ wallet_a: WalletTool,
405
+ self_hostname: str,
406
+ ) -> None:
407
+ full_node_1, _full_node_2, server_1, server_2, bt = two_nodes_one_block
408
+
409
+ peer = await connect_and_get_peer(server_1, server_2, self_hostname)
410
+
411
+ _ = await next_block(full_node_1, wallet_a, bt)
412
+ coin = await next_block(full_node_1, wallet_a, bt)
413
+ spend_bundle = generate_test_spend_bundle(wallet_a, coin)
414
+ assert spend_bundle is not None
415
+ tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle)
416
+ await full_node_1.respond_transaction(tx, peer, test=True)
417
+
418
+ await time_out_assert(
419
+ 10,
420
+ full_node_1.full_node.mempool_manager.get_spendbundle,
421
+ spend_bundle,
422
+ spend_bundle.name(),
423
+ )
424
+
425
+ @pytest.mark.anyio
426
+ @pytest.mark.parametrize(
427
+ "opcode,lock_value,expected",
428
+ [
429
+ # the mempool rules don't allow relative height- or time conditions on
430
+ # ephemeral spends
431
+ (co.ASSERT_MY_BIRTH_HEIGHT, -1, mis.FAILED),
432
+ (co.ASSERT_MY_BIRTH_HEIGHT, 0x100000000, mis.FAILED),
433
+ (co.ASSERT_MY_BIRTH_HEIGHT, 5, mis.FAILED),
434
+ (co.ASSERT_MY_BIRTH_HEIGHT, 6, mis.FAILED),
435
+ (co.ASSERT_MY_BIRTH_SECONDS, -1, mis.FAILED),
436
+ (co.ASSERT_MY_BIRTH_SECONDS, 0x10000000000000000, mis.FAILED),
437
+ (co.ASSERT_MY_BIRTH_SECONDS, 10049, mis.FAILED),
438
+ (co.ASSERT_MY_BIRTH_SECONDS, 10050, mis.FAILED),
439
+ (co.ASSERT_MY_BIRTH_SECONDS, 10051, mis.FAILED),
440
+ (co.ASSERT_SECONDS_RELATIVE, -2, mis.FAILED),
441
+ (co.ASSERT_SECONDS_RELATIVE, -1, mis.FAILED),
442
+ (co.ASSERT_SECONDS_RELATIVE, 0, mis.FAILED),
443
+ (co.ASSERT_SECONDS_RELATIVE, 1, mis.FAILED),
444
+ (co.ASSERT_HEIGHT_RELATIVE, -2, mis.FAILED),
445
+ (co.ASSERT_HEIGHT_RELATIVE, -1, mis.FAILED),
446
+ (co.ASSERT_HEIGHT_RELATIVE, 0, mis.FAILED),
447
+ (co.ASSERT_HEIGHT_RELATIVE, 1, mis.FAILED),
448
+ # the absolute height and seconds tests require fresh full nodes to
449
+ # run the test on. The fixture (one_node_one_block) creates a block,
450
+ # then condition_tester2 creates another 3 blocks
451
+ (co.ASSERT_HEIGHT_ABSOLUTE, 4, mis.SUCCESS),
452
+ (co.ASSERT_HEIGHT_ABSOLUTE, 5, mis.SUCCESS),
453
+ (co.ASSERT_HEIGHT_ABSOLUTE, 6, mis.PENDING),
454
+ (co.ASSERT_HEIGHT_ABSOLUTE, 7, mis.PENDING),
455
+ # genesis timestamp is 10000 and each block is 10 seconds
456
+ (co.ASSERT_SECONDS_ABSOLUTE, 10049, mis.SUCCESS),
457
+ (co.ASSERT_SECONDS_ABSOLUTE, 10050, mis.SUCCESS),
458
+ (co.ASSERT_SECONDS_ABSOLUTE, 10051, mis.FAILED),
459
+ (co.ASSERT_SECONDS_ABSOLUTE, 10052, mis.FAILED),
460
+ ],
461
+ )
462
+ async def test_ephemeral_timelock(
463
+ self,
464
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
465
+ wallet_a: WalletTool,
466
+ opcode: ConditionOpcode,
467
+ lock_value: int,
468
+ expected: MempoolInclusionStatus,
469
+ ) -> None:
470
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
471
+ conditions = {opcode: [ConditionWithArgs(opcode, [int_to_bytes(lock_value)])]}
472
+ tx1 = wallet_a.generate_signed_transaction(uint64(1000000), wallet_a.get_new_puzzlehash(), coin_2)
473
+
474
+ ephemeral_coin: Coin = tx1.additions()[0]
475
+ tx2 = wallet_a.generate_signed_transaction(
476
+ uint64(1000000), wallet_a.get_new_puzzlehash(), ephemeral_coin, conditions.copy(), uint64(0)
477
+ )
478
+
479
+ bundle = SpendBundle.aggregate([tx1, tx2])
480
+ return bundle
481
+
482
+ full_node_1, _server_1, bt = one_node_one_block
483
+ _ = await next_block(full_node_1, wallet_a, bt)
484
+ _ = await next_block(full_node_1, wallet_a, bt)
485
+ _blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
486
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
487
+
488
+ print(f"opcode={opcode} timelock_value={lock_value} expected={expected} status={status}")
489
+ print(f"status: {status}")
490
+ print(f"error: {err}")
491
+
492
+ assert status == expected
493
+ if expected == MempoolInclusionStatus.SUCCESS:
494
+ assert mempool_bundle == bundle
495
+ assert err is None
496
+ else:
497
+ assert mempool_bundle is None
498
+ assert err is not None
499
+
500
+ # this test makes sure that one spend successfully asserts the announce from
501
+ # another spend, even though the assert condition is duplicated 100 times
502
+ @pytest.mark.anyio
503
+ async def test_coin_announcement_duplicate_consumed(
504
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
505
+ ) -> None:
506
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
507
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
508
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
509
+ dic = {cvp.opcode: [cvp] * 100}
510
+
511
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
512
+ dic2 = {cvp.opcode: [cvp2]}
513
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
514
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
515
+ bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
516
+ return bundle
517
+
518
+ full_node_1, _server_1, _bt = one_node_one_block
519
+ _blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
520
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
521
+
522
+ assert err is None
523
+ assert mempool_bundle == bundle
524
+ assert status == MempoolInclusionStatus.SUCCESS
525
+
526
+ # this test makes sure that one spend successfully asserts the announce from
527
+ # another spend, even though the create announcement is duplicated 100 times
528
+ @pytest.mark.anyio
529
+ async def test_coin_duplicate_announcement_consumed(
530
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
531
+ ) -> None:
532
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
533
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
534
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
535
+ dic = {cvp.opcode: [cvp]}
536
+
537
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
538
+ dic2 = {cvp.opcode: [cvp2] * 100}
539
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
540
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
541
+ bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
542
+ return bundle
543
+
544
+ full_node_1, _server_1, _bt = one_node_one_block
545
+ _blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
546
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
547
+
548
+ assert err is None
549
+ assert mempool_bundle == bundle
550
+ assert status == MempoolInclusionStatus.SUCCESS
551
+
552
+ @pytest.mark.anyio
553
+ async def test_double_spend(
554
+ self,
555
+ two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
556
+ wallet_a: WalletTool,
557
+ self_hostname: str,
558
+ ) -> None:
559
+ reward_ph = wallet_a.get_new_puzzlehash()
560
+ full_node_1, _full_node_2, server_1, server_2, bt = two_nodes_one_block
561
+ blocks = await full_node_1.get_all_full_blocks()
562
+ start_height = blocks[-1].height
563
+ blocks = bt.get_consecutive_blocks(
564
+ 3,
565
+ block_list_input=blocks,
566
+ guarantee_transaction_block=True,
567
+ farmer_reward_puzzle_hash=reward_ph,
568
+ pool_reward_puzzle_hash=reward_ph,
569
+ )
570
+ peer = await connect_and_get_peer(server_1, server_2, self_hostname)
571
+
572
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
573
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
574
+
575
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, blocks[-1].get_included_reward_coins()[0])
576
+
577
+ assert spend_bundle1 is not None
578
+ tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
579
+ status, err = await respond_transaction(full_node_1, tx1, peer, test=True)
580
+ assert err is None
581
+ assert status == MempoolInclusionStatus.SUCCESS
582
+
583
+ spend_bundle2 = generate_test_spend_bundle(
584
+ wallet_a,
585
+ blocks[-1].get_included_reward_coins()[0],
586
+ new_puzzle_hash=BURN_PUZZLE_HASH_2,
587
+ )
588
+ assert spend_bundle2 is not None
589
+ tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle2)
590
+ status, err = await respond_transaction(full_node_1, tx2, peer, test=True)
591
+
592
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
593
+ sb2 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle2.name())
594
+
595
+ assert err == Err.MEMPOOL_CONFLICT
596
+ assert sb1 == spend_bundle1
597
+ assert sb2 is None
598
+ assert status == MempoolInclusionStatus.PENDING
599
+
600
+ @pytest.mark.anyio
601
+ async def test_double_spend_with_higher_fee(
602
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
603
+ ) -> None:
604
+ full_node_1, _, bt = one_node_one_block
605
+ blocks = await full_node_1.get_all_full_blocks()
606
+ start_height = blocks[-1].height if len(blocks) > 0 else -1
607
+ reward_ph = wallet_a.get_new_puzzlehash()
608
+ blocks = bt.get_consecutive_blocks(
609
+ 3,
610
+ block_list_input=blocks,
611
+ guarantee_transaction_block=True,
612
+ farmer_reward_puzzle_hash=reward_ph,
613
+ pool_reward_puzzle_hash=reward_ph,
614
+ )
615
+
616
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
617
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
618
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
619
+
620
+ coins = iter(blocks[-1].get_included_reward_coins())
621
+ coin1, coin2 = next(coins), next(coins)
622
+ coins = iter(blocks[-2].get_included_reward_coins())
623
+ coin3, coin4 = next(coins), next(coins)
624
+
625
+ sb1_1 = await gen_and_send_sb(full_node_1, wallet_a, coin1)
626
+ sb1_2 = await gen_and_send_sb(full_node_1, wallet_a, coin1, fee=uint64(1))
627
+
628
+ # Fee increase is insufficient, the old spendbundle must stay
629
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1_1)
630
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_2)
631
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
632
+
633
+ sb1_3 = await gen_and_send_sb(full_node_1, wallet_a, coin1, fee=MEMPOOL_MIN_FEE_INCREASE)
634
+
635
+ # Fee increase is sufficiently high, sb1_1 gets replaced with sb1_3
636
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_1)
637
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1_3)
638
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
639
+
640
+ sb2 = generate_test_spend_bundle(wallet_a, coin2, fee=MEMPOOL_MIN_FEE_INCREASE)
641
+ sb12 = SpendBundle.aggregate([sb2, sb1_3])
642
+ await send_sb(full_node_1, sb12)
643
+
644
+ # Aggregated spendbundle sb12 replaces sb1_3 since it spends a superset
645
+ # of coins spent in sb1_3
646
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb12)
647
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_3)
648
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
649
+
650
+ sb3 = generate_test_spend_bundle(wallet_a, coin3, fee=uint64(MEMPOOL_MIN_FEE_INCREASE * 2))
651
+ sb23 = SpendBundle.aggregate([sb2, sb3])
652
+ await send_sb(full_node_1, sb23)
653
+
654
+ # sb23 must not replace existing sb12 as the former does not spend all
655
+ # coins that are spent in the latter (specifically, coin1)
656
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb12)
657
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb23)
658
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
659
+
660
+ await send_sb(full_node_1, sb3)
661
+ # Adding non-conflicting sb3 should succeed
662
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb3)
663
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
664
+
665
+ sb4_1 = generate_test_spend_bundle(wallet_a, coin4, fee=MEMPOOL_MIN_FEE_INCREASE)
666
+ sb1234_1 = SpendBundle.aggregate([sb12, sb3, sb4_1])
667
+ await send_sb(full_node_1, sb1234_1)
668
+ # sb1234_1 should not be in pool as it decreases total fees per cost
669
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1234_1)
670
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
671
+
672
+ sb4_2 = generate_test_spend_bundle(wallet_a, coin4, fee=uint64(MEMPOOL_MIN_FEE_INCREASE * 2))
673
+ sb1234_2 = SpendBundle.aggregate([sb12, sb3, sb4_2])
674
+ await send_sb(full_node_1, sb1234_2)
675
+ # sb1234_2 has a higher fee per cost than its conflicts and should get
676
+ # into mempool
677
+ assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1234_2)
678
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb12)
679
+ assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb3)
680
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
681
+
682
+ @pytest.mark.anyio
683
+ async def test_invalid_signature(
684
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
685
+ ) -> None:
686
+ reward_ph = wallet_a.get_new_puzzlehash()
687
+
688
+ full_node_1, _server_1, bt = one_node_one_block
689
+ blocks = await full_node_1.get_all_full_blocks()
690
+ start_height = blocks[-1].height if len(blocks) > 0 else -1
691
+ blocks = bt.get_consecutive_blocks(
692
+ 3,
693
+ block_list_input=blocks,
694
+ guarantee_transaction_block=True,
695
+ farmer_reward_puzzle_hash=reward_ph,
696
+ pool_reward_puzzle_hash=reward_ph,
697
+ )
698
+
699
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
700
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
701
+
702
+ coins = iter(blocks[-1].get_included_reward_coins())
703
+ coin1 = next(coins)
704
+
705
+ sb: SpendBundle = generate_test_spend_bundle(wallet_a, coin1)
706
+ assert sb.aggregated_signature != G2Element.generator()
707
+ sb = sb.replace(aggregated_signature=G2Element.generator())
708
+ res: Optional[Message] = await send_sb(full_node_1, sb)
709
+ assert res is not None
710
+ ack: TransactionAck = TransactionAck.from_bytes(res.data)
711
+ assert ack.status == MempoolInclusionStatus.FAILED.value
712
+ assert ack.error == Err.BAD_AGGREGATE_SIGNATURE.name
713
+ invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
714
+
715
+ async def condition_tester(
716
+ self,
717
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
718
+ wallet_a: WalletTool,
719
+ dic: dict[ConditionOpcode, list[ConditionWithArgs]],
720
+ fee: int = 0,
721
+ num_blocks: int = 3,
722
+ coin: Optional[Coin] = None,
723
+ ) -> tuple[list[FullBlock], SpendBundle, WSChiaConnection, MempoolInclusionStatus, Optional[Err]]:
724
+ reward_ph = wallet_a.get_new_puzzlehash()
725
+ full_node_1, server_1, bt = one_node_one_block
726
+ blocks = await full_node_1.get_all_full_blocks()
727
+ start_height = blocks[-1].height
728
+ blocks = bt.get_consecutive_blocks(
729
+ num_blocks,
730
+ block_list_input=blocks,
731
+ guarantee_transaction_block=True,
732
+ farmer_reward_puzzle_hash=reward_ph,
733
+ pool_reward_puzzle_hash=reward_ph,
734
+ )
735
+ _, dummy_node_id = await add_dummy_connection(server_1, bt.config["self_hostname"], 100)
736
+ for node_id, wsc in server_1.all_connections.items():
737
+ if node_id == dummy_node_id:
738
+ dummy_peer = wsc
739
+ break
740
+ else:
741
+ raise Exception("dummy peer not found")
742
+
743
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
744
+
745
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + num_blocks)
746
+
747
+ spend_bundle1 = generate_test_spend_bundle(
748
+ wallet_a, coin or blocks[-num_blocks + 2].get_included_reward_coins()[0], dic, uint64(fee)
749
+ )
750
+
751
+ assert spend_bundle1 is not None
752
+
753
+ tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
754
+
755
+ status, err = await respond_transaction(full_node_1, tx1, dummy_peer, test=True)
756
+ return blocks, spend_bundle1, dummy_peer, status, err
757
+
758
+ @pytest.mark.anyio
759
+ async def condition_tester2(
760
+ self,
761
+ node_server_bt: tuple[FullNodeSimulator, ChiaServer, BlockTools],
762
+ wallet_a: WalletTool,
763
+ test_fun: Callable[[Coin, Coin], SpendBundle],
764
+ ) -> tuple[list[FullBlock], SpendBundle, MempoolInclusionStatus, Optional[Err]]:
765
+ reward_ph = wallet_a.get_new_puzzlehash()
766
+ full_node_1, server_1, bt = node_server_bt
767
+ blocks = await full_node_1.get_all_full_blocks()
768
+ start_height = blocks[-1].height if len(blocks) > 0 else -1
769
+ blocks = bt.get_consecutive_blocks(
770
+ 3,
771
+ block_list_input=blocks,
772
+ guarantee_transaction_block=True,
773
+ farmer_reward_puzzle_hash=reward_ph,
774
+ pool_reward_puzzle_hash=reward_ph,
775
+ time_per_block=10,
776
+ )
777
+ _, dummy_node_id = await add_dummy_connection(server_1, bt.config["self_hostname"], 100)
778
+ for node_id, wsc in server_1.all_connections.items():
779
+ if node_id == dummy_node_id:
780
+ dummy_peer = wsc
781
+ break
782
+ else:
783
+ raise Exception("dummy peer not found")
784
+
785
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
786
+
787
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
788
+
789
+ coin_1 = blocks[-2].get_included_reward_coins()[0]
790
+ coin_2 = blocks[-1].get_included_reward_coins()[0]
791
+
792
+ bundle = test_fun(coin_1, coin_2)
793
+
794
+ tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(bundle)
795
+ status, err = await respond_transaction(full_node_1, tx1, dummy_peer, test=True)
796
+
797
+ return blocks, bundle, status, err
798
+
799
+ @pytest.mark.anyio
800
+ async def test_invalid_block_index(
801
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
802
+ ) -> None:
803
+ full_node_1, _server_1, _bt = one_node_one_block
804
+ blocks = await full_node_1.get_all_full_blocks()
805
+ start_height = blocks[-1].height
806
+ cvp = ConditionWithArgs(
807
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
808
+ [int_to_bytes(start_height + 5)],
809
+ )
810
+ dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
811
+ blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
812
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
813
+ assert sb1 is None
814
+ # the transaction may become valid later
815
+ assert err == Err.ASSERT_HEIGHT_ABSOLUTE_FAILED
816
+ assert status == MempoolInclusionStatus.PENDING
817
+
818
+ @pytest.mark.anyio
819
+ async def test_block_index_missing_arg(
820
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
821
+ ) -> None:
822
+ full_node_1, _server_1, _bt = one_node_one_block
823
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [])
824
+ dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
825
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
826
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
827
+ assert sb1 is None
828
+ # the transaction may become valid later
829
+ assert err == Err.INVALID_CONDITION
830
+ assert status == MempoolInclusionStatus.FAILED
831
+
832
+ @pytest.mark.anyio
833
+ async def test_correct_block_index(
834
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
835
+ ) -> None:
836
+ full_node_1, _server_1, _bt = one_node_one_block
837
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(1)])
838
+ dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
839
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
840
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
841
+ assert err is None
842
+ assert sb1 == spend_bundle1
843
+ assert status == MempoolInclusionStatus.SUCCESS
844
+
845
+ @pytest.mark.anyio
846
+ async def test_block_index_garbage(
847
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
848
+ ) -> None:
849
+ full_node_1, _server_1, _bt = one_node_one_block
850
+ # garbage at the end of the argument list is ignored in consensus mode,
851
+ # but not in mempool-mode
852
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(1), b"garbage"])
853
+ dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
854
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
855
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
856
+ assert err is Err.INVALID_CONDITION
857
+ assert sb1 is None
858
+ assert status == MempoolInclusionStatus.FAILED
859
+
860
+ @pytest.mark.anyio
861
+ async def test_negative_block_index(
862
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
863
+ ) -> None:
864
+ full_node_1, _server_1, _bt = one_node_one_block
865
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(-1)])
866
+ dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
867
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
868
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
869
+ assert err is None
870
+ assert sb1 == spend_bundle1
871
+ assert status == MempoolInclusionStatus.SUCCESS
872
+
873
+ @pytest.mark.anyio
874
+ async def test_invalid_block_age(
875
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
876
+ ) -> None:
877
+ full_node_1, _server_1, _bt = one_node_one_block
878
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(5)])
879
+ dic = {cvp.opcode: [cvp]}
880
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
881
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
882
+ assert err == Err.ASSERT_HEIGHT_RELATIVE_FAILED
883
+ assert sb1 is None
884
+ # the transaction may become valid later
885
+ assert status == MempoolInclusionStatus.PENDING
886
+
887
+ @pytest.mark.anyio
888
+ async def test_block_age_missing_arg(
889
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
890
+ ) -> None:
891
+ full_node_1, _server_1, _bt = one_node_one_block
892
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [])
893
+ dic = {cvp.opcode: [cvp]}
894
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
895
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
896
+ assert err == Err.INVALID_CONDITION
897
+ assert sb1 is None
898
+ # the transaction may become valid later
899
+ assert status == MempoolInclusionStatus.FAILED
900
+
901
+ @pytest.mark.anyio
902
+ async def test_correct_block_age(
903
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
904
+ ) -> None:
905
+ full_node_1, _server_1, _bt = one_node_one_block
906
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(1)])
907
+ dic = {cvp.opcode: [cvp]}
908
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
909
+ one_node_one_block, wallet_a, dic, num_blocks=4
910
+ )
911
+
912
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
913
+ assert err is None
914
+ assert sb1 == spend_bundle1
915
+ assert status == MempoolInclusionStatus.SUCCESS
916
+
917
+ @pytest.mark.anyio
918
+ async def test_block_age_garbage(
919
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
920
+ ) -> None:
921
+ full_node_1, _server_1, _bt = one_node_one_block
922
+ # garbage at the end of the argument list is ignored in consensus mode,
923
+ # but not in mempool mode
924
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(1), b"garbage"])
925
+ dic = {cvp.opcode: [cvp]}
926
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
927
+ one_node_one_block, wallet_a, dic, num_blocks=4
928
+ )
929
+
930
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
931
+ assert err is Err.INVALID_CONDITION
932
+ assert sb1 is None
933
+ assert status == MempoolInclusionStatus.FAILED
934
+
935
+ @pytest.mark.anyio
936
+ async def test_negative_block_age(
937
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
938
+ ) -> None:
939
+ full_node_1, _server_1, _bt = one_node_one_block
940
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(-1)])
941
+ dic = {cvp.opcode: [cvp]}
942
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
943
+ one_node_one_block, wallet_a, dic, num_blocks=4
944
+ )
945
+
946
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
947
+ assert err is None
948
+ assert sb1 == spend_bundle1
949
+ assert status == MempoolInclusionStatus.SUCCESS
950
+
951
+ @pytest.mark.anyio
952
+ async def test_correct_my_id(
953
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
954
+ ) -> None:
955
+ full_node_1, _server_1, bt = one_node_one_block
956
+
957
+ _ = await next_block(full_node_1, wallet_a, bt)
958
+ _ = await next_block(full_node_1, wallet_a, bt)
959
+ coin = await next_block(full_node_1, wallet_a, bt)
960
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin.name()])
961
+ dic = {cvp.opcode: [cvp]}
962
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
963
+ one_node_one_block, wallet_a, dic, coin=coin
964
+ )
965
+
966
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
967
+ assert err is None
968
+ assert sb1 == spend_bundle1
969
+ assert status == MempoolInclusionStatus.SUCCESS
970
+
971
+ @pytest.mark.anyio
972
+ async def test_my_id_garbage(
973
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
974
+ ) -> None:
975
+ full_node_1, _server_1, bt = one_node_one_block
976
+
977
+ _ = await next_block(full_node_1, wallet_a, bt)
978
+ _ = await next_block(full_node_1, wallet_a, bt)
979
+ coin = await next_block(full_node_1, wallet_a, bt)
980
+ # garbage at the end of the argument list is ignored in consensus mode,
981
+ # but not in mempool mode
982
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin.name(), b"garbage"])
983
+ dic = {cvp.opcode: [cvp]}
984
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
985
+ one_node_one_block, wallet_a, dic, coin=coin
986
+ )
987
+
988
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
989
+ assert err is Err.INVALID_CONDITION
990
+ assert sb1 is None
991
+ assert status == MempoolInclusionStatus.FAILED
992
+
993
+ @pytest.mark.anyio
994
+ async def test_invalid_my_id(
995
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
996
+ ) -> None:
997
+ full_node_1, _server_1, bt = one_node_one_block
998
+
999
+ _ = await next_block(full_node_1, wallet_a, bt)
1000
+ _ = await next_block(full_node_1, wallet_a, bt)
1001
+ coin = await next_block(full_node_1, wallet_a, bt)
1002
+ coin_2 = await next_block(full_node_1, wallet_a, bt)
1003
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin_2.name()])
1004
+ dic = {cvp.opcode: [cvp]}
1005
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1006
+ one_node_one_block, wallet_a, dic, coin=coin
1007
+ )
1008
+
1009
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1010
+ assert err == Err.ASSERT_MY_COIN_ID_FAILED
1011
+ assert sb1 is None
1012
+ assert status == MempoolInclusionStatus.FAILED
1013
+
1014
+ @pytest.mark.anyio
1015
+ async def test_my_id_missing_arg(
1016
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1017
+ ) -> None:
1018
+ full_node_1, _server_1, _bt = one_node_one_block
1019
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [])
1020
+ dic = {cvp.opcode: [cvp]}
1021
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1022
+
1023
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1024
+ assert err == Err.INVALID_CONDITION
1025
+ assert sb1 is None
1026
+ assert status == MempoolInclusionStatus.FAILED
1027
+
1028
+ @pytest.mark.anyio
1029
+ async def test_assert_time_exceeds(
1030
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1031
+ ) -> None:
1032
+ full_node_1, _, _ = one_node_one_block
1033
+ blockchain_peak = full_node_1.full_node.blockchain.get_peak()
1034
+ assert blockchain_peak is not None
1035
+ assert blockchain_peak.timestamp is not None
1036
+ # 5 seconds should be before the next block
1037
+ time_now = blockchain_peak.timestamp + 5
1038
+
1039
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
1040
+ dic = {cvp.opcode: [cvp]}
1041
+ _, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1042
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1043
+ assert err is None
1044
+ assert sb1 == spend_bundle1
1045
+ assert status == MempoolInclusionStatus.SUCCESS
1046
+
1047
+ @pytest.mark.anyio
1048
+ async def test_assert_time_fail(
1049
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1050
+ ) -> None:
1051
+ full_node_1, _, _ = one_node_one_block
1052
+ blockchain_peak = full_node_1.full_node.blockchain.get_peak()
1053
+ assert blockchain_peak is not None
1054
+ assert blockchain_peak.timestamp is not None
1055
+ time_now = blockchain_peak.timestamp + 1000
1056
+
1057
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
1058
+ dic = {cvp.opcode: [cvp]}
1059
+ _, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1060
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1061
+ assert err == Err.ASSERT_SECONDS_ABSOLUTE_FAILED
1062
+ assert sb1 is None
1063
+ assert status == MempoolInclusionStatus.FAILED
1064
+
1065
+ @pytest.mark.anyio
1066
+ async def test_assert_height_pending(
1067
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1068
+ ) -> None:
1069
+ full_node_1, _, _ = one_node_one_block
1070
+ blockchain_peak = full_node_1.full_node.blockchain.get_peak()
1071
+ assert blockchain_peak is not None
1072
+ current_height = blockchain_peak.height
1073
+
1074
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(current_height + 4)])
1075
+ dic = {cvp.opcode: [cvp]}
1076
+ _, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1077
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1078
+ assert err == Err.ASSERT_HEIGHT_ABSOLUTE_FAILED
1079
+ assert sb1 is None
1080
+ assert status == MempoolInclusionStatus.PENDING
1081
+
1082
+ @pytest.mark.anyio
1083
+ async def test_assert_time_negative(
1084
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1085
+ ) -> None:
1086
+ full_node_1, _server_1, _bt = one_node_one_block
1087
+ time_now = -1
1088
+
1089
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
1090
+ dic = {cvp.opcode: [cvp]}
1091
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1092
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1093
+ assert err is None
1094
+ assert sb1 == spend_bundle1
1095
+ assert status == MempoolInclusionStatus.SUCCESS
1096
+
1097
+ @pytest.mark.anyio
1098
+ async def test_assert_time_missing_arg(
1099
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1100
+ ) -> None:
1101
+ full_node_1, _server_1, _bt = one_node_one_block
1102
+
1103
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [])
1104
+ dic = {cvp.opcode: [cvp]}
1105
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1106
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1107
+ assert err == Err.INVALID_CONDITION
1108
+ assert sb1 is None
1109
+ assert status == MempoolInclusionStatus.FAILED
1110
+
1111
+ @pytest.mark.anyio
1112
+ async def test_assert_time_garbage(
1113
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1114
+ ) -> None:
1115
+ full_node_1, _, _ = one_node_one_block
1116
+ blockchain_peak = full_node_1.full_node.blockchain.get_peak()
1117
+ assert blockchain_peak is not None
1118
+ assert blockchain_peak.timestamp is not None
1119
+ time_now = blockchain_peak.timestamp + 5
1120
+
1121
+ # garbage at the end of the argument list is ignored in consensus mode,
1122
+ # but not in mempool mode
1123
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now), b"garbage"])
1124
+ dic = {cvp.opcode: [cvp]}
1125
+ _, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1126
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1127
+ assert err is Err.INVALID_CONDITION
1128
+ assert sb1 is None
1129
+ assert status == MempoolInclusionStatus.FAILED
1130
+
1131
+ @pytest.mark.anyio
1132
+ async def test_assert_time_relative_exceeds(
1133
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1134
+ ) -> None:
1135
+ full_node_1, _server_1, _bt = one_node_one_block
1136
+ time_relative = 3
1137
+
1138
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative)])
1139
+ dic = {cvp.opcode: [cvp]}
1140
+ _blocks, spend_bundle1, peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1141
+
1142
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1143
+ assert err == Err.ASSERT_SECONDS_RELATIVE_FAILED
1144
+ assert sb1 is None
1145
+ assert status == MempoolInclusionStatus.FAILED
1146
+
1147
+ for i in range(0, 4):
1148
+ await full_node_1.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
1149
+
1150
+ tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
1151
+
1152
+ status, err = await respond_transaction(full_node_1, tx2, peer, test=True)
1153
+
1154
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1155
+ assert err is None
1156
+ assert sb1 == spend_bundle1
1157
+ assert status == MempoolInclusionStatus.SUCCESS
1158
+
1159
+ @pytest.mark.anyio
1160
+ async def test_assert_time_relative_garbage(
1161
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1162
+ ) -> None:
1163
+ full_node_1, _server_1, _bt = one_node_one_block
1164
+ time_relative = 0
1165
+
1166
+ # garbage at the end of the arguments is ignored in consensus mode, but
1167
+ # not in mempool mode
1168
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative), b"garbage"])
1169
+ dic = {cvp.opcode: [cvp]}
1170
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1171
+
1172
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1173
+ assert err is Err.INVALID_CONDITION
1174
+ assert sb1 is None
1175
+ assert status == MempoolInclusionStatus.FAILED
1176
+
1177
+ @pytest.mark.anyio
1178
+ async def test_assert_time_relative_missing_arg(
1179
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1180
+ ) -> None:
1181
+ full_node_1, _server_1, _bt = one_node_one_block
1182
+
1183
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [])
1184
+ dic = {cvp.opcode: [cvp]}
1185
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1186
+
1187
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1188
+ assert err == Err.INVALID_CONDITION
1189
+ assert sb1 is None
1190
+ assert status == MempoolInclusionStatus.FAILED
1191
+
1192
+ @pytest.mark.anyio
1193
+ async def test_assert_time_relative_negative(
1194
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1195
+ ) -> None:
1196
+ full_node_1, _server_1, _bt = one_node_one_block
1197
+ time_relative = -3
1198
+
1199
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative)])
1200
+ dic = {cvp.opcode: [cvp]}
1201
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1202
+
1203
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1204
+ assert err is None
1205
+ assert sb1 == spend_bundle1
1206
+ assert status == MempoolInclusionStatus.SUCCESS
1207
+
1208
+ # ensure one spend can assert a coin announcement from another spend
1209
+ @pytest.mark.anyio
1210
+ async def test_correct_coin_announcement_consumed(
1211
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1212
+ ) -> None:
1213
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1214
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
1215
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
1216
+ dic = {cvp.opcode: [cvp]}
1217
+
1218
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
1219
+ dic2 = {cvp.opcode: [cvp2]}
1220
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1221
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1222
+ bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1223
+ return bundle
1224
+
1225
+ full_node_1, _server_1, _bt = one_node_one_block
1226
+ _blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1227
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1228
+
1229
+ assert err is None
1230
+ assert mempool_bundle == bundle
1231
+ assert status == MempoolInclusionStatus.SUCCESS
1232
+
1233
+ # ensure one spend can assert a coin announcement from another spend, even
1234
+ # though the conditions have garbage at the end
1235
+ @pytest.mark.anyio
1236
+ @pytest.mark.parametrize(
1237
+ "assert_garbage,announce_garbage,expected,expected_included",
1238
+ [
1239
+ (True, False, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
1240
+ (False, True, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
1241
+ (False, False, None, MempoolInclusionStatus.SUCCESS),
1242
+ ],
1243
+ )
1244
+ async def test_coin_announcement_garbage(
1245
+ self,
1246
+ assert_garbage: bool,
1247
+ announce_garbage: bool,
1248
+ expected: Optional[Err],
1249
+ expected_included: MempoolInclusionStatus,
1250
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1251
+ wallet_a: WalletTool,
1252
+ ) -> None:
1253
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1254
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
1255
+ # garbage at the end is ignored in consensus mode, but not in
1256
+ # mempool mode
1257
+ cvp = ConditionWithArgs(
1258
+ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
1259
+ [bytes(announce.msg_calc)] + ([b"garbage"] if announce_garbage else []),
1260
+ )
1261
+ dic = {cvp.opcode: [cvp]}
1262
+
1263
+ # garbage at the end is ignored in consensus mode, but not in
1264
+ # mempool mode
1265
+ cvp2 = ConditionWithArgs(
1266
+ ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"] + ([b"garbage"] if assert_garbage else [])
1267
+ )
1268
+ dic2 = {cvp.opcode: [cvp2]}
1269
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1270
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1271
+ bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1272
+ return bundle
1273
+
1274
+ full_node_1, _server_1, _bt = one_node_one_block
1275
+ _blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1276
+
1277
+ assert err is expected
1278
+ assert status == expected_included
1279
+ if status == MempoolInclusionStatus.SUCCESS:
1280
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1281
+ assert mempool_bundle == bundle
1282
+
1283
+ @pytest.mark.anyio
1284
+ async def test_coin_announcement_missing_arg(
1285
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1286
+ ) -> None:
1287
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1288
+ # missing arg here
1289
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [])
1290
+ dic = {cvp.opcode: [cvp]}
1291
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
1292
+ dic2 = {cvp.opcode: [cvp2]}
1293
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1294
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1295
+
1296
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1297
+
1298
+ full_node_1, _, _ = one_node_one_block
1299
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1300
+
1301
+ assert err == Err.INVALID_CONDITION
1302
+ assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
1303
+ assert status == MempoolInclusionStatus.FAILED
1304
+
1305
+ @pytest.mark.anyio
1306
+ async def test_coin_announcement_missing_arg2(
1307
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1308
+ ) -> None:
1309
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1310
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
1311
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
1312
+ dic = {cvp.opcode: [cvp]}
1313
+ # missing arg here
1314
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [])
1315
+ dic2 = {cvp.opcode: [cvp2]}
1316
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1317
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1318
+
1319
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1320
+
1321
+ full_node_1, _, _ = one_node_one_block
1322
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1323
+
1324
+ assert err == Err.INVALID_CONDITION
1325
+ assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
1326
+ assert status == MempoolInclusionStatus.FAILED
1327
+
1328
+ @pytest.mark.anyio
1329
+ async def test_coin_announcement_too_big(
1330
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1331
+ ) -> None:
1332
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1333
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=bytes([1] * 10000))
1334
+
1335
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
1336
+
1337
+ dic = {cvp.opcode: [cvp]}
1338
+
1339
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
1340
+ dic2 = {cvp.opcode: [cvp2]}
1341
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1342
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1343
+
1344
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1345
+
1346
+ full_node_1, _, bt = one_node_one_block
1347
+ blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1348
+
1349
+ assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1350
+ assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
1351
+ assert status == MempoolInclusionStatus.FAILED
1352
+
1353
+ blocks = bt.get_consecutive_blocks(
1354
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=bundle
1355
+ )
1356
+ try:
1357
+ await _validate_and_add_block(full_node_1.full_node.blockchain, blocks[-1])
1358
+ assert False
1359
+ except AssertionError as e:
1360
+ assert e.args[0] == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1361
+
1362
+ # ensure an assert coin announcement is rejected if it doesn't match the
1363
+ # create announcement
1364
+ @pytest.mark.anyio
1365
+ async def test_invalid_coin_announcement_rejected(
1366
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1367
+ ) -> None:
1368
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1369
+ announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
1370
+
1371
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
1372
+
1373
+ dic = {cvp.opcode: [cvp]}
1374
+ # mismatching message
1375
+ cvp2 = ConditionWithArgs(
1376
+ ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
1377
+ [b"wrong test"],
1378
+ )
1379
+ dic2 = {cvp.opcode: [cvp2]}
1380
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1381
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1382
+
1383
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1384
+
1385
+ full_node_1, _, _ = one_node_one_block
1386
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1387
+
1388
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1389
+
1390
+ assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1391
+ assert mempool_bundle is None
1392
+ assert status == MempoolInclusionStatus.FAILED
1393
+
1394
+ @pytest.mark.anyio
1395
+ async def test_invalid_coin_announcement_rejected_two(
1396
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1397
+ ) -> None:
1398
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1399
+ announce = AssertCoinAnnouncement(asserted_id=coin_1.name(), asserted_msg=b"test")
1400
+
1401
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
1402
+
1403
+ dic = {cvp.opcode: [cvp]}
1404
+
1405
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
1406
+ dic2 = {cvp.opcode: [cvp2]}
1407
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1408
+ # coin 2 is making the announcement, right message wrong coin
1409
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1410
+
1411
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1412
+
1413
+ full_node_1, _, _ = one_node_one_block
1414
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1415
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1416
+
1417
+ assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1418
+ assert mempool_bundle is None
1419
+ assert status == MempoolInclusionStatus.FAILED
1420
+
1421
+ @pytest.mark.anyio
1422
+ async def test_correct_puzzle_announcement(
1423
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1424
+ ) -> None:
1425
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1426
+ announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
1427
+
1428
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
1429
+
1430
+ dic = {cvp.opcode: [cvp]}
1431
+
1432
+ cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, [bytes(0x80)])
1433
+ dic2 = {cvp.opcode: [cvp2]}
1434
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1435
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1436
+
1437
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1438
+
1439
+ full_node_1, _, _ = one_node_one_block
1440
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1441
+
1442
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1443
+
1444
+ assert err is None
1445
+ assert mempool_bundle == bundle
1446
+ assert status == MempoolInclusionStatus.SUCCESS
1447
+
1448
+ @pytest.mark.anyio
1449
+ @pytest.mark.parametrize(
1450
+ "assert_garbage,announce_garbage,expected,expected_included",
1451
+ [
1452
+ (True, False, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
1453
+ (False, True, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
1454
+ (False, False, None, MempoolInclusionStatus.SUCCESS),
1455
+ ],
1456
+ )
1457
+ async def test_puzzle_announcement_garbage(
1458
+ self,
1459
+ assert_garbage: bool,
1460
+ announce_garbage: bool,
1461
+ expected: Optional[Err],
1462
+ expected_included: MempoolInclusionStatus,
1463
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1464
+ wallet_a: WalletTool,
1465
+ ) -> None:
1466
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1467
+ announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
1468
+
1469
+ # garbage at the end is ignored in consensus mode, but not in
1470
+ # mempool mode
1471
+ cvp = ConditionWithArgs(
1472
+ ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT,
1473
+ [bytes(announce.msg_calc)] + ([b"garbage"] if assert_garbage else []),
1474
+ )
1475
+ dic = {cvp.opcode: [cvp]}
1476
+ # garbage at the end is ignored in consensus mode, but not in
1477
+ # mempool mode
1478
+ cvp2 = ConditionWithArgs(
1479
+ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, [bytes(0x80)] + ([b"garbage"] if announce_garbage else [])
1480
+ )
1481
+ dic2 = {cvp.opcode: [cvp2]}
1482
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1483
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1484
+
1485
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1486
+
1487
+ full_node_1, _, _ = one_node_one_block
1488
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1489
+
1490
+ assert err is expected
1491
+ assert status == expected_included
1492
+ if status == MempoolInclusionStatus.SUCCESS:
1493
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1494
+ assert mempool_bundle == bundle
1495
+
1496
+ @pytest.mark.anyio
1497
+ async def test_puzzle_announcement_missing_arg(
1498
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1499
+ ) -> None:
1500
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1501
+ # missing arg here
1502
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [])
1503
+ dic = {cvp.opcode: [cvp]}
1504
+ cvp2 = ConditionWithArgs(
1505
+ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
1506
+ [b"test"],
1507
+ )
1508
+ dic2 = {cvp.opcode: [cvp2]}
1509
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1510
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1511
+
1512
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1513
+
1514
+ full_node_1, _, _ = one_node_one_block
1515
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1516
+
1517
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1518
+
1519
+ assert err == Err.INVALID_CONDITION
1520
+ assert mempool_bundle is None
1521
+ assert status == MempoolInclusionStatus.FAILED
1522
+
1523
+ @pytest.mark.anyio
1524
+ async def test_puzzle_announcement_missing_arg2(
1525
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1526
+ ) -> None:
1527
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1528
+ announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=b"test")
1529
+
1530
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
1531
+ dic = {cvp.opcode: [cvp]}
1532
+ # missing arg here
1533
+ cvp2 = ConditionWithArgs(
1534
+ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
1535
+ [],
1536
+ )
1537
+ dic2 = {cvp.opcode: [cvp2]}
1538
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1539
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1540
+
1541
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1542
+
1543
+ full_node_1, _, _ = one_node_one_block
1544
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1545
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1546
+
1547
+ assert err == Err.INVALID_CONDITION
1548
+ assert mempool_bundle is None
1549
+ assert status == MempoolInclusionStatus.FAILED
1550
+
1551
+ @pytest.mark.anyio
1552
+ async def test_invalid_puzzle_announcement_rejected(
1553
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1554
+ ) -> None:
1555
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1556
+ announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes("test", "utf-8"))
1557
+
1558
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
1559
+
1560
+ dic = {cvp.opcode: [cvp]}
1561
+
1562
+ cvp2 = ConditionWithArgs(
1563
+ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
1564
+ [b"wrong test"],
1565
+ )
1566
+ dic2 = {cvp.opcode: [cvp2]}
1567
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1568
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1569
+
1570
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1571
+
1572
+ full_node_1, _, _ = one_node_one_block
1573
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1574
+
1575
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1576
+
1577
+ assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1578
+ assert mempool_bundle is None
1579
+ assert status == MempoolInclusionStatus.FAILED
1580
+
1581
+ @pytest.mark.anyio
1582
+ async def test_invalid_puzzle_announcement_rejected_two(
1583
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1584
+ ) -> None:
1585
+ def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
1586
+ announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
1587
+
1588
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
1589
+
1590
+ dic = {cvp.opcode: [cvp]}
1591
+ # Wrong type of Create_announcement
1592
+ cvp2 = ConditionWithArgs(
1593
+ ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
1594
+ [b"test"],
1595
+ )
1596
+ dic2 = {cvp.opcode: [cvp2]}
1597
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
1598
+ spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
1599
+
1600
+ return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1601
+
1602
+ full_node_1, _, _ = one_node_one_block
1603
+ _, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
1604
+
1605
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
1606
+
1607
+ assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
1608
+ assert mempool_bundle is None
1609
+ assert status == MempoolInclusionStatus.FAILED
1610
+
1611
+ @pytest.mark.anyio
1612
+ async def test_assert_fee_condition(
1613
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1614
+ ) -> None:
1615
+ full_node_1, _server_1, _bt = one_node_one_block
1616
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
1617
+ dic = {cvp.opcode: [cvp]}
1618
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1619
+ one_node_one_block, wallet_a, dic, fee=10
1620
+ )
1621
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1622
+
1623
+ assert err is None
1624
+ assert mempool_bundle is not None
1625
+ assert status == MempoolInclusionStatus.SUCCESS
1626
+
1627
+ @pytest.mark.anyio
1628
+ async def test_assert_fee_condition_garbage(
1629
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1630
+ ) -> None:
1631
+ full_node_1, _server_1, _bt = one_node_one_block
1632
+ # garbage at the end of the arguments is ignored in consensus mode, but
1633
+ # not in mempool mode
1634
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10), b"garbage"])
1635
+ dic = {cvp.opcode: [cvp]}
1636
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1637
+ one_node_one_block, wallet_a, dic, fee=10
1638
+ )
1639
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1640
+
1641
+ assert err is Err.INVALID_CONDITION
1642
+ assert mempool_bundle is None
1643
+ assert status == MempoolInclusionStatus.FAILED
1644
+
1645
+ @pytest.mark.anyio
1646
+ async def test_assert_fee_condition_missing_arg(
1647
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1648
+ ) -> None:
1649
+ full_node_1, _server_1, _bt = one_node_one_block
1650
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [])
1651
+ dic = {cvp.opcode: [cvp]}
1652
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1653
+ one_node_one_block, wallet_a, dic, fee=10
1654
+ )
1655
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1656
+
1657
+ assert err == Err.INVALID_CONDITION
1658
+ assert mempool_bundle is None
1659
+ assert status == MempoolInclusionStatus.FAILED
1660
+
1661
+ @pytest.mark.anyio
1662
+ async def test_assert_fee_condition_negative_fee(
1663
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1664
+ ) -> None:
1665
+ full_node_1, _server_1, bt = one_node_one_block
1666
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(-1)])
1667
+ dic = {cvp.opcode: [cvp]}
1668
+ blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1669
+ one_node_one_block, wallet_a, dic, fee=10
1670
+ )
1671
+ assert err == Err.RESERVE_FEE_CONDITION_FAILED
1672
+ assert status == MempoolInclusionStatus.FAILED
1673
+ blocks = bt.get_consecutive_blocks(
1674
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=spend_bundle1
1675
+ )
1676
+ assert full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) is None
1677
+ await _validate_and_add_block(
1678
+ full_node_1.full_node.blockchain, blocks[-1], expected_error=Err.RESERVE_FEE_CONDITION_FAILED
1679
+ )
1680
+
1681
+ @pytest.mark.anyio
1682
+ async def test_assert_fee_condition_fee_too_large(
1683
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1684
+ ) -> None:
1685
+ full_node_1, _server_1, bt = one_node_one_block
1686
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(2**64)])
1687
+ dic = {cvp.opcode: [cvp]}
1688
+ blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1689
+ one_node_one_block, wallet_a, dic, fee=10
1690
+ )
1691
+ assert err == Err.RESERVE_FEE_CONDITION_FAILED
1692
+ assert status == MempoolInclusionStatus.FAILED
1693
+ blocks = bt.get_consecutive_blocks(
1694
+ 1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=spend_bundle1
1695
+ )
1696
+ assert full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) is None
1697
+ await _validate_and_add_block(
1698
+ full_node_1.full_node.blockchain, blocks[-1], expected_error=Err.RESERVE_FEE_CONDITION_FAILED
1699
+ )
1700
+
1701
+ @pytest.mark.anyio
1702
+ async def test_assert_fee_condition_wrong_fee(
1703
+ self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
1704
+ ) -> None:
1705
+ full_node_1, _server_1, _bt = one_node_one_block
1706
+
1707
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
1708
+ dic = {cvp.opcode: [cvp]}
1709
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1710
+ one_node_one_block, wallet_a, dic, fee=9
1711
+ )
1712
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1713
+
1714
+ assert err == Err.RESERVE_FEE_CONDITION_FAILED
1715
+ assert mempool_bundle is None
1716
+ assert status == MempoolInclusionStatus.FAILED
1717
+
1718
+ @pytest.mark.anyio
1719
+ async def test_stealing_fee(
1720
+ self,
1721
+ two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
1722
+ wallet_a: WalletTool,
1723
+ ) -> None:
1724
+ reward_ph = wallet_a.get_new_puzzlehash()
1725
+ full_node_1, _, server_1, server_2, bt = two_nodes_one_block
1726
+ blocks = await full_node_1.get_all_full_blocks()
1727
+ start_height = blocks[-1].height
1728
+ blocks = bt.get_consecutive_blocks(
1729
+ 5,
1730
+ block_list_input=blocks,
1731
+ guarantee_transaction_block=True,
1732
+ farmer_reward_puzzle_hash=reward_ph,
1733
+ pool_reward_puzzle_hash=reward_ph,
1734
+ )
1735
+
1736
+ peer = await connect_and_get_peer(server_1, server_2, bt.config["self_hostname"])
1737
+
1738
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
1739
+
1740
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 5)
1741
+
1742
+ receiver_puzzlehash = BURN_PUZZLE_HASH
1743
+
1744
+ cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
1745
+ dic = {cvp.opcode: [cvp]}
1746
+
1747
+ fee = 9
1748
+
1749
+ coin_1 = blocks[-2].get_included_reward_coins()[0]
1750
+ coin_2 = None
1751
+ for coin in blocks[-1].get_included_reward_coins():
1752
+ if coin.amount == coin_1.amount:
1753
+ coin_2 = coin
1754
+ assert coin_2 is not None
1755
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic, uint64(fee))
1756
+
1757
+ steal_fee_spendbundle = wallet_a.generate_signed_transaction(
1758
+ uint64(coin_1.amount + fee - 4), receiver_puzzlehash, coin_2
1759
+ )
1760
+
1761
+ assert spend_bundle1 is not None
1762
+ assert steal_fee_spendbundle is not None
1763
+
1764
+ combined = SpendBundle.aggregate([spend_bundle1, steal_fee_spendbundle])
1765
+
1766
+ assert estimate_fees(combined) == 4
1767
+
1768
+ tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
1769
+
1770
+ status, err = await respond_transaction(full_node_1, tx1, peer, test=True)
1771
+
1772
+ mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1773
+
1774
+ assert err == Err.RESERVE_FEE_CONDITION_FAILED
1775
+ assert mempool_bundle is None
1776
+ assert status == MempoolInclusionStatus.FAILED
1777
+
1778
+ @pytest.mark.anyio
1779
+ async def test_double_spend_same_bundle(
1780
+ self,
1781
+ two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
1782
+ wallet_a: WalletTool,
1783
+ ) -> None:
1784
+ reward_ph = wallet_a.get_new_puzzlehash()
1785
+ full_node_1, _, server_1, server_2, bt = two_nodes_one_block
1786
+ blocks = await full_node_1.get_all_full_blocks()
1787
+ start_height = blocks[-1].height
1788
+ blocks = bt.get_consecutive_blocks(
1789
+ 3,
1790
+ block_list_input=blocks,
1791
+ guarantee_transaction_block=True,
1792
+ farmer_reward_puzzle_hash=reward_ph,
1793
+ pool_reward_puzzle_hash=reward_ph,
1794
+ )
1795
+
1796
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
1797
+
1798
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
1799
+ # coin = blocks[-1].get_included_reward_coins()[0]
1800
+ # spend_bundle1 = generate_test_spend_bundle(wallet_a, coin)
1801
+ coin = await next_block(full_node_1, wallet_a, bt)
1802
+ spend_bundle1 = generate_test_spend_bundle(wallet_a, coin)
1803
+
1804
+ assert spend_bundle1 is not None
1805
+
1806
+ spend_bundle2 = generate_test_spend_bundle(
1807
+ wallet_a,
1808
+ coin,
1809
+ new_puzzle_hash=BURN_PUZZLE_HASH_2,
1810
+ )
1811
+
1812
+ assert spend_bundle2 is not None
1813
+
1814
+ spend_bundle_combined = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
1815
+
1816
+ tx = full_node_protocol.RespondTransaction(spend_bundle_combined)
1817
+
1818
+ peer = await connect_and_get_peer(server_1, server_2, bt.config["self_hostname"])
1819
+ status, err = await respond_transaction(full_node_1, tx, peer, test=True)
1820
+
1821
+ sb = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle_combined.name())
1822
+ assert err == Err.DOUBLE_SPEND
1823
+ assert sb is None
1824
+ assert status == MempoolInclusionStatus.FAILED
1825
+
1826
+ @pytest.mark.anyio
1827
+ async def test_agg_sig_condition(
1828
+ self,
1829
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1830
+ wallet_a: WalletTool,
1831
+ ) -> None:
1832
+ reward_ph = wallet_a.get_new_puzzlehash()
1833
+ full_node_1, _server_1, bt = one_node_one_block
1834
+ blocks = await full_node_1.get_all_full_blocks()
1835
+ start_height = blocks[-1].height
1836
+ blocks = bt.get_consecutive_blocks(
1837
+ 3,
1838
+ block_list_input=blocks,
1839
+ guarantee_transaction_block=True,
1840
+ farmer_reward_puzzle_hash=reward_ph,
1841
+ pool_reward_puzzle_hash=reward_ph,
1842
+ )
1843
+
1844
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
1845
+
1846
+ await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
1847
+
1848
+ coin = await next_block(full_node_1, wallet_a, bt)
1849
+ # coin = blocks[-1].get_included_reward_coins()[0]
1850
+ spend_bundle_0 = generate_test_spend_bundle(wallet_a, coin)
1851
+ unsigned: list[CoinSpend] = spend_bundle_0.coin_spends
1852
+
1853
+ assert len(unsigned) == 1
1854
+ # coin_spend: CoinSpend = unsigned[0]
1855
+
1856
+ # TODO(straya): fix this test
1857
+ # puzzle, solution = list(coin_spend.solution.as_iter())
1858
+ # conditions_dict = conditions_dict_for_solution(coin_spend.puzzle_reveal, coin_spend.solution, INFINITE_COST)
1859
+
1860
+ # pkm_pairs = pkm_pairs_for_conditions_dict(conditions_dict, coin_spend.coin.name())
1861
+ # assert len(pkm_pairs) == 1
1862
+ #
1863
+ # assert pkm_pairs[0][1] == solution.rest().first().get_tree_hash() + coin_spend.coin.name()
1864
+ #
1865
+ # spend_bundle = wallet_a.sign_transaction(unsigned)
1866
+ # assert spend_bundle is not None
1867
+ #
1868
+ # tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle)
1869
+ # await full_node_1.add_transaction(tx, peer, test=True)
1870
+ #
1871
+ # sb = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle.name())
1872
+ # assert sb is spend_bundle
1873
+
1874
+ @pytest.mark.anyio
1875
+ async def test_correct_my_parent(
1876
+ self,
1877
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1878
+ wallet_a: WalletTool,
1879
+ ) -> None:
1880
+ full_node_1, _server_1, bt = one_node_one_block
1881
+
1882
+ _ = await next_block(full_node_1, wallet_a, bt)
1883
+ _ = await next_block(full_node_1, wallet_a, bt)
1884
+ coin = await next_block(full_node_1, wallet_a, bt)
1885
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin.parent_coin_info])
1886
+ dic = {cvp.opcode: [cvp]}
1887
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1888
+ one_node_one_block, wallet_a, dic, coin=coin
1889
+ )
1890
+
1891
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1892
+
1893
+ assert err is None
1894
+ assert sb1 == spend_bundle1
1895
+ assert status == MempoolInclusionStatus.SUCCESS
1896
+
1897
+ @pytest.mark.anyio
1898
+ async def test_my_parent_garbage(
1899
+ self,
1900
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1901
+ wallet_a: WalletTool,
1902
+ ) -> None:
1903
+ full_node_1, _server_1, bt = one_node_one_block
1904
+
1905
+ _ = await next_block(full_node_1, wallet_a, bt)
1906
+ _ = await next_block(full_node_1, wallet_a, bt)
1907
+ coin = await next_block(full_node_1, wallet_a, bt)
1908
+ # garbage at the end of the arguments list is allowed in consensus mode,
1909
+ # but not in mempool mode
1910
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin.parent_coin_info, b"garbage"])
1911
+ dic = {cvp.opcode: [cvp]}
1912
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1913
+ one_node_one_block, wallet_a, dic, coin=coin
1914
+ )
1915
+
1916
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1917
+
1918
+ assert err is Err.INVALID_CONDITION
1919
+ assert sb1 is None
1920
+ assert status == MempoolInclusionStatus.FAILED
1921
+
1922
+ @pytest.mark.anyio
1923
+ async def test_my_parent_missing_arg(
1924
+ self,
1925
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1926
+ wallet_a: WalletTool,
1927
+ ) -> None:
1928
+ full_node_1, _server_1, _bt = one_node_one_block
1929
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [])
1930
+ dic = {cvp.opcode: [cvp]}
1931
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
1932
+
1933
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1934
+
1935
+ assert err == Err.INVALID_CONDITION
1936
+ assert sb1 is None
1937
+ assert status == MempoolInclusionStatus.FAILED
1938
+
1939
+ @pytest.mark.anyio
1940
+ async def test_invalid_my_parent(
1941
+ self,
1942
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1943
+ wallet_a: WalletTool,
1944
+ ) -> None:
1945
+ full_node_1, _server_1, bt = one_node_one_block
1946
+
1947
+ _ = await next_block(full_node_1, wallet_a, bt)
1948
+ _ = await next_block(full_node_1, wallet_a, bt)
1949
+ coin = await next_block(full_node_1, wallet_a, bt)
1950
+ coin_2 = await next_block(full_node_1, wallet_a, bt)
1951
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin_2.parent_coin_info])
1952
+ dic = {cvp.opcode: [cvp]}
1953
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1954
+ one_node_one_block, wallet_a, dic, coin=coin
1955
+ )
1956
+
1957
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1958
+
1959
+ assert err == Err.ASSERT_MY_PARENT_ID_FAILED
1960
+ assert sb1 is None
1961
+ assert status == MempoolInclusionStatus.FAILED
1962
+
1963
+ @pytest.mark.anyio
1964
+ async def test_correct_my_puzhash(
1965
+ self,
1966
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1967
+ wallet_a: WalletTool,
1968
+ ) -> None:
1969
+ full_node_1, _server_1, bt = one_node_one_block
1970
+
1971
+ _ = await next_block(full_node_1, wallet_a, bt)
1972
+ _ = await next_block(full_node_1, wallet_a, bt)
1973
+ coin = await next_block(full_node_1, wallet_a, bt)
1974
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [coin.puzzle_hash])
1975
+ dic = {cvp.opcode: [cvp]}
1976
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
1977
+ one_node_one_block, wallet_a, dic, coin=coin
1978
+ )
1979
+
1980
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
1981
+
1982
+ assert err is None
1983
+ assert sb1 == spend_bundle1
1984
+ assert status == MempoolInclusionStatus.SUCCESS
1985
+
1986
+ @pytest.mark.anyio
1987
+ async def test_my_puzhash_garbage(
1988
+ self,
1989
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
1990
+ wallet_a: WalletTool,
1991
+ ) -> None:
1992
+ full_node_1, _server_1, bt = one_node_one_block
1993
+
1994
+ _ = await next_block(full_node_1, wallet_a, bt)
1995
+ _ = await next_block(full_node_1, wallet_a, bt)
1996
+ coin = await next_block(full_node_1, wallet_a, bt)
1997
+ # garbage at the end of the arguments list is allowed but stripped
1998
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [coin.puzzle_hash, b"garbage"])
1999
+ dic = {cvp.opcode: [cvp]}
2000
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
2001
+ one_node_one_block, wallet_a, dic, coin=coin
2002
+ )
2003
+
2004
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2005
+
2006
+ assert err is Err.INVALID_CONDITION
2007
+ assert sb1 is None
2008
+ assert status == MempoolInclusionStatus.FAILED
2009
+
2010
+ @pytest.mark.anyio
2011
+ async def test_my_puzhash_missing_arg(
2012
+ self,
2013
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2014
+ wallet_a: WalletTool,
2015
+ ) -> None:
2016
+ full_node_1, _server_1, _bt = one_node_one_block
2017
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [])
2018
+ dic = {cvp.opcode: [cvp]}
2019
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
2020
+
2021
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2022
+
2023
+ assert err == Err.INVALID_CONDITION
2024
+ assert sb1 is None
2025
+ assert status == MempoolInclusionStatus.FAILED
2026
+
2027
+ @pytest.mark.anyio
2028
+ async def test_invalid_my_puzhash(
2029
+ self,
2030
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2031
+ wallet_a: WalletTool,
2032
+ ) -> None:
2033
+ full_node_1, _server_1, bt = one_node_one_block
2034
+
2035
+ _ = await next_block(full_node_1, wallet_a, bt)
2036
+ _ = await next_block(full_node_1, wallet_a, bt)
2037
+ coin = await next_block(full_node_1, wallet_a, bt)
2038
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [Program.to([]).get_tree_hash()])
2039
+ dic = {cvp.opcode: [cvp]}
2040
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
2041
+ one_node_one_block, wallet_a, dic, coin=coin
2042
+ )
2043
+
2044
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2045
+
2046
+ assert err == Err.ASSERT_MY_PUZZLEHASH_FAILED
2047
+ assert sb1 is None
2048
+ assert status == MempoolInclusionStatus.FAILED
2049
+
2050
+ @pytest.mark.anyio
2051
+ async def test_correct_my_amount(
2052
+ self,
2053
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2054
+ wallet_a: WalletTool,
2055
+ ) -> None:
2056
+ full_node_1, _server_1, bt = one_node_one_block
2057
+
2058
+ _ = await next_block(full_node_1, wallet_a, bt)
2059
+ _ = await next_block(full_node_1, wallet_a, bt)
2060
+ coin = await next_block(full_node_1, wallet_a, bt)
2061
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(coin.amount)])
2062
+ dic = {cvp.opcode: [cvp]}
2063
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
2064
+ one_node_one_block, wallet_a, dic, coin=coin
2065
+ )
2066
+
2067
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2068
+
2069
+ assert err is None
2070
+ assert sb1 == spend_bundle1
2071
+ assert status == MempoolInclusionStatus.SUCCESS
2072
+
2073
+ @pytest.mark.anyio
2074
+ async def test_my_amount_garbage(
2075
+ self,
2076
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2077
+ wallet_a: WalletTool,
2078
+ ) -> None:
2079
+ full_node_1, _server_1, bt = one_node_one_block
2080
+
2081
+ _ = await next_block(full_node_1, wallet_a, bt)
2082
+ _ = await next_block(full_node_1, wallet_a, bt)
2083
+ coin = await next_block(full_node_1, wallet_a, bt)
2084
+ # garbage at the end of the arguments list is allowed in consensus mode,
2085
+ # but not in mempool mode
2086
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(coin.amount), b"garbage"])
2087
+ dic = {cvp.opcode: [cvp]}
2088
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
2089
+ one_node_one_block, wallet_a, dic, coin=coin
2090
+ )
2091
+
2092
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2093
+
2094
+ assert err is Err.INVALID_CONDITION
2095
+ assert sb1 is None
2096
+ assert status == MempoolInclusionStatus.FAILED
2097
+
2098
+ @pytest.mark.anyio
2099
+ async def test_my_amount_missing_arg(
2100
+ self,
2101
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2102
+ wallet_a: WalletTool,
2103
+ ) -> None:
2104
+ full_node_1, _server_1, _bt = one_node_one_block
2105
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [])
2106
+ dic = {cvp.opcode: [cvp]}
2107
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
2108
+
2109
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2110
+
2111
+ assert err == Err.INVALID_CONDITION
2112
+ assert sb1 is None
2113
+ assert status == MempoolInclusionStatus.FAILED
2114
+
2115
+ @pytest.mark.anyio
2116
+ async def test_invalid_my_amount(
2117
+ self,
2118
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2119
+ wallet_a: WalletTool,
2120
+ ) -> None:
2121
+ full_node_1, _server_1, _bt = one_node_one_block
2122
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(1000)])
2123
+ dic = {cvp.opcode: [cvp]}
2124
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
2125
+
2126
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2127
+
2128
+ assert err == Err.ASSERT_MY_AMOUNT_FAILED
2129
+ assert sb1 is None
2130
+ assert status == MempoolInclusionStatus.FAILED
2131
+
2132
+ @pytest.mark.anyio
2133
+ async def test_negative_my_amount(
2134
+ self,
2135
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2136
+ wallet_a: WalletTool,
2137
+ ) -> None:
2138
+ full_node_1, _server_1, _bt = one_node_one_block
2139
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(-1)])
2140
+ dic = {cvp.opcode: [cvp]}
2141
+ _blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
2142
+
2143
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2144
+
2145
+ assert err == Err.ASSERT_MY_AMOUNT_FAILED
2146
+ assert sb1 is None
2147
+ assert status == MempoolInclusionStatus.FAILED
2148
+
2149
+ @pytest.mark.anyio
2150
+ async def test_my_amount_too_large(
2151
+ self,
2152
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2153
+ wallet_a: WalletTool,
2154
+ ) -> None:
2155
+ full_node_1, _, _ = one_node_one_block
2156
+ cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(2**64)])
2157
+ dic = {cvp.opcode: [cvp]}
2158
+ _, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
2159
+
2160
+ sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
2161
+
2162
+ assert err == Err.ASSERT_MY_AMOUNT_FAILED
2163
+ assert sb1 is None
2164
+ assert status == MempoolInclusionStatus.FAILED
2165
+
2166
+
2167
+ # the following tests generate generator programs and run them through get_name_puzzle_conditions()
2168
+
2169
+ COST_PER_BYTE = 12000
2170
+ MAX_BLOCK_COST_CLVM = 11000000000
2171
+
2172
+
2173
+ def generator_condition_tester(
2174
+ conditions: str,
2175
+ *,
2176
+ mempool_mode: bool = False,
2177
+ quote: bool = True,
2178
+ max_cost: int = MAX_BLOCK_COST_CLVM,
2179
+ height: uint32,
2180
+ coin_amount: int = 123,
2181
+ ) -> NPCResult:
2182
+ prg = f"(q ((0x0101010101010101010101010101010101010101010101010101010101010101 {'(q ' if quote else ''} {conditions} {')' if quote else ''} {coin_amount} (() (q . ())))))" # noqa
2183
+ print(f"program: {prg}")
2184
+ program = SerializedProgram.from_bytes(binutils.assemble(prg).as_bin())
2185
+ generator = BlockGenerator(program, [])
2186
+ print(f"len: {len(bytes(program))}")
2187
+ npc_result: NPCResult = get_name_puzzle_conditions(
2188
+ generator, max_cost, mempool_mode=mempool_mode, height=height, constants=test_constants
2189
+ )
2190
+ return npc_result
2191
+
2192
+
2193
+ class TestGeneratorConditions:
2194
+ def test_invalid_condition_args_terminator(self, softfork_height: uint32) -> None:
2195
+ # note how the condition argument list isn't correctly terminated with a
2196
+ # NIL atom. This is allowed, and all arguments beyond the ones we look
2197
+ # at are ignored, including the termination of the list
2198
+ npc_result = generator_condition_tester("(80 50 . 1)", height=softfork_height)
2199
+ assert npc_result.error is None
2200
+ assert npc_result.conds is not None
2201
+ assert len(npc_result.conds.spends) == 1
2202
+ assert npc_result.conds.spends[0].seconds_relative == 50
2203
+
2204
+ @pytest.mark.parametrize(
2205
+ "mempool,operand",
2206
+ [
2207
+ (True, -1),
2208
+ (False, -1),
2209
+ (True, 1),
2210
+ (False, 1),
2211
+ ],
2212
+ )
2213
+ def test_div(self, mempool: bool, operand: int, softfork_height: uint32) -> None:
2214
+ # op_div is disallowed on negative numbers in the mempool, and after the
2215
+ # softfork
2216
+ npc_result = generator_condition_tester(
2217
+ f"(c (c (q . 80) (c (/ (q . 50) (q . {operand})) ())) ())",
2218
+ quote=False,
2219
+ mempool_mode=mempool,
2220
+ height=softfork_height,
2221
+ )
2222
+
2223
+ # after the 2.0 hard fork, division with negative numbers is allowed
2224
+ assert npc_result.error is None
2225
+
2226
+ def test_invalid_condition_list_terminator(self, softfork_height: uint32) -> None:
2227
+ # note how the list of conditions isn't correctly terminated with a
2228
+ # NIL atom. This is a failure
2229
+ npc_result = generator_condition_tester("(80 50) . 3", height=softfork_height)
2230
+ assert npc_result.error in {Err.INVALID_CONDITION.value, Err.GENERATOR_RUNTIME_ERROR.value}
2231
+
2232
+ @pytest.mark.parametrize(
2233
+ "opcode",
2234
+ [
2235
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2236
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2237
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2238
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2239
+ ],
2240
+ )
2241
+ def test_duplicate_height_time_conditions(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
2242
+ # even though the generator outputs multiple conditions, we only
2243
+ # need to return the highest one (i.e. most strict)
2244
+ npc_result = generator_condition_tester(
2245
+ " ".join([f"({opcode.value[0]} {i})" for i in range(50, 101)]), height=softfork_height
2246
+ )
2247
+ print(npc_result)
2248
+ assert npc_result.error is None
2249
+ assert npc_result.conds is not None
2250
+ assert len(npc_result.conds.spends) == 1
2251
+
2252
+ assert len(npc_result.conds.spends) == 1
2253
+ if opcode == ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE:
2254
+ assert npc_result.conds.height_absolute == 100
2255
+ elif opcode == ConditionOpcode.ASSERT_HEIGHT_RELATIVE:
2256
+ assert npc_result.conds.spends[0].height_relative == 100
2257
+ elif opcode == ConditionOpcode.ASSERT_SECONDS_ABSOLUTE:
2258
+ assert npc_result.conds.seconds_absolute == 100
2259
+ elif opcode == ConditionOpcode.ASSERT_SECONDS_RELATIVE:
2260
+ assert npc_result.conds.spends[0].seconds_relative == 100
2261
+
2262
+ @pytest.mark.parametrize(
2263
+ "opcode",
2264
+ [
2265
+ ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
2266
+ ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
2267
+ ],
2268
+ )
2269
+ def test_just_announcement(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
2270
+ message = "a" * 1024
2271
+ # announcements are validated on the Rust side and never returned
2272
+ # back. They are either satisified or cause an immediate failure
2273
+ npc_result = generator_condition_tester(f'({opcode.value[0]} "{message}") ' * 50, height=softfork_height)
2274
+ assert npc_result.error is None
2275
+ assert npc_result.conds is not None
2276
+ assert len(npc_result.conds.spends) == 1
2277
+ # create-announcements and assert-announcements are dropped once
2278
+ # validated
2279
+
2280
+ @pytest.mark.parametrize(
2281
+ "opcode",
2282
+ [
2283
+ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
2284
+ ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT,
2285
+ ],
2286
+ )
2287
+ def test_assert_announcement_fail(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
2288
+ message = "a" * 1024
2289
+ # announcements are validated on the Rust side and never returned
2290
+ # back. They ar either satisified or cause an immediate failure
2291
+ # in this test we just assert announcements, we never make them, so
2292
+ # these should fail
2293
+ npc_result = generator_condition_tester(f'({opcode.value[0]} "{message}") ', height=softfork_height)
2294
+ print(npc_result)
2295
+ assert npc_result.error == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED.value
2296
+
2297
+ def test_multiple_reserve_fee(self, softfork_height: uint32) -> None:
2298
+ # RESERVE_FEE
2299
+ cond = 52
2300
+ # even though the generator outputs 3 conditions, we only need to return one copy
2301
+ # with all the fees accumulated
2302
+ npc_result = generator_condition_tester(f"({cond} 10) " * 3, height=softfork_height)
2303
+ assert npc_result.error is None
2304
+ assert npc_result.conds is not None
2305
+ assert npc_result.conds.reserve_fee == 30
2306
+ assert len(npc_result.conds.spends) == 1
2307
+
2308
+ def test_duplicate_outputs(self, softfork_height: uint32) -> None:
2309
+ # CREATE_COIN
2310
+ # creating multiple coins with the same properties (same parent, same
2311
+ # target puzzle hash and same amount) is not allowed. That's a consensus
2312
+ # failure.
2313
+ puzzle_hash = "abababababababababababababababab"
2314
+ npc_result = generator_condition_tester(f'(51 "{puzzle_hash}" 10) ' * 2, height=softfork_height)
2315
+ assert npc_result.error == Err.DUPLICATE_OUTPUT.value
2316
+
2317
+ def test_create_coin_cost(self, softfork_height: uint32) -> None:
2318
+ # CREATE_COIN
2319
+ puzzle_hash = "abababababababababababababababab"
2320
+
2321
+ if softfork_height >= test_constants.HARD_FORK_HEIGHT:
2322
+ generator_base_cost = 40
2323
+ else:
2324
+ generator_base_cost = 20470
2325
+
2326
+ # this max cost is exactly enough for the create coin condition
2327
+ npc_result = generator_condition_tester(
2328
+ f'(51 "{puzzle_hash}" 10) ',
2329
+ max_cost=generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value,
2330
+ height=softfork_height,
2331
+ )
2332
+ assert npc_result.error is None
2333
+ assert npc_result.conds is not None
2334
+ assert npc_result.conds.cost == generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value
2335
+ assert len(npc_result.conds.spends) == 1
2336
+ assert len(npc_result.conds.spends[0].create_coin) == 1
2337
+
2338
+ # if we subtract one from max cost, this should fail
2339
+ npc_result = generator_condition_tester(
2340
+ f'(51 "{puzzle_hash}" 10) ',
2341
+ max_cost=generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value - 1,
2342
+ height=softfork_height,
2343
+ )
2344
+ assert npc_result.error in {Err.BLOCK_COST_EXCEEDS_MAX.value, Err.INVALID_BLOCK_COST.value}
2345
+
2346
+ @pytest.mark.parametrize(
2347
+ "condition",
2348
+ [
2349
+ ConditionOpcode.AGG_SIG_PARENT,
2350
+ ConditionOpcode.AGG_SIG_PUZZLE,
2351
+ ConditionOpcode.AGG_SIG_AMOUNT,
2352
+ ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
2353
+ ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
2354
+ ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
2355
+ ConditionOpcode.AGG_SIG_UNSAFE,
2356
+ ConditionOpcode.AGG_SIG_ME,
2357
+ ],
2358
+ )
2359
+ def test_agg_sig_cost(self, condition: ConditionOpcode, softfork_height: uint32) -> None:
2360
+ pubkey = "0x" + bytes(G1Element.generator()).hex()
2361
+
2362
+ if softfork_height >= test_constants.HARD_FORK_HEIGHT:
2363
+ generator_base_cost = 40
2364
+ else:
2365
+ generator_base_cost = 20512
2366
+
2367
+ expected_cost = ConditionCost.AGG_SIG.value
2368
+
2369
+ # this max cost is exactly enough for the AGG_SIG condition
2370
+ npc_result = generator_condition_tester(
2371
+ f'({condition[0]} {pubkey} "foobar") ',
2372
+ max_cost=generator_base_cost + 117 * COST_PER_BYTE + expected_cost,
2373
+ height=softfork_height,
2374
+ )
2375
+ assert npc_result.error is None
2376
+ assert npc_result.conds is not None
2377
+ assert npc_result.conds.cost == generator_base_cost + 117 * COST_PER_BYTE + expected_cost
2378
+ assert len(npc_result.conds.spends) == 1
2379
+
2380
+ # if we subtract one from max cost, this should fail
2381
+ npc_result = generator_condition_tester(
2382
+ f'({condition[0]} {pubkey} "foobar") ',
2383
+ max_cost=generator_base_cost + 117 * COST_PER_BYTE + expected_cost - 1,
2384
+ height=softfork_height,
2385
+ )
2386
+ assert npc_result.error in {
2387
+ Err.GENERATOR_RUNTIME_ERROR.value,
2388
+ Err.BLOCK_COST_EXCEEDS_MAX.value,
2389
+ Err.INVALID_BLOCK_COST.value,
2390
+ }
2391
+
2392
+ @pytest.mark.parametrize(
2393
+ "condition",
2394
+ [
2395
+ ConditionOpcode.AGG_SIG_PARENT,
2396
+ ConditionOpcode.AGG_SIG_PUZZLE,
2397
+ ConditionOpcode.AGG_SIG_AMOUNT,
2398
+ ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
2399
+ ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
2400
+ ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
2401
+ ConditionOpcode.AGG_SIG_UNSAFE,
2402
+ ConditionOpcode.AGG_SIG_ME,
2403
+ ],
2404
+ )
2405
+ @pytest.mark.parametrize("extra_arg", [' "baz"', ""])
2406
+ @pytest.mark.parametrize("mempool", [True, False])
2407
+ def test_agg_sig_extra_arg(
2408
+ self, condition: ConditionOpcode, extra_arg: str, mempool: bool, softfork_height: uint32
2409
+ ) -> None:
2410
+ pubkey = "0x" + bytes(G1Element.generator()).hex()
2411
+
2412
+ # in mempool mode, we don't allow extra arguments
2413
+ if mempool and extra_arg != "":
2414
+ expected_error = Err.INVALID_CONDITION.value
2415
+ else:
2416
+ expected_error = None
2417
+
2418
+ # this max cost is exactly enough for the AGG_SIG condition
2419
+ npc_result = generator_condition_tester(
2420
+ f'({condition[0]} {pubkey} "foobar"{extra_arg}) ',
2421
+ max_cost=11000000000,
2422
+ height=softfork_height,
2423
+ mempool_mode=mempool,
2424
+ )
2425
+ assert npc_result.error == expected_error
2426
+ if npc_result.error is None:
2427
+ assert npc_result.conds is not None
2428
+ assert len(npc_result.conds.spends) == 1
2429
+ else:
2430
+ assert npc_result.conds is None
2431
+
2432
+ def test_create_coin_different_parent(self, softfork_height: uint32) -> None:
2433
+ # if the coins we create have different parents, they are never
2434
+ # considered duplicate, even when they have the same puzzle hash and
2435
+ # amount
2436
+ puzzle_hash = "abababababababababababababababab"
2437
+ program = SerializedProgram.from_bytes(
2438
+ binutils.assemble(
2439
+ f'(q ((0x0101010101010101010101010101010101010101010101010101010101010101 (q (51 "{puzzle_hash}" 10)) 123 (() (q . ())))(0x0101010101010101010101010101010101010101010101010101010101010102 (q (51 "{puzzle_hash}" 10)) 123 (() (q . ()))) ))' # noqa
2440
+ ).as_bin()
2441
+ )
2442
+ generator = BlockGenerator(program, [])
2443
+ npc_result: NPCResult = get_name_puzzle_conditions(
2444
+ generator, MAX_BLOCK_COST_CLVM, mempool_mode=False, height=softfork_height, constants=test_constants
2445
+ )
2446
+ assert npc_result.error is None
2447
+ assert npc_result.conds is not None
2448
+ assert len(npc_result.conds.spends) == 2
2449
+ for s in npc_result.conds.spends:
2450
+ assert s.create_coin == [(puzzle_hash.encode("ascii"), 10, None)]
2451
+
2452
+ def test_create_coin_different_puzzhash(self, softfork_height: uint32) -> None:
2453
+ # CREATE_COIN
2454
+ # coins with different puzzle hashes are not considered duplicate
2455
+ puzzle_hash_1 = "abababababababababababababababab"
2456
+ puzzle_hash_2 = "cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb"
2457
+ npc_result = generator_condition_tester(
2458
+ f'(51 "{puzzle_hash_1}" 5) (51 "{puzzle_hash_2}" 5)', height=softfork_height
2459
+ )
2460
+ assert npc_result.error is None
2461
+ assert npc_result.conds is not None
2462
+ assert len(npc_result.conds.spends) == 1
2463
+ assert (puzzle_hash_1.encode("ascii"), 5, None) in npc_result.conds.spends[0].create_coin
2464
+ assert (puzzle_hash_2.encode("ascii"), 5, None) in npc_result.conds.spends[0].create_coin
2465
+
2466
+ def test_create_coin_different_amounts(self, softfork_height: uint32) -> None:
2467
+ # CREATE_COIN
2468
+ # coins with different amounts are not considered duplicate
2469
+ puzzle_hash = "abababababababababababababababab"
2470
+ npc_result = generator_condition_tester(
2471
+ f'(51 "{puzzle_hash}" 5) (51 "{puzzle_hash}" 4)', height=softfork_height
2472
+ )
2473
+ assert npc_result.error is None
2474
+ assert npc_result.conds is not None
2475
+ assert len(npc_result.conds.spends) == 1
2476
+ coins = npc_result.conds.spends[0].create_coin
2477
+ assert (puzzle_hash.encode("ascii"), 5, None) in coins
2478
+ assert (puzzle_hash.encode("ascii"), 4, None) in coins
2479
+
2480
+ def test_create_coin_with_hint(self, softfork_height: uint32) -> None:
2481
+ # CREATE_COIN
2482
+ puzzle_hash_1 = "abababababababababababababababab"
2483
+ hint = "12341234123412341234213421341234"
2484
+ npc_result = generator_condition_tester(f'(51 "{puzzle_hash_1}" 5 ("{hint}"))', height=softfork_height)
2485
+ assert npc_result.error is None
2486
+ assert npc_result.conds is not None
2487
+ assert len(npc_result.conds.spends) == 1
2488
+ coins = npc_result.conds.spends[0].create_coin
2489
+ assert coins == [(puzzle_hash_1.encode("ascii"), 5, hint.encode("ascii"))]
2490
+
2491
+ @pytest.mark.parametrize("mempool", [True, False])
2492
+ @pytest.mark.parametrize(
2493
+ "condition",
2494
+ [
2495
+ '(2 100 "foo" "bar")',
2496
+ "(100)",
2497
+ "(4 1) (2 2) (3 3)",
2498
+ '("foobar")',
2499
+ '(0x100 "foobar")',
2500
+ '(0x1ff "foobar")',
2501
+ ],
2502
+ )
2503
+ def test_unknown_condition(self, mempool: bool, condition: str, softfork_height: uint32) -> None:
2504
+ npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
2505
+ print(npc_result)
2506
+ if mempool:
2507
+ assert npc_result.error == Err.INVALID_CONDITION.value
2508
+ else:
2509
+ assert npc_result.error is None
2510
+
2511
+ @pytest.mark.parametrize("mempool", [True, False])
2512
+ @pytest.mark.parametrize(
2513
+ "condition, expect_error",
2514
+ [
2515
+ # the softfork condition must include at least 1 argument to
2516
+ # indicate its cost
2517
+ ("(90)", Err.INVALID_CONDITION.value),
2518
+ ("(90 1000000)", None),
2519
+ ],
2520
+ )
2521
+ def test_softfork_condition(
2522
+ self, mempool: bool, condition: str, expect_error: Optional[int], softfork_height: uint32
2523
+ ) -> None:
2524
+ npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
2525
+ print(npc_result)
2526
+
2527
+ # in mempool all unknown conditions are always a failure
2528
+ if mempool:
2529
+ expect_error = Err.INVALID_CONDITION.value
2530
+
2531
+ assert npc_result.error == expect_error
2532
+
2533
+ @pytest.mark.parametrize("mempool", [True, False])
2534
+ @pytest.mark.parametrize(
2535
+ "condition, expect_error",
2536
+ [
2537
+ ('(66 0 "foo") (67 0 "bar")', Err.MESSAGE_NOT_SENT_OR_RECEIVED.value),
2538
+ ('(66 0 "foo") (67 0 "foo")', None),
2539
+ ],
2540
+ )
2541
+ def test_message_condition(
2542
+ self, mempool: bool, condition: str, expect_error: Optional[int], softfork_height: uint32
2543
+ ) -> None:
2544
+ npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
2545
+ print(npc_result)
2546
+ assert npc_result.error == expect_error
2547
+
2548
+
2549
+ # the tests below are malicious generator programs
2550
+
2551
+ # this program:
2552
+ # (mod (A B)
2553
+ # (defun large_string (V N)
2554
+ # (if N (large_string (concat V V) (- N 1)) V)
2555
+ # )
2556
+ # (defun iter (V N)
2557
+ # (if N (c V (iter V (- N 1))) ())
2558
+ # )
2559
+ # (iter (c (q . 83) (c (concat (large_string 0x00 A) (q . 100)) ())) B)
2560
+ # )
2561
+ # with A=28 and B specified as {num}
2562
+
2563
+ SINGLE_ARG_INT_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa
2564
+
2565
+ # this program:
2566
+ # (mod (A B)
2567
+ # (defun large_string (V N)
2568
+ # (if N (large_string (concat V V) (- N 1)) V)
2569
+ # )
2570
+ # (defun iter (V N)
2571
+ # (if N (c (c (q . 83) (c V ())) (iter (substr V 1) (- N 1))) ())
2572
+ # )
2573
+ # (iter (concat (large_string 0x00 A) (q . 100)) B)
2574
+ # )
2575
+ # truncates the first byte of the large string being passed down for each
2576
+ # iteration, in an attempt to defeat any caching of integers by node ID.
2577
+ # substr is cheap, and no memory is copied, so we can perform a lot of these
2578
+ SINGLE_ARG_INT_SUBSTR_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 (q . 1)) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa
2579
+
2580
+ # this program:
2581
+ # (mod (A B)
2582
+ # (defun large_string (V N)
2583
+ # (if N (large_string (concat V V) (- N 1)) V)
2584
+ # )
2585
+ # (defun iter (V N)
2586
+ # (if N (c (c (q . 83) (c V ())) (iter (substr V 0 (- (strlen V) 1)) (- N 1))) ())
2587
+ # )
2588
+ # (iter (concat (large_string 0x00 A) (q . 0xffffffff)) B)
2589
+ # )
2590
+ SINGLE_ARG_INT_SUBSTR_TAIL_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 () (- (strlen 5) (q . 1))) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 25 {num})))" # noqa
2591
+
2592
+ # (mod (A B)
2593
+ # (defun large_string (V N)
2594
+ # (if N (large_string (concat V V) (- N 1)) V)
2595
+ # )
2596
+ # (defun iter (V N)
2597
+ # (if N (c (c (q . 83) (c (concat V N) ())) (iter V (- N 1))) ())
2598
+ # )
2599
+ # (iter (large_string 0x00 A) B)
2600
+ # )
2601
+ SINGLE_ARG_INT_LADDER_COND = "(a (q 2 4 (c 2 (c (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c (concat 5 11) ())) (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 24 {num})))" # noqa
2602
+
2603
+ # this program:
2604
+ # (mod (A B)
2605
+ # (defun large_message (N)
2606
+ # (lsh (q . "a") N)
2607
+ # )
2608
+ # (defun iter (V N)
2609
+ # (if N (c V (iter V (- N 1))) ())
2610
+ # )
2611
+ # (iter (c (q . 60) (c (large_message A) ())) B)
2612
+ # )
2613
+ # with B set to {num}
2614
+
2615
+ CREATE_ANNOUNCE_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (a 6 (c 2 (c 5 ()))) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 23 (q . 97) 5) (q 8184 {num})))" # noqa
2616
+
2617
+ # this program:
2618
+ # (mod (A)
2619
+ # (defun iter (V N)
2620
+ # (if N (c V (iter V (- N 1))) ())
2621
+ # )
2622
+ # (iter (q 51 "abababababababababababababababab" 1) A)
2623
+ # )
2624
+ CREATE_COIN = '(a (q 2 2 (c 2 (c (q 51 "abababababababababababababababab" 1) (c 5 ())))) (c (q 2 (i 11 (q 4 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa
2625
+
2626
+ # this program:
2627
+ # (mod (A)
2628
+ # (defun append (L B)
2629
+ # (if L
2630
+ # (c (f L) (append (r L) B))
2631
+ # (c B ())
2632
+ # )
2633
+ # )
2634
+ # (defun iter (V N)
2635
+ # (if N (c (append V N) (iter V (- N 1))) ())
2636
+ # )
2637
+ # (iter (q 51 "abababababababababababababababab") A)
2638
+ # )
2639
+ # creates {num} CREATE_COIN conditions, each with a different amount
2640
+ CREATE_UNIQUE_COINS = '(a (q 2 6 (c 2 (c (q 51 "abababababababababababababababab") (c 5 ())))) (c (q (a (i 5 (q 4 9 (a 4 (c 2 (c 13 (c 11 ()))))) (q 4 11 ())) 1) 2 (i 11 (q 4 (a 4 (c 2 (c 5 (c 11 ())))) (a 6 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa
2641
+
2642
+
2643
+ # some of the malicious tests will fail post soft-fork, this function helps test
2644
+ # the specific error to expect
2645
+ def error_for_condition(cond: ConditionOpcode) -> int:
2646
+ if cond == ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE:
2647
+ return Err.ASSERT_HEIGHT_ABSOLUTE_FAILED.value
2648
+ if cond == ConditionOpcode.ASSERT_HEIGHT_RELATIVE:
2649
+ return Err.ASSERT_HEIGHT_RELATIVE_FAILED.value
2650
+ if cond == ConditionOpcode.ASSERT_SECONDS_ABSOLUTE:
2651
+ return Err.ASSERT_SECONDS_ABSOLUTE_FAILED.value
2652
+ if cond == ConditionOpcode.ASSERT_SECONDS_RELATIVE:
2653
+ return Err.ASSERT_SECONDS_RELATIVE_FAILED.value
2654
+ if cond == ConditionOpcode.RESERVE_FEE:
2655
+ return Err.RESERVE_FEE_CONDITION_FAILED.value
2656
+ assert False
2657
+
2658
+
2659
+ class TestMaliciousGenerators:
2660
+ # TODO: create a lot of announcements. The messages can be made different by
2661
+ # using substr on a large buffer
2662
+
2663
+ # for all the height/time locks, we should only return the most strict
2664
+ # condition, not all of them
2665
+ @pytest.mark.parametrize(
2666
+ "opcode",
2667
+ [
2668
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2669
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2670
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2671
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2672
+ ],
2673
+ )
2674
+ def test_duplicate_large_integer_ladder(
2675
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2676
+ ) -> None:
2677
+ condition = SINGLE_ARG_INT_LADDER_COND.format(opcode=opcode.value[0], num=28, filler="0x00")
2678
+
2679
+ with benchmark_runner.assert_runtime(seconds=1):
2680
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2681
+
2682
+ assert npc_result.error == error_for_condition(opcode)
2683
+
2684
+ @pytest.mark.parametrize(
2685
+ "opcode",
2686
+ [
2687
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2688
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2689
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2690
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2691
+ ],
2692
+ )
2693
+ def test_duplicate_large_integer(
2694
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2695
+ ) -> None:
2696
+ condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
2697
+
2698
+ with benchmark_runner.assert_runtime(seconds=3):
2699
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2700
+
2701
+ assert npc_result.error == error_for_condition(opcode)
2702
+
2703
+ @pytest.mark.parametrize(
2704
+ "opcode",
2705
+ [
2706
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2707
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2708
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2709
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2710
+ ],
2711
+ )
2712
+ def test_duplicate_large_integer_substr(
2713
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2714
+ ) -> None:
2715
+ condition = SINGLE_ARG_INT_SUBSTR_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
2716
+
2717
+ with benchmark_runner.assert_runtime(seconds=2):
2718
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2719
+
2720
+ assert npc_result.error == error_for_condition(opcode)
2721
+
2722
+ @pytest.mark.parametrize(
2723
+ "opcode",
2724
+ [
2725
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2726
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2727
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2728
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2729
+ ],
2730
+ )
2731
+ def test_duplicate_large_integer_substr_tail(
2732
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2733
+ ) -> None:
2734
+ condition = SINGLE_ARG_INT_SUBSTR_TAIL_COND.format(
2735
+ opcode=opcode.value[0], num=280, val="0xffffffff", filler="0x00"
2736
+ )
2737
+
2738
+ with benchmark_runner.assert_runtime(seconds=1):
2739
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2740
+
2741
+ assert npc_result.error == error_for_condition(opcode)
2742
+
2743
+ @pytest.mark.parametrize(
2744
+ "opcode",
2745
+ [
2746
+ ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
2747
+ ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
2748
+ ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
2749
+ ConditionOpcode.ASSERT_SECONDS_RELATIVE,
2750
+ ],
2751
+ )
2752
+ def test_duplicate_large_integer_negative(
2753
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2754
+ ) -> None:
2755
+ condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0xff")
2756
+
2757
+ with benchmark_runner.assert_runtime(seconds=2.75):
2758
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2759
+
2760
+ assert npc_result.error is None
2761
+ assert npc_result.conds is not None
2762
+ assert len(npc_result.conds.spends) == 1
2763
+
2764
+ def test_duplicate_reserve_fee(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
2765
+ opcode = ConditionOpcode.RESERVE_FEE
2766
+ condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
2767
+
2768
+ with benchmark_runner.assert_runtime(seconds=1.5):
2769
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2770
+
2771
+ assert npc_result.error == error_for_condition(opcode)
2772
+
2773
+ def test_duplicate_reserve_fee_negative(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
2774
+ opcode = ConditionOpcode.RESERVE_FEE
2775
+ condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=200000, val=100, filler="0xff")
2776
+
2777
+ with benchmark_runner.assert_runtime(seconds=1.5):
2778
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2779
+
2780
+ # RESERVE_FEE conditions fail unconditionally if they have a negative
2781
+ # amount
2782
+ assert npc_result.error == Err.RESERVE_FEE_CONDITION_FAILED.value
2783
+ assert npc_result.conds is None
2784
+
2785
+ @pytest.mark.parametrize(
2786
+ "opcode", [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT]
2787
+ )
2788
+ def test_duplicate_coin_announces(
2789
+ self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
2790
+ ) -> None:
2791
+ # we only allow 1024 create- or assert announcements per spend
2792
+ condition = CREATE_ANNOUNCE_COND.format(opcode=opcode.value[0], num=1024)
2793
+
2794
+ with benchmark_runner.assert_runtime(seconds=14):
2795
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2796
+
2797
+ assert npc_result.error is None
2798
+ assert npc_result.conds is not None
2799
+ assert len(npc_result.conds.spends) == 1
2800
+ # coin announcements are not propagated to python, but validated in rust
2801
+ # TODO: optimize clvm to make this run in < 1 second
2802
+
2803
+ def test_create_coin_duplicates(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
2804
+ # CREATE_COIN
2805
+ # this program will emit 6000 identical CREATE_COIN conditions. However,
2806
+ # we'll just end up looking at two of them, and fail at the first
2807
+ # duplicate
2808
+ condition = CREATE_COIN.format(num=600000)
2809
+
2810
+ with benchmark_runner.assert_runtime(seconds=1.5):
2811
+ npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
2812
+
2813
+ assert npc_result.error == Err.DUPLICATE_OUTPUT.value
2814
+ assert npc_result.conds is None
2815
+
2816
+ def test_many_create_coin(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
2817
+ # CREATE_COIN
2818
+ # this program will emit many CREATE_COIN conditions, all with different
2819
+ # amounts.
2820
+ # the number 6095 was chosen carefully to not exceed the maximum cost
2821
+ condition = CREATE_UNIQUE_COINS.format(num=6094)
2822
+
2823
+ with benchmark_runner.assert_runtime(seconds=0.3):
2824
+ npc_result = generator_condition_tester(
2825
+ condition, quote=False, height=softfork_height, coin_amount=123000000
2826
+ )
2827
+
2828
+ assert npc_result.error is None
2829
+ assert npc_result.conds is not None
2830
+ assert len(npc_result.conds.spends) == 1
2831
+ spend = npc_result.conds.spends[0]
2832
+ assert len(spend.create_coin) == 6094
2833
+
2834
+ @pytest.mark.anyio
2835
+ async def test_invalid_coin_spend_coin(
2836
+ self,
2837
+ one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
2838
+ wallet_a: WalletTool,
2839
+ ) -> None:
2840
+ full_node_1, _, bt = one_node_one_block
2841
+ reward_ph = wallet_a.get_new_puzzlehash()
2842
+ blocks = bt.get_consecutive_blocks(
2843
+ 5,
2844
+ guarantee_transaction_block=True,
2845
+ farmer_reward_puzzle_hash=reward_ph,
2846
+ pool_reward_puzzle_hash=reward_ph,
2847
+ )
2848
+
2849
+ await add_blocks_in_batches(blocks, full_node_1.full_node)
2850
+
2851
+ await time_out_assert(60, node_height_at_least, True, full_node_1, blocks[-1].height)
2852
+
2853
+ spend_bundle = generate_test_spend_bundle(wallet_a, blocks[-1].get_included_reward_coins()[0])
2854
+ cs = spend_bundle.coin_spends[0]
2855
+ c = cs.coin
2856
+ coin_0 = Coin(c.parent_coin_info, bytes32([1] * 32), c.amount)
2857
+ coin_spend_0 = make_spend(coin_0, cs.puzzle_reveal, cs.solution)
2858
+ new_bundle = recursive_replace(spend_bundle, "coin_spends", [coin_spend_0] + spend_bundle.coin_spends[1:])
2859
+ assert spend_bundle is not None
2860
+ res = await full_node_1.full_node.add_transaction(new_bundle, new_bundle.name(), test=True)
2861
+ assert res == (MempoolInclusionStatus.FAILED, Err.INVALID_SPEND_BUNDLE)
2862
+
2863
+
2864
+ coins = make_test_coins()
2865
+
2866
+
2867
+ # This test makes sure we're properly sorting items by fee rate
2868
+ @pytest.mark.parametrize(
2869
+ "items,expected",
2870
+ [
2871
+ # make sure fractions of fee-rate are ordered correctly (i.e. that
2872
+ # we don't use integer division)
2873
+ (
2874
+ [
2875
+ mk_item(coins[0:1], fee=110, cost=50),
2876
+ mk_item(coins[1:2], fee=100, cost=50),
2877
+ mk_item(coins[2:3], fee=105, cost=50),
2878
+ ],
2879
+ [coins[0], coins[2], coins[1]],
2880
+ ),
2881
+ # make sure insertion order is a tie-breaker for items with the same
2882
+ # fee-rate
2883
+ (
2884
+ [
2885
+ mk_item(coins[0:1], fee=100, cost=50),
2886
+ mk_item(coins[1:2], fee=100, cost=50),
2887
+ mk_item(coins[2:3], fee=100, cost=50),
2888
+ ],
2889
+ [coins[0], coins[1], coins[2]],
2890
+ ),
2891
+ # also for items that don't pay fees
2892
+ (
2893
+ [
2894
+ mk_item(coins[2:3], fee=0, cost=50),
2895
+ mk_item(coins[1:2], fee=0, cost=50),
2896
+ mk_item(coins[0:1], fee=0, cost=50),
2897
+ ],
2898
+ [coins[2], coins[1], coins[0]],
2899
+ ),
2900
+ ],
2901
+ )
2902
+ def test_items_by_feerate(items: list[MempoolItem], expected: list[Coin]) -> None:
2903
+ fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
2904
+
2905
+ mempool_info = MempoolInfo(
2906
+ CLVMCost(uint64(11000000000 * 3)),
2907
+ FeeRate(uint64(1000000)),
2908
+ CLVMCost(uint64(11000000000)),
2909
+ )
2910
+ mempool = Mempool(mempool_info, fee_estimator)
2911
+ for i in items:
2912
+ mempool.add_to_pool(i)
2913
+
2914
+ ordered_items = list(mempool.items_by_feerate())
2915
+
2916
+ assert len(ordered_items) == len(expected)
2917
+
2918
+ last_fpc: Optional[float] = None
2919
+ for mi, expected_coin in zip(ordered_items, expected):
2920
+ assert len(mi.spend_bundle.coin_spends) == 1
2921
+ assert mi.spend_bundle.coin_spends[0].coin == expected_coin
2922
+ assert last_fpc is None or last_fpc >= mi.fee_per_cost
2923
+ last_fpc = mi.fee_per_cost
2924
+
2925
+
2926
+ def rand_hash() -> bytes32:
2927
+ rng = random.Random()
2928
+ ret = bytearray(32)
2929
+ for i in range(32):
2930
+ ret[i] = rng.getrandbits(8)
2931
+ return bytes32(ret)
2932
+
2933
+
2934
+ def item_cost(cost: int, fee_rate: float) -> MempoolItem:
2935
+ fee = cost * fee_rate
2936
+ amount = uint64(fee + 100)
2937
+ coin = Coin(rand_hash(), rand_hash(), amount)
2938
+ return mk_item([coin], cost=cost, fee=int(cost * fee_rate))
2939
+
2940
+
2941
+ @pytest.mark.parametrize(
2942
+ "items,add,expected",
2943
+ [
2944
+ # the max size is 100
2945
+ # we need to evict two items
2946
+ ([50, 25, 13, 12, 5], 10, [10, 50, 25, 13]),
2947
+ # we don't need to evict anything
2948
+ ([50, 25, 13], 10, [10, 50, 25, 13]),
2949
+ # we need to evict everything
2950
+ ([95, 5], 10, [10]),
2951
+ # we evict a single item
2952
+ ([75, 15, 9], 10, [10, 75, 15]),
2953
+ ],
2954
+ )
2955
+ def test_full_mempool(items: list[int], add: int, expected: list[int]) -> None:
2956
+ fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
2957
+
2958
+ mempool_info = MempoolInfo(
2959
+ CLVMCost(uint64(100)),
2960
+ FeeRate(uint64(1000000)),
2961
+ CLVMCost(uint64(100)),
2962
+ )
2963
+ mempool = Mempool(mempool_info, fee_estimator)
2964
+ invariant_check_mempool(mempool)
2965
+ fee_rate: float = 3.0
2966
+ for i in items:
2967
+ mempool.add_to_pool(item_cost(i, fee_rate))
2968
+ fee_rate -= 0.1
2969
+ invariant_check_mempool(mempool)
2970
+
2971
+ # now, add the item we're testing
2972
+ mempool.add_to_pool(item_cost(add, 3.1))
2973
+ invariant_check_mempool(mempool)
2974
+
2975
+ ordered_items = list(mempool.items_by_feerate())
2976
+
2977
+ assert len(ordered_items) == len(expected)
2978
+
2979
+ for mi, expected_cost in zip(ordered_items, expected):
2980
+ assert mi.cost == expected_cost
2981
+
2982
+
2983
+ @pytest.mark.parametrize("height", [True, False])
2984
+ @pytest.mark.parametrize(
2985
+ "items,expected,increase_fee",
2986
+ [
2987
+ # the max size is 100
2988
+ # the max block size is 50
2989
+ # which is also the max size for expiring transactions
2990
+ # the increasing fee will order the transactions in the reverse
2991
+ # insertion order
2992
+ ([10, 11, 12, 13, 14], [14, 13, 12, 11], True),
2993
+ # decreasing fee rate will make the last one fail to be inserted
2994
+ ([10, 11, 12, 13, 14], [10, 11, 12, 13], False),
2995
+ # the last is big enough to evict all previous ones
2996
+ ([10, 11, 12, 13, 50], [50], True),
2997
+ # the last one will not evict any earlier ones, because the fee rate is
2998
+ # lower
2999
+ ([10, 11, 12, 13, 50], [10, 11, 12, 13], False),
3000
+ ],
3001
+ )
3002
+ def test_limit_expiring_transactions(height: bool, items: list[int], expected: list[int], increase_fee: bool) -> None:
3003
+ fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
3004
+
3005
+ mempool_info = MempoolInfo(
3006
+ CLVMCost(uint64(100)),
3007
+ FeeRate(uint64(1000000)),
3008
+ CLVMCost(uint64(50)),
3009
+ )
3010
+ mempool = Mempool(mempool_info, fee_estimator)
3011
+ mempool.new_tx_block(uint32(10), uint64(100000))
3012
+ invariant_check_mempool(mempool)
3013
+
3014
+ # fill the mempool with regular transactions (without expiration)
3015
+ fee_rate: float = 3.0
3016
+ for i in range(1, 20):
3017
+ mempool.add_to_pool(item_cost(i, fee_rate))
3018
+ fee_rate -= 0.1
3019
+ invariant_check_mempool(mempool)
3020
+
3021
+ # now add the expiring transactions from the test case
3022
+ fee_rate = 2.7
3023
+ for cost in items:
3024
+ fee = cost * fee_rate
3025
+ amount = uint64(fee + 100)
3026
+ coin = Coin(rand_hash(), rand_hash(), amount)
3027
+ if height:
3028
+ ret = mempool.add_to_pool(mk_item([coin], cost=cost, fee=int(cost * fee_rate), assert_before_height=15))
3029
+ else:
3030
+ ret = mempool.add_to_pool(mk_item([coin], cost=cost, fee=int(cost * fee_rate), assert_before_seconds=10400))
3031
+ invariant_check_mempool(mempool)
3032
+ if increase_fee:
3033
+ fee_rate += 0.1
3034
+ assert ret.error is None
3035
+ else:
3036
+ fee_rate -= 0.1
3037
+
3038
+ ordered_costs = [
3039
+ item.cost
3040
+ for item in mempool.items_by_feerate()
3041
+ if item.assert_before_height is not None or item.assert_before_seconds is not None
3042
+ ]
3043
+
3044
+ assert ordered_costs == expected
3045
+
3046
+ print("")
3047
+ for item in mempool.items_by_feerate():
3048
+ if item.assert_before_seconds is not None or item.assert_before_height is not None:
3049
+ ttl = "yes"
3050
+ else:
3051
+ ttl = "No"
3052
+ print(f"- cost: {item.cost} TTL: {ttl}")
3053
+
3054
+ assert mempool.total_mempool_cost() > 90
3055
+ invariant_check_mempool(mempool)
3056
+
3057
+
3058
+ @pytest.mark.parametrize(
3059
+ "items,coin_ids,expected",
3060
+ [
3061
+ # None of these spend those coins
3062
+ (
3063
+ [mk_item(coins[0:1]), mk_item(coins[1:2]), mk_item(coins[2:3])],
3064
+ [coins[3].name(), coins[4].name()],
3065
+ [],
3066
+ ),
3067
+ # One of these spends one of the coins
3068
+ (
3069
+ [mk_item(coins[0:1]), mk_item(coins[1:2]), mk_item(coins[2:3])],
3070
+ [coins[1].name(), coins[3].name()],
3071
+ [mk_item(coins[1:2])],
3072
+ ),
3073
+ # One of these spends one another spends two
3074
+ (
3075
+ [mk_item(coins[0:1]), mk_item(coins[1:3]), mk_item(coins[2:4]), mk_item(coins[3:4])],
3076
+ [coins[2].name(), coins[3].name()],
3077
+ [mk_item(coins[1:3]), mk_item(coins[2:4]), mk_item(coins[3:4])],
3078
+ ),
3079
+ ],
3080
+ )
3081
+ def test_get_items_by_coin_ids(items: list[MempoolItem], coin_ids: list[bytes32], expected: list[MempoolItem]) -> None:
3082
+ fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
3083
+ mempool_info = MempoolInfo(
3084
+ CLVMCost(uint64(11000000000 * 3)),
3085
+ FeeRate(uint64(1000000)),
3086
+ CLVMCost(uint64(11000000000)),
3087
+ )
3088
+ mempool = Mempool(mempool_info, fee_estimator)
3089
+ for i in items:
3090
+ mempool.add_to_pool(i)
3091
+ invariant_check_mempool(mempool)
3092
+ result = mempool.get_items_by_coin_ids(coin_ids)
3093
+ assert set(result) == set(expected)
3094
+
3095
+
3096
+ @pytest.mark.anyio
3097
+ async def test_aggregating_on_a_solution_then_a_more_cost_saving_one_appears() -> None:
3098
+ def always(_: bytes32) -> bool:
3099
+ return True
3100
+
3101
+ async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
3102
+ assert False # pragma: no cover
3103
+
3104
+ def make_test_spendbundle(coin: Coin, *, fee: int = 0, with_higher_cost: bool = False) -> SpendBundle:
3105
+ conditions = []
3106
+ actual_fee = fee
3107
+ if with_higher_cost:
3108
+ conditions.extend([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i] for i in range(3)])
3109
+ actual_fee += 3
3110
+ conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - actual_fee])
3111
+ sb = spend_bundle_from_conditions(conditions, coin)
3112
+ return sb
3113
+
3114
+ def agg_and_add_sb_returning_cost_info(mempool: Mempool, spend_bundles: list[SpendBundle]) -> uint64:
3115
+ sb = SpendBundle.aggregate(spend_bundles)
3116
+ mi = mempool_item_from_spendbundle(sb)
3117
+ mempool.add_to_pool(mi)
3118
+ invariant_check_mempool(mempool)
3119
+ saved_cost = run_for_cost(
3120
+ sb.coin_spends[0].puzzle_reveal, sb.coin_spends[0].solution, len(mi.additions), mi.cost
3121
+ )
3122
+ return saved_cost
3123
+
3124
+ fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
3125
+ mempool_info = MempoolInfo(
3126
+ CLVMCost(uint64(11000000000 * 3)),
3127
+ FeeRate(uint64(1000000)),
3128
+ CLVMCost(uint64(11000000000)),
3129
+ )
3130
+ mempool = Mempool(mempool_info, fee_estimator)
3131
+ coins = [
3132
+ Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount)) for amount in range(2000000000, 2000000020, 2)
3133
+ ]
3134
+ # Create a ~10 FPC item that spends the eligible coin[0]
3135
+ sb_A = make_test_spendbundle(coins[0])
3136
+ highest_fee = 58282830
3137
+ sb_high_rate = make_test_spendbundle(coins[1], fee=highest_fee)
3138
+ agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_high_rate])
3139
+ invariant_check_mempool(mempool)
3140
+ # Create a ~2 FPC item that spends the eligible coin using the same solution A
3141
+ sb_low_rate = make_test_spendbundle(coins[2], fee=highest_fee // 5)
3142
+ saved_cost_on_solution_A = agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_low_rate])
3143
+ invariant_check_mempool(mempool)
3144
+ result = await mempool.create_bundle_from_mempool_items(
3145
+ always, get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0)
3146
+ )
3147
+ assert result is not None
3148
+ agg, _ = result
3149
+ # Make sure both items would be processed
3150
+ assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
3151
+ # Now let's add 3 x ~3 FPC items that spend the eligible coin differently
3152
+ # (solution B). It creates a higher (saved) cost than solution A
3153
+ sb_B = make_test_spendbundle(coins[0], with_higher_cost=True)
3154
+ for i in range(3, 6):
3155
+ # We're picking this fee to get a ~3 FPC, and get picked after sb_A1
3156
+ # (which has ~10 FPC) but before sb_A2 (which has ~2 FPC)
3157
+ sb_mid_rate = make_test_spendbundle(coins[i], fee=38004852 - i)
3158
+ saved_cost_on_solution_B = agg_and_add_sb_returning_cost_info(mempool, [sb_B, sb_mid_rate])
3159
+ invariant_check_mempool(mempool)
3160
+ # We'd save more cost if we went with solution B instead of A
3161
+ assert saved_cost_on_solution_B > saved_cost_on_solution_A
3162
+ # If we process everything now, the 3 x ~3 FPC items get skipped because
3163
+ # sb_A1 gets picked before them (~10 FPC), so from then on only sb_A2 (~2 FPC)
3164
+ # would get picked
3165
+ result = await mempool.create_bundle_from_mempool_items(
3166
+ always, get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0)
3167
+ )
3168
+ assert result is not None
3169
+ agg, _ = result
3170
+ # The 3 items got skipped here
3171
+ # We ran with solution A and missed bigger savings on solution B
3172
+ assert mempool.size() == 5
3173
+ assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
3174
+ invariant_check_mempool(mempool)
3175
+
3176
+
3177
+ def test_get_puzzle_and_solution_for_coin_failure() -> None:
3178
+ with pytest.raises(
3179
+ ValueError, match=f"Failed to get puzzle and solution for coin {TEST_COIN}, error: \\('coin not found', '80'\\)"
3180
+ ):
3181
+ get_puzzle_and_solution_for_coin(BlockGenerator(SerializedProgram.to(None), []), TEST_COIN, 0, test_constants)
3182
+
3183
+
3184
+ # TODO: import this from chia_rs once we bump the version we depend on
3185
+ ENABLE_KECCAK = 0x200
3186
+ ENABLE_KECCAK_OPS_OUTSIDE_GUARD = 0x100
3187
+
3188
+
3189
+ def test_flags_for_height() -> None:
3190
+ # the keccak operator is supposed to be enabled at soft-fork 6 height
3191
+ flags = get_flags_for_height_and_constants(DEFAULT_CONSTANTS.SOFT_FORK6_HEIGHT, DEFAULT_CONSTANTS)
3192
+ print(f"{flags:x}")
3193
+ assert (flags & ENABLE_KECCAK) != 0
3194
+
3195
+ flags = get_flags_for_height_and_constants(DEFAULT_CONSTANTS.SOFT_FORK6_HEIGHT - 1, DEFAULT_CONSTANTS)
3196
+ print(f"{flags:x}")
3197
+ assert (flags & ENABLE_KECCAK) == 0
3198
+
3199
+
3200
+ def test_keccak() -> None:
3201
+ # the keccak operator is 62. The assemble() function doesn't support it
3202
+ # (yet)
3203
+
3204
+ # keccak256 is available when the softfork has activated
3205
+ keccak_prg = Program.to(
3206
+ assemble(
3207
+ "(softfork (q . 1134) (q . 1) (q a (i "
3208
+ "(= "
3209
+ '(62 (q . "foobar"))'
3210
+ "(q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e))"
3211
+ "(q . 0) (q x)) (q . ())) (q . ()))"
3212
+ )
3213
+ )
3214
+
3215
+ cost, ret = keccak_prg.run_with_flags(1215, ENABLE_KECCAK, [])
3216
+ assert cost == 1215
3217
+ assert ret.atom == b""
3218
+
3219
+ # keccak is ignored when the softfork has not activated
3220
+ cost, ret = keccak_prg.run_with_flags(1215, 0, [])
3221
+ assert cost == 1215
3222
+ assert ret.atom == b""
3223
+
3224
+ # make sure keccak is actually executed, by comparing with the wrong output
3225
+ keccak_prg = Program.to(
3226
+ assemble(
3227
+ "(softfork (q . 1134) (q . 1) (q a (i "
3228
+ '(= (62 (q . "foobar")) '
3229
+ "(q . 0x58d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e))"
3230
+ "(q . 0) (q x)) (q . ())) (q . ()))"
3231
+ )
3232
+ )
3233
+ with pytest.raises(ValueError, match="clvm raise"):
3234
+ keccak_prg.run_with_flags(1215, ENABLE_KECCAK, [])
3235
+
3236
+ # keccak is ignored when the softfork has not activated
3237
+ cost, ret = keccak_prg.run_with_flags(1215, 0, [])
3238
+ assert cost == 1215
3239
+ assert ret.atom == b""
3240
+
3241
+ # === HARD FORK ===
3242
+ # new operators *outside* the softfork guard
3243
+ # keccak256 is available outside the guard with the appropriate flag
3244
+ keccak_prg = Program.to(
3245
+ assemble(
3246
+ "(a (i (= "
3247
+ '(62 (q . "foobar")) '
3248
+ "(q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) "
3249
+ "(q . 0) (q x)) (q . ()))"
3250
+ )
3251
+ )
3252
+
3253
+ cost, ret = keccak_prg.run_with_flags(994, ENABLE_KECCAK | ENABLE_KECCAK_OPS_OUTSIDE_GUARD, [])
3254
+ assert cost == 994
3255
+ assert ret.atom == b""