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