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