chia-blockchain 2.5.7rc4__py3-none-any.whl → 2.5.8rc1__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 (528) hide show
  1. chia/__init__.py +8 -4
  2. chia/_tests/blockchain/blockchain_test_utils.py +6 -8
  3. chia/_tests/blockchain/test_augmented_chain.py +4 -4
  4. chia/_tests/blockchain/test_blockchain.py +165 -190
  5. chia/_tests/blockchain/test_build_chains.py +2 -4
  6. chia/_tests/blockchain/test_get_block_generator.py +2 -3
  7. chia/_tests/clvm/coin_store.py +4 -7
  8. chia/_tests/clvm/test_clvm_step.py +4 -4
  9. chia/_tests/clvm/test_puzzle_compression.py +2 -1
  10. chia/_tests/clvm/test_puzzle_drivers.py +2 -2
  11. chia/_tests/clvm/test_singletons.py +2 -4
  12. chia/_tests/clvm/test_spend_sim.py +2 -2
  13. chia/_tests/cmds/cmd_test_utils.py +27 -45
  14. chia/_tests/cmds/test_cmd_framework.py +6 -6
  15. chia/_tests/cmds/test_daemon.py +3 -3
  16. chia/_tests/cmds/test_show.py +4 -4
  17. chia/_tests/cmds/test_tx_config_args.py +1 -2
  18. chia/_tests/cmds/testing_classes.py +4 -5
  19. chia/_tests/cmds/wallet/test_did.py +24 -27
  20. chia/_tests/cmds/wallet/test_nft.py +12 -10
  21. chia/_tests/cmds/wallet/test_vcs.py +11 -12
  22. chia/_tests/cmds/wallet/test_wallet.py +134 -89
  23. chia/_tests/conftest.py +59 -30
  24. chia/_tests/connection_utils.py +2 -2
  25. chia/_tests/core/cmds/test_beta.py +4 -4
  26. chia/_tests/core/cmds/test_keys.py +2 -3
  27. chia/_tests/core/cmds/test_wallet.py +15 -15
  28. chia/_tests/core/consensus/test_pot_iterations.py +19 -73
  29. chia/_tests/core/custom_types/test_proof_of_space.py +124 -98
  30. chia/_tests/core/daemon/test_daemon.py +11 -11
  31. chia/_tests/core/data_layer/conftest.py +2 -2
  32. chia/_tests/core/data_layer/test_data_rpc.py +28 -14
  33. chia/_tests/core/data_layer/test_data_store.py +10 -10
  34. chia/_tests/core/data_layer/util.py +11 -11
  35. chia/_tests/core/farmer/test_farmer_api.py +2 -4
  36. chia/_tests/core/full_node/full_sync/test_full_sync.py +8 -7
  37. chia/_tests/core/full_node/stores/test_block_store.py +5 -4
  38. chia/_tests/core/full_node/stores/test_coin_store.py +5 -11
  39. chia/_tests/core/full_node/stores/test_full_node_store.py +8 -8
  40. chia/_tests/core/full_node/stores/test_hint_store.py +2 -2
  41. chia/_tests/core/full_node/test_block_height_map.py +3 -4
  42. chia/_tests/core/full_node/test_conditions.py +21 -23
  43. chia/_tests/core/full_node/test_full_node.py +225 -62
  44. chia/_tests/core/full_node/test_hint_management.py +2 -4
  45. chia/_tests/core/full_node/test_performance.py +0 -1
  46. chia/_tests/core/full_node/test_prev_tx_block.py +88 -11
  47. chia/_tests/core/full_node/test_transactions.py +1 -2
  48. chia/_tests/core/full_node/test_tx_processing_queue.py +109 -25
  49. chia/_tests/core/mempool/test_mempool.py +29 -37
  50. chia/_tests/core/mempool/test_mempool_fee_estimator.py +39 -39
  51. chia/_tests/core/mempool/test_mempool_fee_protocol.py +2 -6
  52. chia/_tests/core/mempool/test_mempool_manager.py +963 -839
  53. chia/_tests/core/mempool/test_singleton_fast_forward.py +6 -6
  54. chia/_tests/core/server/serve.py +7 -7
  55. chia/_tests/core/server/test_dos.py +1 -2
  56. chia/_tests/core/server/test_event_loop.py +12 -4
  57. chia/_tests/core/server/test_loop.py +7 -8
  58. chia/_tests/core/server/test_rate_limits.py +9 -8
  59. chia/_tests/core/server/test_server.py +61 -1
  60. chia/_tests/core/services/test_services.py +2 -2
  61. chia/_tests/core/ssl/test_ssl.py +2 -2
  62. chia/_tests/core/test_cost_calculation.py +2 -6
  63. chia/_tests/core/test_farmer_harvester_rpc.py +3 -5
  64. chia/_tests/core/test_filter.py +0 -1
  65. chia/_tests/core/test_full_node_rpc.py +2 -2
  66. chia/_tests/core/test_merkle_set.py +1 -2
  67. chia/_tests/core/test_seeder.py +4 -4
  68. chia/_tests/core/util/test_config.py +4 -4
  69. chia/_tests/core/util/test_jsonify.py +2 -2
  70. chia/_tests/core/util/test_keychain.py +3 -3
  71. chia/_tests/core/util/test_lockfile.py +2 -1
  72. chia/_tests/core/util/test_log_exceptions.py +1 -2
  73. chia/_tests/core/util/test_streamable.py +17 -17
  74. chia/_tests/db/test_db_wrapper.py +3 -2
  75. chia/_tests/environments/wallet.py +14 -14
  76. chia/_tests/ether.py +4 -3
  77. chia/_tests/farmer_harvester/test_farmer.py +41 -24
  78. chia/_tests/farmer_harvester/test_farmer_harvester.py +50 -17
  79. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +27 -27
  80. chia/_tests/farmer_harvester/test_third_party_harvesters.py +21 -22
  81. chia/_tests/fee_estimation/test_fee_estimation_integration.py +18 -18
  82. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +11 -9
  83. chia/_tests/harvester/test_harvester_api.py +11 -4
  84. chia/_tests/plot_sync/test_plot_sync.py +13 -11
  85. chia/_tests/plot_sync/test_receiver.py +11 -10
  86. chia/_tests/plot_sync/test_sync_simulated.py +2 -2
  87. chia/_tests/plot_sync/util.py +1 -2
  88. chia/_tests/plotting/test_plot_manager.py +7 -6
  89. chia/_tests/plotting/test_prover.py +30 -38
  90. chia/_tests/pools/test_pool_cmdline.py +4 -6
  91. chia/_tests/pools/test_pool_rpc.py +203 -61
  92. chia/_tests/pools/test_pool_wallet.py +3 -3
  93. chia/_tests/pools/test_wallet_pool_store.py +1 -4
  94. chia/_tests/process_junit.py +2 -2
  95. chia/_tests/rpc/test_rpc_client.py +4 -4
  96. chia/_tests/rpc/test_rpc_server.py +3 -3
  97. chia/_tests/simulation/test_simulation.py +12 -25
  98. chia/_tests/solver/test_solver_service.py +13 -4
  99. chia/_tests/testconfig.py +2 -2
  100. chia/_tests/timelord/test_new_peak.py +22 -11
  101. chia/_tests/tools/test_run_block.py +0 -2
  102. chia/_tests/tools/test_virtual_project.py +2 -1
  103. chia/_tests/util/benchmarks.py +1 -0
  104. chia/_tests/util/blockchain.py +38 -36
  105. chia/_tests/util/blockchain_mock.py +11 -11
  106. chia/_tests/util/build_network_protocol_files.py +2 -1
  107. chia/_tests/util/coin_store.py +2 -1
  108. chia/_tests/util/config.py +1 -1
  109. chia/_tests/util/db_connection.py +2 -3
  110. chia/_tests/util/full_sync.py +9 -11
  111. chia/_tests/util/gen_ssl_certs.py +4 -5
  112. chia/_tests/util/get_name_puzzle_conditions.py +2 -0
  113. chia/_tests/util/misc.py +24 -24
  114. chia/_tests/util/network_protocol_data.py +20 -3
  115. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  116. chia/_tests/util/protocol_messages_json.py +292 -3
  117. chia/_tests/util/setup_nodes.py +62 -47
  118. chia/_tests/util/spend_sim.py +57 -57
  119. chia/_tests/util/test_async_pool.py +2 -3
  120. chia/_tests/util/test_chia_version.py +1 -3
  121. chia/_tests/util/test_config.py +3 -3
  122. chia/_tests/util/test_full_block_utils.py +6 -3
  123. chia/_tests/util/test_limited_semaphore.py +1 -2
  124. chia/_tests/util/test_misc.py +2 -2
  125. chia/_tests/util/test_network.py +1 -2
  126. chia/_tests/util/test_priority_mutex.py +3 -3
  127. chia/_tests/util/test_recursive_replace.py +5 -6
  128. chia/_tests/util/test_replace_str_to_bytes.py +8 -10
  129. chia/_tests/util/test_testnet_overrides.py +3 -3
  130. chia/_tests/util/time_out_assert.py +2 -2
  131. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +4 -6
  132. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +2 -4
  133. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +19 -13
  134. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +13 -13
  135. chia/_tests/wallet/cat_wallet/test_trades.py +40 -38
  136. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +2 -4
  137. chia/_tests/wallet/conftest.py +6 -6
  138. chia/_tests/wallet/db_wallet/test_db_graftroot.py +1 -1
  139. chia/_tests/wallet/db_wallet/test_dl_offers.py +34 -34
  140. chia/_tests/wallet/did_wallet/test_did.py +16 -6
  141. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +21 -21
  142. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +20 -6
  143. chia/_tests/wallet/nft_wallet/test_nft_offers.py +19 -21
  144. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +1 -2
  145. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +121 -2
  146. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +6 -9
  147. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +44 -1
  148. chia/_tests/wallet/rpc/test_wallet_rpc.py +1672 -896
  149. chia/_tests/wallet/sync/test_wallet_sync.py +43 -47
  150. chia/_tests/wallet/test_clvm_streamable.py +2 -3
  151. chia/_tests/wallet/test_coin_management.py +2 -2
  152. chia/_tests/wallet/test_conditions.py +45 -51
  153. chia/_tests/wallet/test_debug_spend_bundle.py +2 -2
  154. chia/_tests/wallet/test_new_wallet_protocol.py +4 -6
  155. chia/_tests/wallet/test_notifications.py +14 -14
  156. chia/_tests/wallet/test_signer_protocol.py +5 -5
  157. chia/_tests/wallet/test_singleton_lifecycle_fast.py +4 -3
  158. chia/_tests/wallet/test_transaction_store.py +20 -20
  159. chia/_tests/wallet/test_util.py +2 -2
  160. chia/_tests/wallet/test_wallet.py +380 -228
  161. chia/_tests/wallet/test_wallet_action_scope.py +4 -4
  162. chia/_tests/wallet/test_wallet_blockchain.py +12 -12
  163. chia/_tests/wallet/test_wallet_coin_store.py +3 -4
  164. chia/_tests/wallet/test_wallet_node.py +14 -14
  165. chia/_tests/wallet/test_wallet_test_framework.py +2 -1
  166. chia/_tests/wallet/test_wallet_utils.py +2 -3
  167. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +3 -5
  168. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +14 -15
  169. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +29 -24
  170. chia/_tests/wallet/wallet_block_tools.py +12 -11
  171. chia/_tests/weight_proof/config.py +1 -0
  172. chia/_tests/weight_proof/test_weight_proof.py +5 -4
  173. chia/apis/__init__.py +21 -0
  174. chia/apis/farmer_stub.py +102 -0
  175. chia/apis/full_node_stub.py +372 -0
  176. chia/apis/harvester_stub.py +57 -0
  177. chia/apis/introducer_stub.py +35 -0
  178. chia/apis/solver_stub.py +30 -0
  179. chia/apis/stub_protocol_registry.py +21 -0
  180. chia/apis/timelord_stub.py +39 -0
  181. chia/apis/wallet_stub.py +161 -0
  182. chia/cmds/beta.py +3 -4
  183. chia/cmds/beta_funcs.py +4 -3
  184. chia/cmds/check_wallet_db.py +4 -4
  185. chia/cmds/chia.py +1 -2
  186. chia/cmds/cmd_classes.py +11 -13
  187. chia/cmds/cmd_helpers.py +11 -11
  188. chia/cmds/cmds_util.py +15 -15
  189. chia/cmds/coin_funcs.py +6 -7
  190. chia/cmds/coins.py +2 -3
  191. chia/cmds/configure.py +1 -2
  192. chia/cmds/data.py +42 -42
  193. chia/cmds/data_funcs.py +81 -81
  194. chia/cmds/db.py +4 -5
  195. chia/cmds/db_backup_func.py +2 -2
  196. chia/cmds/db_upgrade_func.py +3 -3
  197. chia/cmds/db_validate_func.py +2 -2
  198. chia/cmds/dev/data.py +4 -4
  199. chia/cmds/dev/gh.py +5 -5
  200. chia/cmds/dev/installers.py +2 -3
  201. chia/cmds/dev/mempool.py +3 -4
  202. chia/cmds/dev/mempool_funcs.py +4 -4
  203. chia/cmds/dev/sim.py +8 -8
  204. chia/cmds/dump_keyring.py +3 -3
  205. chia/cmds/farm.py +6 -8
  206. chia/cmds/farm_funcs.py +25 -24
  207. chia/cmds/init_funcs.py +4 -4
  208. chia/cmds/keys.py +16 -18
  209. chia/cmds/keys_funcs.py +36 -36
  210. chia/cmds/netspace.py +1 -3
  211. chia/cmds/netspace_funcs.py +1 -2
  212. chia/cmds/options.py +3 -2
  213. chia/cmds/param_types.py +17 -16
  214. chia/cmds/passphrase.py +6 -7
  215. chia/cmds/passphrase_funcs.py +11 -13
  216. chia/cmds/peer.py +1 -3
  217. chia/cmds/peer_funcs.py +3 -3
  218. chia/cmds/plotnft.py +6 -7
  219. chia/cmds/plotnft_funcs.py +37 -26
  220. chia/cmds/rpc.py +3 -3
  221. chia/cmds/show.py +3 -5
  222. chia/cmds/show_funcs.py +9 -9
  223. chia/cmds/sim_funcs.py +25 -26
  224. chia/cmds/solver.py +1 -3
  225. chia/cmds/solver_funcs.py +1 -2
  226. chia/cmds/start_funcs.py +2 -2
  227. chia/cmds/wallet.py +76 -81
  228. chia/cmds/wallet_funcs.py +206 -177
  229. chia/consensus/augmented_chain.py +6 -6
  230. chia/consensus/block_body_validation.py +19 -15
  231. chia/consensus/block_creation.py +25 -21
  232. chia/consensus/block_header_validation.py +27 -13
  233. chia/consensus/block_height_map.py +3 -6
  234. chia/consensus/block_height_map_protocol.py +2 -2
  235. chia/consensus/block_record.py +2 -4
  236. chia/consensus/blockchain.py +58 -40
  237. chia/consensus/blockchain_interface.py +7 -7
  238. chia/consensus/coin_store_protocol.py +5 -6
  239. chia/consensus/condition_tools.py +4 -4
  240. chia/consensus/cost_calculator.py +2 -3
  241. chia/consensus/default_constants.py +16 -13
  242. chia/consensus/deficit.py +1 -3
  243. chia/consensus/difficulty_adjustment.py +3 -5
  244. chia/consensus/find_fork_point.py +2 -4
  245. chia/consensus/full_block_to_block_record.py +11 -13
  246. chia/consensus/generator_tools.py +2 -3
  247. chia/consensus/get_block_challenge.py +42 -26
  248. chia/consensus/get_block_generator.py +2 -3
  249. chia/consensus/make_sub_epoch_summary.py +8 -7
  250. chia/consensus/multiprocess_validation.py +31 -20
  251. chia/consensus/pos_quality.py +6 -23
  252. chia/consensus/pot_iterations.py +17 -44
  253. chia/consensus/signage_point.py +4 -5
  254. chia/consensus/vdf_info_computation.py +2 -4
  255. chia/daemon/client.py +8 -8
  256. chia/daemon/keychain_proxy.py +31 -37
  257. chia/daemon/server.py +32 -33
  258. chia/daemon/windows_signal.py +4 -3
  259. chia/data_layer/data_layer.py +86 -77
  260. chia/data_layer/data_layer_rpc_api.py +9 -9
  261. chia/data_layer/data_layer_rpc_client.py +13 -15
  262. chia/data_layer/data_layer_server.py +3 -3
  263. chia/data_layer/data_layer_util.py +14 -14
  264. chia/data_layer/data_layer_wallet.py +94 -101
  265. chia/data_layer/data_store.py +50 -50
  266. chia/data_layer/dl_wallet_store.py +9 -12
  267. chia/data_layer/download_data.py +8 -9
  268. chia/data_layer/s3_plugin_service.py +5 -9
  269. chia/data_layer/start_data_layer.py +5 -5
  270. chia/farmer/farmer.py +31 -31
  271. chia/farmer/farmer_api.py +45 -33
  272. chia/farmer/farmer_rpc_api.py +5 -4
  273. chia/farmer/farmer_rpc_client.py +6 -6
  274. chia/farmer/start_farmer.py +6 -6
  275. chia/full_node/block_store.py +13 -16
  276. chia/full_node/check_fork_next_block.py +1 -2
  277. chia/full_node/coin_store.py +15 -16
  278. chia/full_node/eligible_coin_spends.py +3 -3
  279. chia/full_node/fee_estimate_store.py +2 -3
  280. chia/full_node/fee_tracker.py +1 -2
  281. chia/full_node/full_block_utils.py +4 -4
  282. chia/full_node/full_node.py +238 -224
  283. chia/full_node/full_node_api.py +193 -150
  284. chia/full_node/full_node_rpc_api.py +53 -31
  285. chia/full_node/full_node_rpc_client.py +18 -19
  286. chia/full_node/full_node_store.py +45 -43
  287. chia/full_node/hint_management.py +2 -2
  288. chia/full_node/mempool.py +17 -19
  289. chia/full_node/mempool_manager.py +89 -42
  290. chia/full_node/pending_tx_cache.py +2 -3
  291. chia/full_node/start_full_node.py +5 -5
  292. chia/full_node/sync_store.py +3 -4
  293. chia/full_node/tx_processing_queue.py +34 -13
  294. chia/full_node/weight_proof.py +61 -48
  295. chia/harvester/harvester.py +25 -24
  296. chia/harvester/harvester_api.py +61 -38
  297. chia/harvester/harvester_rpc_api.py +10 -10
  298. chia/harvester/start_harvester.py +4 -4
  299. chia/introducer/introducer.py +3 -3
  300. chia/introducer/introducer_api.py +6 -4
  301. chia/introducer/start_introducer.py +4 -4
  302. chia/legacy/keyring.py +3 -3
  303. chia/plot_sync/delta.py +1 -2
  304. chia/plot_sync/receiver.py +20 -17
  305. chia/plot_sync/sender.py +15 -10
  306. chia/plotters/bladebit.py +7 -7
  307. chia/plotters/chiapos.py +2 -2
  308. chia/plotters/madmax.py +4 -4
  309. chia/plotters/plotters.py +4 -4
  310. chia/plotters/plotters_util.py +3 -3
  311. chia/plotting/cache.py +20 -14
  312. chia/plotting/check_plots.py +26 -35
  313. chia/plotting/create_plots.py +22 -23
  314. chia/plotting/manager.py +21 -14
  315. chia/plotting/prover.py +59 -42
  316. chia/plotting/util.py +16 -16
  317. chia/pools/pool_config.py +2 -1
  318. chia/pools/pool_puzzles.py +11 -12
  319. chia/pools/pool_wallet.py +34 -57
  320. chia/pools/pool_wallet_info.py +39 -10
  321. chia/protocols/farmer_protocol.py +8 -9
  322. chia/protocols/fee_estimate.py +3 -4
  323. chia/protocols/full_node_protocol.py +3 -4
  324. chia/protocols/harvester_protocol.py +27 -15
  325. chia/protocols/outbound_message.py +3 -3
  326. chia/protocols/pool_protocol.py +8 -9
  327. chia/protocols/shared_protocol.py +1 -2
  328. chia/protocols/solver_protocol.py +9 -2
  329. chia/protocols/timelord_protocol.py +4 -7
  330. chia/protocols/wallet_protocol.py +11 -12
  331. chia/rpc/rpc_client.py +9 -9
  332. chia/rpc/rpc_server.py +17 -17
  333. chia/rpc/util.py +2 -2
  334. chia/seeder/crawler.py +8 -8
  335. chia/seeder/crawler_api.py +21 -27
  336. chia/seeder/crawler_rpc_api.py +2 -2
  337. chia/seeder/dns_server.py +21 -21
  338. chia/seeder/start_crawler.py +4 -4
  339. chia/server/address_manager.py +15 -16
  340. chia/server/api_protocol.py +11 -11
  341. chia/server/chia_policy.py +46 -26
  342. chia/server/introducer_peers.py +2 -3
  343. chia/server/node_discovery.py +19 -19
  344. chia/server/rate_limit_numbers.py +4 -5
  345. chia/server/rate_limits.py +4 -4
  346. chia/server/resolve_peer_info.py +4 -4
  347. chia/server/server.py +49 -52
  348. chia/server/signal_handlers.py +6 -6
  349. chia/server/start_service.py +17 -17
  350. chia/server/upnp.py +4 -6
  351. chia/server/ws_connection.py +52 -37
  352. chia/simulator/add_blocks_in_batches.py +1 -3
  353. chia/simulator/block_tools.py +312 -200
  354. chia/simulator/full_node_simulator.py +56 -35
  355. chia/simulator/keyring.py +2 -3
  356. chia/simulator/setup_services.py +15 -15
  357. chia/simulator/simulator_full_node_rpc_api.py +1 -2
  358. chia/simulator/simulator_full_node_rpc_client.py +1 -2
  359. chia/simulator/simulator_protocol.py +1 -2
  360. chia/simulator/simulator_test_tools.py +3 -3
  361. chia/simulator/start_simulator.py +7 -7
  362. chia/simulator/wallet_tools.py +10 -10
  363. chia/solver/solver.py +10 -10
  364. chia/solver/solver_api.py +10 -8
  365. chia/solver/solver_rpc_api.py +2 -2
  366. chia/solver/start_solver.py +4 -4
  367. chia/ssl/cacert.pem +148 -90
  368. chia/ssl/chia_ca.crt +14 -10
  369. chia/ssl/chia_ca_old.crt +19 -0
  370. chia/ssl/create_ssl.py +4 -4
  371. chia/ssl/renewedselfsignedca.conf +4 -0
  372. chia/ssl/ssl_check.py +1 -2
  373. chia/timelord/iters_from_block.py +1 -4
  374. chia/timelord/start_timelord.py +4 -4
  375. chia/timelord/timelord.py +44 -40
  376. chia/timelord/timelord_api.py +6 -4
  377. chia/timelord/timelord_launcher.py +2 -2
  378. chia/timelord/timelord_rpc_api.py +2 -2
  379. chia/timelord/timelord_state.py +11 -12
  380. chia/types/block_protocol.py +1 -3
  381. chia/types/blockchain_format/coin.py +1 -3
  382. chia/types/blockchain_format/program.py +11 -8
  383. chia/types/blockchain_format/proof_of_space.py +123 -76
  384. chia/types/blockchain_format/tree_hash.py +3 -3
  385. chia/types/blockchain_format/vdf.py +1 -2
  386. chia/types/coin_spend.py +3 -3
  387. chia/types/mempool_item.py +5 -5
  388. chia/types/mempool_submission_status.py +2 -3
  389. chia/types/peer_info.py +1 -2
  390. chia/types/unfinished_header_block.py +3 -4
  391. chia/types/validation_state.py +1 -2
  392. chia/util/action_scope.py +8 -8
  393. chia/util/async_pool.py +5 -5
  394. chia/util/bech32m.py +1 -2
  395. chia/util/beta_metrics.py +2 -2
  396. chia/util/block_cache.py +4 -4
  397. chia/util/chia_logging.py +2 -2
  398. chia/util/chia_version.py +1 -2
  399. chia/util/config.py +15 -16
  400. chia/util/db_wrapper.py +26 -27
  401. chia/util/default_root.py +1 -2
  402. chia/util/errors.py +3 -3
  403. chia/util/file_keyring.py +14 -14
  404. chia/util/files.py +2 -3
  405. chia/util/hash.py +4 -4
  406. chia/util/initial-config.yaml +3 -5
  407. chia/util/inline_executor.py +2 -1
  408. chia/util/ip_address.py +1 -2
  409. chia/util/keychain.py +25 -27
  410. chia/util/keyring_wrapper.py +18 -19
  411. chia/util/lock.py +3 -4
  412. chia/util/log_exceptions.py +1 -2
  413. chia/util/lru_cache.py +2 -2
  414. chia/util/network.py +6 -6
  415. chia/util/path.py +2 -3
  416. chia/util/priority_mutex.py +2 -2
  417. chia/util/profiler.py +1 -2
  418. chia/util/safe_cancel_task.py +1 -2
  419. chia/util/streamable.py +22 -8
  420. chia/util/task_referencer.py +1 -1
  421. chia/util/timing.py +3 -3
  422. chia/util/virtual_project_analysis.py +6 -5
  423. chia/util/ws_message.py +2 -2
  424. chia/wallet/cat_wallet/cat_info.py +3 -4
  425. chia/wallet/cat_wallet/cat_outer_puzzle.py +12 -11
  426. chia/wallet/cat_wallet/cat_utils.py +3 -4
  427. chia/wallet/cat_wallet/cat_wallet.py +61 -83
  428. chia/wallet/cat_wallet/lineage_store.py +3 -4
  429. chia/wallet/cat_wallet/r_cat_wallet.py +19 -22
  430. chia/wallet/coin_selection.py +9 -10
  431. chia/wallet/conditions.py +120 -105
  432. chia/wallet/db_wallet/db_wallet_puzzles.py +4 -5
  433. chia/wallet/derivation_record.py +1 -2
  434. chia/wallet/derive_keys.py +2 -4
  435. chia/wallet/did_wallet/did_info.py +10 -11
  436. chia/wallet/did_wallet/did_wallet.py +36 -82
  437. chia/wallet/did_wallet/did_wallet_puzzles.py +7 -8
  438. chia/wallet/driver_protocol.py +5 -7
  439. chia/wallet/lineage_proof.py +4 -4
  440. chia/wallet/nft_wallet/metadata_outer_puzzle.py +11 -11
  441. chia/wallet/nft_wallet/nft_info.py +8 -9
  442. chia/wallet/nft_wallet/nft_puzzle_utils.py +8 -8
  443. chia/wallet/nft_wallet/nft_wallet.py +79 -116
  444. chia/wallet/nft_wallet/ownership_outer_puzzle.py +14 -14
  445. chia/wallet/nft_wallet/singleton_outer_puzzle.py +12 -11
  446. chia/wallet/nft_wallet/transfer_program_puzzle.py +11 -11
  447. chia/wallet/nft_wallet/uncurry_nft.py +10 -11
  448. chia/wallet/notification_manager.py +3 -3
  449. chia/wallet/notification_store.py +44 -61
  450. chia/wallet/outer_puzzles.py +6 -7
  451. chia/wallet/puzzle_drivers.py +34 -6
  452. chia/wallet/puzzles/clawback/drivers.py +6 -6
  453. chia/wallet/puzzles/deployed_puzzle_hashes.json +1 -54
  454. chia/wallet/puzzles/load_clvm.py +1 -1
  455. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +1 -2
  456. chia/wallet/puzzles/singleton_top_layer.py +2 -3
  457. chia/wallet/puzzles/singleton_top_layer_v1_1.py +3 -4
  458. chia/wallet/puzzles/tails.py +3 -3
  459. chia/wallet/singleton.py +5 -7
  460. chia/wallet/singleton_record.py +3 -3
  461. chia/wallet/start_wallet.py +5 -5
  462. chia/wallet/trade_manager.py +37 -58
  463. chia/wallet/trade_record.py +4 -4
  464. chia/wallet/trading/offer.py +59 -46
  465. chia/wallet/trading/trade_store.py +8 -9
  466. chia/wallet/transaction_record.py +8 -8
  467. chia/wallet/uncurried_puzzle.py +1 -2
  468. chia/wallet/util/clvm_streamable.py +12 -12
  469. chia/wallet/util/compute_hints.py +4 -5
  470. chia/wallet/util/curry_and_treehash.py +1 -2
  471. chia/wallet/util/merkle_tree.py +2 -3
  472. chia/wallet/util/peer_request_cache.py +8 -8
  473. chia/wallet/util/signing.py +85 -0
  474. chia/wallet/util/tx_config.py +15 -6
  475. chia/wallet/util/wallet_sync_utils.py +14 -16
  476. chia/wallet/util/wallet_types.py +2 -2
  477. chia/wallet/vc_wallet/cr_cat_drivers.py +10 -11
  478. chia/wallet/vc_wallet/cr_cat_wallet.py +50 -68
  479. chia/wallet/vc_wallet/cr_outer_puzzle.py +14 -13
  480. chia/wallet/vc_wallet/vc_drivers.py +27 -27
  481. chia/wallet/vc_wallet/vc_store.py +5 -6
  482. chia/wallet/vc_wallet/vc_wallet.py +33 -61
  483. chia/wallet/wallet.py +50 -78
  484. chia/wallet/wallet_action_scope.py +11 -11
  485. chia/wallet/wallet_blockchain.py +12 -12
  486. chia/wallet/wallet_coin_record.py +12 -6
  487. chia/wallet/wallet_coin_store.py +24 -25
  488. chia/wallet/wallet_interested_store.py +3 -5
  489. chia/wallet/wallet_nft_store.py +10 -11
  490. chia/wallet/wallet_node.py +53 -61
  491. chia/wallet/wallet_node_api.py +5 -3
  492. chia/wallet/wallet_protocol.py +23 -23
  493. chia/wallet/wallet_puzzle_store.py +15 -18
  494. chia/wallet/wallet_request_types.py +778 -114
  495. chia/wallet/wallet_retry_store.py +1 -3
  496. chia/wallet/wallet_rpc_api.py +572 -909
  497. chia/wallet/wallet_rpc_client.py +87 -279
  498. chia/wallet/wallet_singleton_store.py +3 -4
  499. chia/wallet/wallet_state_manager.py +332 -106
  500. chia/wallet/wallet_transaction_store.py +11 -14
  501. chia/wallet/wallet_user_store.py +4 -6
  502. chia/wallet/wallet_weight_proof_handler.py +4 -4
  503. {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.5.8rc1.dist-info}/METADATA +6 -5
  504. {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.5.8rc1.dist-info}/RECORD +507 -516
  505. chia/apis.py +0 -21
  506. chia/consensus/check_time_locks.py +0 -57
  507. chia/data_layer/puzzles/__init__.py +0 -0
  508. chia/data_layer/puzzles/graftroot_dl_offers.clsp +0 -100
  509. chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +0 -1
  510. chia/types/coin_record.py +0 -44
  511. chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
  512. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +0 -6
  513. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +0 -1
  514. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +0 -6
  515. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +0 -1
  516. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +0 -30
  517. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +0 -1
  518. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +0 -28
  519. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +0 -1
  520. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +0 -100
  521. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +0 -1
  522. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +0 -78
  523. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +0 -1
  524. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +0 -74
  525. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +0 -1
  526. {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.5.8rc1.dist-info}/WHEEL +0 -0
  527. {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.5.8rc1.dist-info}/entry_points.txt +0 -0
  528. {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.5.8rc1.dist-info}/licenses/LICENSE +0 -0
@@ -3,20 +3,23 @@ from __future__ import annotations
3
3
  import dataclasses
4
4
  import logging
5
5
  import random
6
- from collections.abc import Awaitable, Collection, Sequence
7
- from typing import Any, Callable, ClassVar, Optional, Union
6
+ from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Collection, Sequence
7
+ from contextlib import asynccontextmanager
8
+ from typing import Any, ClassVar
8
9
 
9
10
  import pytest
10
11
  from chia_rs import (
11
12
  ELIGIBLE_FOR_DEDUP,
12
13
  ELIGIBLE_FOR_FF,
13
14
  AugSchemeMPL,
15
+ CoinRecord,
14
16
  CoinSpend,
15
17
  ConsensusConstants,
16
18
  G2Element,
17
19
  SpendBundle,
18
20
  SpendBundleConditions,
19
21
  SpendConditions,
22
+ check_time_locks,
20
23
  get_conditions_from_spendbundle,
21
24
  run_block_generator2,
22
25
  )
@@ -27,7 +30,6 @@ from chiabip158 import PyBIP158
27
30
  from chia._tests.conftest import ConsensusMode
28
31
  from chia._tests.util.misc import Marks, datacases, invariant_check_mempool
29
32
  from chia._tests.util.setup_nodes import OldSimulatorsAndWallets, setup_simulators_and_wallets
30
- from chia.consensus.check_time_locks import check_time_locks
31
33
  from chia.consensus.condition_costs import ConditionCost
32
34
  from chia.consensus.default_constants import DEFAULT_CONSTANTS
33
35
  from chia.full_node.eligible_coin_spends import (
@@ -60,7 +62,6 @@ from chia.types.blockchain_format.coin import Coin
60
62
  from chia.types.blockchain_format.program import DEFAULT_FLAGS, INFINITE_COST, Program
61
63
  from chia.types.blockchain_format.serialized_program import SerializedProgram
62
64
  from chia.types.clvm_cost import CLVMCost
63
- from chia.types.coin_record import CoinRecord
64
65
  from chia.types.coin_spend import make_spend
65
66
  from chia.types.condition_opcodes import ConditionOpcode
66
67
  from chia.types.condition_with_args import ConditionWithArgs
@@ -205,9 +206,9 @@ class TestBlockRecord:
205
206
 
206
207
  header_hash: bytes32
207
208
  height: uint32
208
- timestamp: Optional[uint64]
209
+ timestamp: uint64 | None
209
210
  prev_transaction_block_height: uint32
210
- prev_transaction_block_hash: Optional[bytes32]
211
+ prev_transaction_block_hash: bytes32 | None
211
212
 
212
213
  @property
213
214
  def is_transaction_block(self) -> bool:
@@ -219,7 +220,7 @@ async def zero_calls_get_coin_records(coin_ids: Collection[bytes32]) -> list[Coi
219
220
  return []
220
221
 
221
222
 
222
- async def zero_calls_get_unspent_lineage_info_for_puzzle_hash(_puzzle_hash: bytes32) -> Optional[UnspentLineageInfo]:
223
+ async def zero_calls_get_unspent_lineage_info_for_puzzle_hash(_puzzle_hash: bytes32) -> UnspentLineageInfo | None:
223
224
  assert False # pragma no cover
224
225
 
225
226
 
@@ -252,37 +253,53 @@ def create_test_block_record(*, height: uint32 = TEST_HEIGHT, timestamp: uint64
252
253
  )
253
254
 
254
255
 
256
+ @asynccontextmanager
255
257
  async def instantiate_mempool_manager(
256
258
  get_coin_records: Callable[[Collection[bytes32]], Awaitable[list[CoinRecord]]],
257
259
  *,
258
260
  block_height: uint32 = TEST_HEIGHT,
259
261
  block_timestamp: uint64 = TEST_TIMESTAMP,
260
262
  constants: ConsensusConstants = DEFAULT_CONSTANTS,
261
- max_tx_clvm_cost: Optional[uint64] = None,
262
- ) -> MempoolManager:
263
- mempool_manager = MempoolManager(
263
+ max_tx_clvm_cost: uint64 | None = None,
264
+ ) -> AsyncGenerator[MempoolManager, None]:
265
+ async with MempoolManager.managed(
264
266
  get_coin_records,
265
267
  zero_calls_get_unspent_lineage_info_for_puzzle_hash,
266
268
  constants,
267
269
  max_tx_clvm_cost=max_tx_clvm_cost,
268
- )
269
- test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
270
- await mempool_manager.new_peak(test_block_record, None)
271
- invariant_check_mempool(mempool_manager.mempool)
272
- return mempool_manager
270
+ ) as mempool_manager:
271
+ test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
272
+ await mempool_manager.new_peak(test_block_record, None)
273
+ invariant_check_mempool(mempool_manager.mempool)
274
+ yield mempool_manager
275
+
276
+
277
+ @pytest.fixture(name="zero_mempool_manager", scope="function")
278
+ async def zero_mempool_manager_fixture() -> AsyncIterator[MempoolManager]:
279
+ async with instantiate_mempool_manager(zero_calls_get_coin_records) as mempool_manager:
280
+ yield mempool_manager
273
281
 
274
282
 
283
+ @pytest.fixture(name="test_coins_mempool_manager", scope="function")
284
+ async def test_coins_mempool_manager_fixture() -> AsyncIterator[MempoolManager]:
285
+ async with instantiate_mempool_manager(get_coin_records_for_test_coins) as mempool_manager:
286
+ yield mempool_manager
287
+
288
+
289
+ @asynccontextmanager
275
290
  async def setup_mempool_with_coins(
276
291
  *,
277
292
  coin_amounts: list[int],
278
- max_block_clvm_cost: Optional[int] = None,
279
- max_tx_clvm_cost: Optional[uint64] = None,
280
- mempool_block_buffer: Optional[int] = None,
281
- ) -> tuple[MempoolManager, list[Coin]]:
293
+ max_block_clvm_cost: int | None = None,
294
+ max_tx_clvm_cost: uint64 | None = None,
295
+ mempool_block_buffer: int | None = None,
296
+ puzzle_hash: bytes32 = IDENTITY_PUZZLE_HASH,
297
+ height: uint32 = TEST_HEIGHT,
298
+ ) -> AsyncGenerator[tuple[MempoolManager, list[Coin]], None]:
282
299
  coins = []
283
300
  test_coin_records = {}
284
301
  for amount in coin_amounts:
285
- coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount))
302
+ coin = Coin(bytes32.random(), puzzle_hash, uint64(amount))
286
303
  coins.append(coin)
287
304
  test_coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
288
305
 
@@ -299,30 +316,31 @@ async def setup_mempool_with_coins(
299
316
  constants = constants.replace(MAX_BLOCK_COST_CLVM=uint64(max_block_clvm_cost + TEST_BLOCK_OVERHEAD))
300
317
  if mempool_block_buffer is not None:
301
318
  constants = constants.replace(MEMPOOL_BLOCK_BUFFER=uint8(mempool_block_buffer))
302
- mempool_manager = await instantiate_mempool_manager(
303
- get_coin_records, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
304
- )
305
- return (mempool_manager, coins)
306
319
 
320
+ async with instantiate_mempool_manager(
321
+ get_coin_records, block_height=height, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
322
+ ) as mempool_manager:
323
+ yield (mempool_manager, coins)
307
324
 
308
- CreateCoin = tuple[bytes32, int, Optional[bytes]]
325
+
326
+ CreateCoin = tuple[bytes32, int, bytes | None]
309
327
 
310
328
 
311
329
  def make_test_conds(
312
330
  *,
313
- birth_height: Optional[int] = None,
314
- birth_seconds: Optional[int] = None,
315
- height_relative: Optional[int] = None,
331
+ birth_height: int | None = None,
332
+ birth_seconds: int | None = None,
333
+ height_relative: int | None = None,
316
334
  height_absolute: int = 0,
317
- seconds_relative: Optional[int] = None,
335
+ seconds_relative: int | None = None,
318
336
  seconds_absolute: int = 0,
319
- before_height_relative: Optional[int] = None,
320
- before_height_absolute: Optional[int] = None,
321
- before_seconds_relative: Optional[int] = None,
322
- before_seconds_absolute: Optional[int] = None,
337
+ before_height_relative: int | None = None,
338
+ before_height_absolute: int | None = None,
339
+ before_seconds_relative: int | None = None,
340
+ before_seconds_absolute: int | None = None,
323
341
  cost: int = 0,
324
- spend_ids: Sequence[tuple[Union[bytes32, Coin], int]] = [(TEST_COIN_ID, 0)],
325
- created_coins: Optional[list[list[CreateCoin]]] = None,
342
+ spend_ids: Sequence[tuple[bytes32 | Coin, int]] = [(TEST_COIN_ID, 0)],
343
+ created_coins: list[list[CreateCoin]] | None = None,
326
344
  ) -> SpendBundleConditions:
327
345
  if created_coins is None:
328
346
  created_coins = []
@@ -432,21 +450,23 @@ class TestCheckTimeLocks:
432
450
  def test_conditions(
433
451
  self,
434
452
  conds: SpendBundleConditions,
435
- expected: Optional[Err],
453
+ expected: Err | None,
436
454
  ) -> None:
437
- assert (
438
- check_time_locks(
439
- dict(self.REMOVALS),
440
- conds,
441
- self.PREV_BLOCK_HEIGHT,
442
- self.PREV_BLOCK_TIMESTAMP,
443
- )
444
- == expected
455
+ res: int | None = check_time_locks(
456
+ dict(self.REMOVALS),
457
+ conds,
458
+ self.PREV_BLOCK_HEIGHT,
459
+ self.PREV_BLOCK_TIMESTAMP,
445
460
  )
461
+ e: Err | None = None
462
+ if res is not None:
463
+ # TODO: remove when Rust errors and Python Errors are the same
464
+ e = Err(res)
465
+ assert e == expected
446
466
 
447
467
 
448
468
  def expect(
449
- *, height: int = 0, seconds: int = 0, before_height: Optional[int] = None, before_seconds: Optional[int] = None
469
+ *, height: int = 0, seconds: int = 0, before_height: int | None = None, before_seconds: int | None = None
450
470
  ) -> TimelockConditions:
451
471
  ret = TimelockConditions(uint32(height), uint64(seconds))
452
472
  if before_height is not None:
@@ -538,7 +558,7 @@ def spend_bundle_from_conditions(
538
558
 
539
559
  async def add_spendbundle(
540
560
  mempool_manager: MempoolManager, sb: SpendBundle, sb_name: bytes32
541
- ) -> tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]:
561
+ ) -> tuple[uint64 | None, MempoolInclusionStatus, Err | None]:
542
562
  sbc = await mempool_manager.pre_validate_spendbundle(sb, sb_name)
543
563
  ret = await mempool_manager.add_spend_bundle(sb, sbc, sb_name, TEST_HEIGHT)
544
564
  invariant_check_mempool(mempool_manager.mempool)
@@ -550,7 +570,7 @@ async def generate_and_add_spendbundle(
550
570
  conditions: list[list[Any]],
551
571
  coin: Coin = TEST_COIN,
552
572
  aggsig: G2Element = G2Element(),
553
- ) -> tuple[SpendBundle, bytes32, tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]]:
573
+ ) -> tuple[SpendBundle, bytes32, tuple[uint64 | None, MempoolInclusionStatus, Err | None]]:
554
574
  sb = spend_bundle_from_conditions(conditions, coin, aggsig)
555
575
  sb_name = sb.name()
556
576
  result = await add_spendbundle(mempool_manager, sb, sb_name)
@@ -607,98 +627,89 @@ def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem:
607
627
 
608
628
 
609
629
  @pytest.mark.anyio
610
- async def test_empty_spend_bundle() -> None:
611
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
630
+ async def test_empty_spend_bundle(zero_mempool_manager: MempoolManager) -> None:
612
631
  sb = SpendBundle([], G2Element())
613
632
  with pytest.raises(ValidationError, match="INVALID_SPEND_BUNDLE"):
614
- await mempool_manager.pre_validate_spendbundle(sb)
633
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
615
634
 
616
635
 
617
636
  @pytest.mark.anyio
618
- async def test_negative_addition_amount() -> None:
619
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
637
+ async def test_negative_addition_amount(zero_mempool_manager: MempoolManager) -> None:
620
638
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, -1]]
621
639
  sb = spend_bundle_from_conditions(conditions)
622
640
  with pytest.raises(ValidationError, match="COIN_AMOUNT_NEGATIVE"):
623
- await mempool_manager.pre_validate_spendbundle(sb)
641
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
624
642
 
625
643
 
626
644
  @pytest.mark.anyio
627
- async def test_valid_addition_amount() -> None:
628
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
629
- max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
645
+ async def test_valid_addition_amount(zero_mempool_manager: MempoolManager) -> None:
646
+ max_amount = zero_mempool_manager.constants.MAX_COIN_AMOUNT
630
647
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount]]
631
648
  coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, max_amount)
632
649
  sb = spend_bundle_from_conditions(conditions, coin)
633
650
  # ensure this does not throw
634
- _ = await mempool_manager.pre_validate_spendbundle(sb)
651
+ _ = await zero_mempool_manager.pre_validate_spendbundle(sb)
635
652
 
636
653
 
637
654
  @pytest.mark.anyio
638
- async def test_too_big_addition_amount() -> None:
639
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
640
- max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
655
+ async def test_too_big_addition_amount(zero_mempool_manager: MempoolManager) -> None:
656
+ max_amount = zero_mempool_manager.constants.MAX_COIN_AMOUNT
641
657
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount + 1]]
642
658
  sb = spend_bundle_from_conditions(conditions)
643
659
  with pytest.raises(ValidationError, match="COIN_AMOUNT_EXCEEDS_MAXIMUM"):
644
- await mempool_manager.pre_validate_spendbundle(sb)
660
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
645
661
 
646
662
 
647
663
  @pytest.mark.anyio
648
- async def test_duplicate_output() -> None:
649
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
664
+ async def test_duplicate_output(zero_mempool_manager: MempoolManager) -> None:
650
665
  conditions = [
651
666
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
652
667
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
653
668
  ]
654
669
  sb = spend_bundle_from_conditions(conditions)
655
670
  with pytest.raises(ValidationError, match="DUPLICATE_OUTPUT"):
656
- await mempool_manager.pre_validate_spendbundle(sb)
671
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
657
672
 
658
673
 
659
674
  @pytest.mark.anyio
660
- async def test_block_cost_exceeds_max() -> None:
661
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
675
+ async def test_block_cost_exceeds_max(zero_mempool_manager: MempoolManager) -> None:
662
676
  conditions = []
663
677
  for i in range(2400):
664
678
  conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i])
665
679
  sb = spend_bundle_from_conditions(conditions)
666
680
  with pytest.raises(ValidationError, match="BLOCK_COST_EXCEEDS_MAX"):
667
- await mempool_manager.pre_validate_spendbundle(sb)
681
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
668
682
 
669
683
 
670
684
  @pytest.mark.anyio
671
- async def test_double_spend_prevalidation() -> None:
672
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
685
+ async def test_double_spend_prevalidation(zero_mempool_manager: MempoolManager) -> None:
673
686
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
674
687
  sb = spend_bundle_from_conditions(conditions)
675
688
  sb_twice = SpendBundle.aggregate([sb, sb])
676
689
  with pytest.raises(ValidationError, match="DOUBLE_SPEND"):
677
- await mempool_manager.pre_validate_spendbundle(sb_twice)
690
+ await zero_mempool_manager.pre_validate_spendbundle(sb_twice)
678
691
 
679
692
 
680
693
  @pytest.mark.anyio
681
- async def test_minting_coin() -> None:
682
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
694
+ async def test_minting_coin(zero_mempool_manager: MempoolManager) -> None:
683
695
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT]]
684
696
  sb = spend_bundle_from_conditions(conditions)
685
- _ = await mempool_manager.pre_validate_spendbundle(sb)
697
+ _ = await zero_mempool_manager.pre_validate_spendbundle(sb)
686
698
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT + 1]]
687
699
  sb = spend_bundle_from_conditions(conditions)
688
700
  with pytest.raises(ValidationError, match="MINTING_COIN"):
689
- await mempool_manager.pre_validate_spendbundle(sb)
701
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
690
702
 
691
703
 
692
704
  @pytest.mark.anyio
693
- async def test_reserve_fee_condition() -> None:
694
- mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
705
+ async def test_reserve_fee_condition(zero_mempool_manager: MempoolManager) -> None:
695
706
  conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT]]
696
707
  sb = spend_bundle_from_conditions(conditions)
697
- _ = await mempool_manager.pre_validate_spendbundle(sb)
708
+ _ = await zero_mempool_manager.pre_validate_spendbundle(sb)
698
709
  conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT + 1]]
699
710
  sb = spend_bundle_from_conditions(conditions)
700
711
  with pytest.raises(ValidationError, match="RESERVE_FEE_CONDITION_FAILED"):
701
- await mempool_manager.pre_validate_spendbundle(sb)
712
+ await zero_mempool_manager.pre_validate_spendbundle(sb)
702
713
 
703
714
 
704
715
  @pytest.mark.anyio
@@ -706,15 +717,15 @@ async def test_unknown_unspent() -> None:
706
717
  async def get_coin_records(_: Collection[bytes32]) -> list[CoinRecord]:
707
718
  return []
708
719
 
709
- mempool_manager = await instantiate_mempool_manager(get_coin_records)
710
- conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
711
- _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
712
- assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
720
+ async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
721
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
722
+ _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
723
+ assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
713
724
 
714
725
 
715
726
  @pytest.mark.anyio
716
- async def test_same_sb_twice_with_eligible_coin() -> None:
717
- mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
727
+ async def test_same_sb_twice_with_eligible_coin(test_coins_mempool_manager: MempoolManager) -> None:
728
+ mempool_manager = test_coins_mempool_manager
718
729
  sb1_conditions = [
719
730
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
720
731
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
@@ -740,8 +751,10 @@ async def test_same_sb_twice_with_eligible_coin() -> None:
740
751
 
741
752
 
742
753
  @pytest.mark.anyio
743
- async def test_sb_twice_with_eligible_coin_and_different_spends_order() -> None:
744
- mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
754
+ async def test_sb_twice_with_eligible_coin_and_different_spends_order(
755
+ test_coins_mempool_manager: MempoolManager,
756
+ ) -> None:
757
+ mempool_manager = test_coins_mempool_manager
745
758
  sb1_conditions = [
746
759
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
747
760
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
@@ -838,29 +851,28 @@ async def test_ephemeral_timelock(
838
851
  opcode: ConditionOpcode,
839
852
  lock_value: int,
840
853
  expected_status: MempoolInclusionStatus,
841
- expected_error: Optional[Err],
854
+ expected_error: Err | None,
842
855
  ) -> None:
843
- mempool_manager = await instantiate_mempool_manager(
856
+ async with instantiate_mempool_manager(
844
857
  get_coin_records=get_coin_records_for_test_coins,
845
858
  block_height=uint32(5),
846
859
  block_timestamp=uint64(10050),
847
860
  constants=DEFAULT_CONSTANTS,
848
- )
849
-
850
- conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
851
- created_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))
852
- sb1 = spend_bundle_from_conditions(conditions)
853
- sb2 = spend_bundle_from_conditions([[opcode, lock_value]], created_coin)
854
- # sb spends TEST_COIN and creates created_coin which gets spent too
855
- sb = SpendBundle.aggregate([sb1, sb2])
856
- # We shouldn't have a record of this ephemeral coin
857
- assert await get_coin_records_for_test_coins([created_coin.name()]) == []
858
- try:
859
- _, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
860
- assert (status, error) == (expected_status, expected_error)
861
- except ValidationError as e:
862
- assert expected_status == mis.FAILED
863
- assert expected_error == e.code
861
+ ) as mempool_manager:
862
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
863
+ created_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))
864
+ sb1 = spend_bundle_from_conditions(conditions)
865
+ sb2 = spend_bundle_from_conditions([[opcode, lock_value]], created_coin)
866
+ # sb spends TEST_COIN and creates created_coin which gets spent too
867
+ sb = SpendBundle.aggregate([sb1, sb2])
868
+ # We shouldn't have a record of this ephemeral coin
869
+ assert await get_coin_records_for_test_coins([created_coin.name()]) == []
870
+ try:
871
+ _, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
872
+ assert (status, error) == (expected_status, expected_error)
873
+ except ValidationError as e:
874
+ assert expected_status == mis.FAILED
875
+ assert expected_error == e.code
864
876
 
865
877
 
866
878
  def test_optional_min() -> None:
@@ -877,7 +889,7 @@ def test_optional_max() -> None:
877
889
  assert optional_max(uint32(123), uint32(234)) == uint32(234)
878
890
 
879
891
 
880
- def mk_coin_spend(coin: Coin, solution: Optional[str] = None) -> CoinSpend:
892
+ def mk_coin_spend(coin: Coin, solution: str | None = None) -> CoinSpend:
881
893
  return make_spend(
882
894
  coin,
883
895
  SerializedProgram.to(None),
@@ -904,10 +916,10 @@ def mk_item(
904
916
  *,
905
917
  cost: int = 1,
906
918
  fee: int = 0,
907
- assert_height: Optional[int] = None,
908
- assert_before_height: Optional[int] = None,
909
- assert_before_seconds: Optional[int] = None,
910
- solution: Optional[str] = None,
919
+ assert_height: int | None = None,
920
+ assert_before_height: int | None = None,
921
+ assert_before_seconds: int | None = None,
922
+ solution: str | None = None,
911
923
  flags: list[int] = [],
912
924
  ) -> MempoolItem:
913
925
  # we don't actually care about the puzzle and solutions for the purpose of
@@ -1126,8 +1138,8 @@ def test_can_replace(existing_items: list[MempoolItem], new_item: MempoolItem, e
1126
1138
 
1127
1139
 
1128
1140
  @pytest.mark.anyio
1129
- async def test_get_items_not_in_filter() -> None:
1130
- mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
1141
+ async def test_get_items_not_in_filter(test_coins_mempool_manager: MempoolManager) -> None:
1142
+ mempool_manager = test_coins_mempool_manager
1131
1143
  conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
1132
1144
  sb1, sb1_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions)
1133
1145
  conditions2 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
@@ -1138,7 +1150,7 @@ async def test_get_items_not_in_filter() -> None:
1138
1150
  # Don't filter anything
1139
1151
  empty_filter = PyBIP158([])
1140
1152
  result = mempool_manager.get_items_not_in_filter(empty_filter)
1141
- assert result == [sb3, sb2, sb1]
1153
+ assert [item.to_spend_bundle() for item in result] == [sb3, sb2, sb1]
1142
1154
 
1143
1155
  # Filter everything
1144
1156
  full_filter = PyBIP158([bytearray(sb1_name), bytearray(sb2_name), bytearray(sb3_name)])
@@ -1158,16 +1170,16 @@ async def test_get_items_not_in_filter() -> None:
1158
1170
 
1159
1171
  # With a limit of one, sb2 has the highest FPC
1160
1172
  result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=1)
1161
- assert result == [sb2]
1173
+ assert [item.to_spend_bundle() for item in result] == [sb2]
1162
1174
 
1163
1175
  # With a higher limit, all bundles aside from sb3 get included
1164
1176
  result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=5)
1165
- assert result == [sb2, sb1]
1177
+ assert [item.to_spend_bundle() for item in result] == [sb2, sb1]
1166
1178
 
1167
1179
  # Filter two of the spend bundles
1168
1180
  sb2_and_3_filter = PyBIP158([bytearray(sb2_name), bytearray(sb3_name)])
1169
1181
  result = mempool_manager.get_items_not_in_filter(sb2_and_3_filter)
1170
- assert result == [sb1]
1182
+ assert [item.to_spend_bundle() for item in result] == [sb1]
1171
1183
 
1172
1184
 
1173
1185
  @pytest.mark.anyio
@@ -1182,29 +1194,29 @@ async def test_total_mempool_fees() -> None:
1182
1194
  ret.append(r)
1183
1195
  return ret
1184
1196
 
1185
- mempool_manager = await instantiate_mempool_manager(get_coin_records)
1186
- conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
1197
+ async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
1198
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
1199
+
1200
+ # the limit of total fees in the mempool is 2^63
1201
+ # the limit per mempool item is 2^50, that lets us add 8192 items with the
1202
+ # maximum amount of fee before reaching the total mempool limit
1203
+ amount = uint64(2**50)
1204
+ total_fee = 0
1205
+ for i in range(8192):
1206
+ coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
1207
+ coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
1208
+ amount = uint64(amount - 1)
1209
+ # the fee is 1 less than the amount because we create a coin of 1 mojo
1210
+ total_fee += amount
1211
+ _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
1212
+ assert result[1] == MempoolInclusionStatus.SUCCESS
1213
+ assert mempool_manager.mempool.total_mempool_fees() == total_fee
1187
1214
 
1188
- # the limit of total fees in the mempool is 2^63
1189
- # the limit per mempool item is 2^50, that lets us add 8192 items with the
1190
- # maximum amount of fee before reaching the total mempool limit
1191
- amount = uint64(2**50)
1192
- total_fee = 0
1193
- for i in range(8192):
1194
1215
  coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
1195
1216
  coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
1196
- amount = uint64(amount - 1)
1197
- # the fee is 1 less than the amount because we create a coin of 1 mojo
1198
- total_fee += amount
1199
1217
  _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
1200
- assert result[1] == MempoolInclusionStatus.SUCCESS
1201
- assert mempool_manager.mempool.total_mempool_fees() == total_fee
1202
-
1203
- coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
1204
- coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
1205
- _, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
1206
- assert result[1] == MempoolInclusionStatus.FAILED
1207
- assert result[2] == Err.INVALID_BLOCK_FEE_AMOUNT
1218
+ assert result[1] == MempoolInclusionStatus.FAILED
1219
+ assert result[2] == Err.INVALID_BLOCK_FEE_AMOUNT
1208
1220
 
1209
1221
 
1210
1222
  @pytest.mark.parametrize("reverse_tx_order", [True, False])
@@ -1230,17 +1242,17 @@ async def test_create_bundle_from_mempool(reverse_tx_order: bool) -> None:
1230
1242
  result = await add_spendbundle(mempool_manager, sb, sb.name())
1231
1243
  assert result[1] == MempoolInclusionStatus.SUCCESS
1232
1244
 
1233
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(2000000000, 2000002200)))
1234
- high_rate_spends = await make_coin_spends(coins[0:2200])
1235
- low_rate_spends = await make_coin_spends(coins[2200:2400], high_fees=False)
1236
- spends = low_rate_spends + high_rate_spends if reverse_tx_order else high_rate_spends + low_rate_spends
1237
- await send_spends_to_mempool(spends)
1238
- assert mempool_manager.peak is not None
1239
- result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
1240
- assert result is not None
1241
- # Make sure we filled the block with only high rate spends
1242
- assert len([s for s in high_rate_spends if s in result[0].coin_spends]) == len(result[0].coin_spends)
1243
- assert len([s for s in low_rate_spends if s in result[0].coin_spends]) == 0
1245
+ async with setup_mempool_with_coins(coin_amounts=list(range(2000000000, 2000002200))) as (mempool_manager, coins):
1246
+ high_rate_spends = await make_coin_spends(coins[0:2200])
1247
+ low_rate_spends = await make_coin_spends(coins[2200:2400], high_fees=False)
1248
+ spends = low_rate_spends + high_rate_spends if reverse_tx_order else high_rate_spends + low_rate_spends
1249
+ await send_spends_to_mempool(spends)
1250
+ assert mempool_manager.peak is not None
1251
+ result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
1252
+ assert result is not None
1253
+ # Make sure we filled the block with only high rate spends
1254
+ assert len([s for s in high_rate_spends if s in result[0].coin_spends]) == len(result[0].coin_spends)
1255
+ assert len([s for s in low_rate_spends if s in result[0].coin_spends]) == 0
1244
1256
 
1245
1257
 
1246
1258
  @pytest.mark.parametrize("num_skipped_items", [PRIORITY_TX_THRESHOLD, MAX_SKIPPED_ITEMS])
@@ -1257,105 +1269,105 @@ async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, ca
1257
1269
 
1258
1270
  MAX_BLOCK_CLVM_COST = 550_000_000
1259
1271
 
1260
- mempool_manager, coins = await setup_mempool_with_coins(
1272
+ async with setup_mempool_with_coins(
1261
1273
  coin_amounts=list(range(1_000_000_000, 1_000_000_030)),
1262
1274
  max_block_clvm_cost=MAX_BLOCK_CLVM_COST,
1263
1275
  max_tx_clvm_cost=uint64(MAX_BLOCK_CLVM_COST),
1264
1276
  mempool_block_buffer=20,
1265
- )
1266
-
1267
- async def make_and_send_big_cost_sb(coin: Coin) -> None:
1268
- """
1269
- Creates a spend bundle with a big enough cost that gets it close to the
1270
- maximum block clvm cost limit.
1271
- """
1272
- conditions = []
1273
- sk = AugSchemeMPL.key_gen(b"7" * 32)
1277
+ ) as (mempool_manager, coins):
1278
+
1279
+ async def make_and_send_big_cost_sb(coin: Coin) -> None:
1280
+ """
1281
+ Creates a spend bundle with a big enough cost that gets it close to the
1282
+ maximum block clvm cost limit.
1283
+ """
1284
+ conditions = []
1285
+ sk = AugSchemeMPL.key_gen(b"7" * 32)
1286
+ g1 = sk.get_g1()
1287
+ sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
1288
+ aggsig = G2Element()
1289
+ # Let's get as close to `MAX_BLOCK_CLVM_COST` (550_000_000) as possible.
1290
+ # We start by accounting for execution cost
1291
+ spend_bundle_cost = 44
1292
+ # And then the created coin
1293
+ conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - 10_000_000])
1294
+ TEST_CREATE_COIN_SPEND_BYTESIZE = 93
1295
+ TEST_CREATE_COIN_CONDITION_COST = (
1296
+ ConditionCost.CREATE_COIN.value + TEST_CREATE_COIN_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1297
+ )
1298
+ spend_bundle_cost += TEST_CREATE_COIN_CONDITION_COST
1299
+ # We're using agg sig conditions to increase the spend bundle's cost
1300
+ # and reach our target cost.
1301
+ TEST_AGG_SIG_SPEND_BYTESIZE = 88
1302
+ TEST_AGGSIG_CONDITION_COST = (
1303
+ ConditionCost.AGG_SIG.value + TEST_AGG_SIG_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1304
+ )
1305
+ while spend_bundle_cost + TEST_AGGSIG_CONDITION_COST < MAX_BLOCK_CLVM_COST:
1306
+ conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH])
1307
+ aggsig += sig
1308
+ spend_bundle_cost += TEST_AGGSIG_CONDITION_COST
1309
+ # We now have a spend bundle with a big enough cost that gets it close to the limit
1310
+ _, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coin, aggsig)
1311
+ cost, status, _ = res
1312
+ assert status == MempoolInclusionStatus.SUCCESS
1313
+ assert cost == spend_bundle_cost
1314
+
1315
+ # Create the spend bundles with a big enough cost that they get close to the limit
1316
+ for i in range(num_skipped_items):
1317
+ await make_and_send_big_cost_sb(coins[i])
1318
+
1319
+ # Create a spend bundle with a relatively smaller cost.
1320
+ # Combined with a big cost spend bundle, we'd exceed the maximum block clvm cost
1321
+ sb2_coin = coins[num_skipped_items]
1322
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, sb2_coin.amount - 200_000]]
1323
+ sb2, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, sb2_coin)
1324
+ assert res[1] == MempoolInclusionStatus.SUCCESS
1325
+ sb2_addition = Coin(sb2_coin.name(), IDENTITY_PUZZLE_HASH, uint64(sb2_coin.amount - 200_000))
1326
+ # Create 4 extra spend bundles with smaller FPC and smaller costs
1327
+ extra_sbs = []
1328
+ extra_additions = []
1329
+ sk = AugSchemeMPL.key_gen(b"8" * 32)
1274
1330
  g1 = sk.get_g1()
1275
- sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
1276
- aggsig = G2Element()
1277
- # Let's get as close to `MAX_BLOCK_CLVM_COST` (550_000_000) as possible.
1278
- # We start by accounting for execution cost
1279
- spend_bundle_cost = 44
1280
- # And then the created coin
1281
- conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - 10_000_000])
1282
- TEST_CREATE_COIN_SPEND_BYTESIZE = 93
1283
- TEST_CREATE_COIN_CONDITION_COST = (
1284
- ConditionCost.CREATE_COIN.value + TEST_CREATE_COIN_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1285
- )
1286
- spend_bundle_cost += TEST_CREATE_COIN_CONDITION_COST
1287
- # We're using agg sig conditions to increase the spend bundle's cost
1288
- # and reach our target cost.
1289
- TEST_AGG_SIG_SPEND_BYTESIZE = 88
1290
- TEST_AGGSIG_CONDITION_COST = (
1291
- ConditionCost.AGG_SIG.value + TEST_AGG_SIG_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
1331
+ sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1332
+ for i in range(num_skipped_items + 1, num_skipped_items + 5):
1333
+ conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coins[i].amount]]
1334
+ # Make the first of these without eligible coins
1335
+ if i == num_skipped_items + 1:
1336
+ conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"])
1337
+ aggsig = sig
1338
+ else:
1339
+ aggsig = G2Element()
1340
+ sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
1341
+ extra_sbs.append(sb)
1342
+ coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount))
1343
+ extra_additions.append(coin)
1344
+ assert res[1] == MempoolInclusionStatus.SUCCESS
1345
+
1346
+ assert mempool_manager.peak is not None
1347
+ caplog.set_level(logging.DEBUG)
1348
+ result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
1349
+ assert result is not None
1350
+ agg, additions = result
1351
+ skipped_due_to_eligible_coins = sum(
1352
+ 1 for line in caplog.text.split("\n") if "Skipping transaction with dedup or FF spends" in line
1292
1353
  )
1293
- while spend_bundle_cost + TEST_AGGSIG_CONDITION_COST < MAX_BLOCK_CLVM_COST:
1294
- conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH])
1295
- aggsig += sig
1296
- spend_bundle_cost += TEST_AGGSIG_CONDITION_COST
1297
- # We now have a spend bundle with a big enough cost that gets it close to the limit
1298
- _, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coin, aggsig)
1299
- cost, status, _ = res
1300
- assert status == MempoolInclusionStatus.SUCCESS
1301
- assert cost == spend_bundle_cost
1302
-
1303
- # Create the spend bundles with a big enough cost that they get close to the limit
1304
- for i in range(num_skipped_items):
1305
- await make_and_send_big_cost_sb(coins[i])
1306
-
1307
- # Create a spend bundle with a relatively smaller cost.
1308
- # Combined with a big cost spend bundle, we'd exceed the maximum block clvm cost
1309
- sb2_coin = coins[num_skipped_items]
1310
- conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, sb2_coin.amount - 200_000]]
1311
- sb2, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, sb2_coin)
1312
- assert res[1] == MempoolInclusionStatus.SUCCESS
1313
- sb2_addition = Coin(sb2_coin.name(), IDENTITY_PUZZLE_HASH, uint64(sb2_coin.amount - 200_000))
1314
- # Create 4 extra spend bundles with smaller FPC and smaller costs
1315
- extra_sbs = []
1316
- extra_additions = []
1317
- sk = AugSchemeMPL.key_gen(b"8" * 32)
1318
- g1 = sk.get_g1()
1319
- sig = AugSchemeMPL.sign(sk, b"foobar", g1)
1320
- for i in range(num_skipped_items + 1, num_skipped_items + 5):
1321
- conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coins[i].amount]]
1322
- # Make the first of these without eligible coins
1323
- if i == num_skipped_items + 1:
1324
- conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"])
1325
- aggsig = sig
1354
+ if num_skipped_items == PRIORITY_TX_THRESHOLD:
1355
+ # We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
1356
+ # so the first from the extra 4 (the one without eligible coins) went in,
1357
+ # and the other 3 were skipped (they have eligible coins)
1358
+ assert skipped_due_to_eligible_coins == 3
1359
+ assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
1360
+ assert additions == [sb2_addition, extra_additions[0]]
1361
+ assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
1362
+ elif num_skipped_items == MAX_SKIPPED_ITEMS:
1363
+ # We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
1364
+ # we didn't process any of the extra items
1365
+ assert skipped_due_to_eligible_coins == 0
1366
+ assert agg == SpendBundle.aggregate([sb2])
1367
+ assert additions == [sb2_addition]
1368
+ assert agg.removals() == [sb2_coin]
1326
1369
  else:
1327
- aggsig = G2Element()
1328
- sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
1329
- extra_sbs.append(sb)
1330
- coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount))
1331
- extra_additions.append(coin)
1332
- assert res[1] == MempoolInclusionStatus.SUCCESS
1333
-
1334
- assert mempool_manager.peak is not None
1335
- caplog.set_level(logging.DEBUG)
1336
- result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
1337
- assert result is not None
1338
- agg, additions = result
1339
- skipped_due_to_eligible_coins = sum(
1340
- 1 for line in caplog.text.split("\n") if "Skipping transaction with dedup or FF spends" in line
1341
- )
1342
- if num_skipped_items == PRIORITY_TX_THRESHOLD:
1343
- # We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
1344
- # so the first from the extra 4 (the one without eligible coins) went in,
1345
- # and the other 3 were skipped (they have eligible coins)
1346
- assert skipped_due_to_eligible_coins == 3
1347
- assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
1348
- assert additions == [sb2_addition, extra_additions[0]]
1349
- assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
1350
- elif num_skipped_items == MAX_SKIPPED_ITEMS:
1351
- # We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
1352
- # we didn't process any of the extra items
1353
- assert skipped_due_to_eligible_coins == 0
1354
- assert agg == SpendBundle.aggregate([sb2])
1355
- assert additions == [sb2_addition]
1356
- assert agg.removals() == [sb2_coin]
1357
- else:
1358
- raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
1370
+ raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
1359
1371
 
1360
1372
 
1361
1373
  @pytest.mark.parametrize(
@@ -1377,7 +1389,7 @@ async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, ca
1377
1389
  )
1378
1390
  @pytest.mark.anyio
1379
1391
  async def test_assert_before_expiration(
1380
- opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: Optional[int]
1392
+ opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: int | None
1381
1393
  ) -> None:
1382
1394
  async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
1383
1395
  all_coins = {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}
@@ -1388,41 +1400,40 @@ async def test_assert_before_expiration(
1388
1400
  ret.append(r)
1389
1401
  return ret
1390
1402
 
1391
- mempool_manager = await instantiate_mempool_manager(
1403
+ async with instantiate_mempool_manager(
1392
1404
  get_coin_records,
1393
1405
  block_height=uint32(10),
1394
1406
  block_timestamp=uint64(10000),
1395
1407
  constants=DEFAULT_CONSTANTS,
1396
- )
1397
-
1398
- bundle = spend_bundle_from_conditions(
1399
- [
1400
- [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1401
- [opcode, arg],
1402
- ],
1403
- coin=TEST_COIN,
1404
- )
1405
- bundle_name = bundle.name()
1406
- assert (await add_spendbundle(mempool_manager, bundle, bundle_name))[1] == mis.SUCCESS
1407
- # make sure the spend was added correctly
1408
- assert mempool_manager.get_spendbundle(bundle_name) == bundle
1409
-
1410
- block_record = create_test_block_record(height=uint32(11), timestamp=uint64(10019))
1411
- await mempool_manager.new_peak(block_record, None)
1412
- invariant_check_mempool(mempool_manager.mempool)
1413
-
1414
- still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
1415
- assert still_in_pool != expect_eviction
1416
- if still_in_pool:
1417
- assert expect_limit is not None
1418
- item = mempool_manager.get_mempool_item(bundle_name)
1419
- assert item is not None
1420
- if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
1421
- assert item.assert_before_seconds == expect_limit
1422
- elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
1423
- assert item.assert_before_height == expect_limit
1424
- else:
1425
- assert False
1408
+ ) as mempool_manager:
1409
+ bundle = spend_bundle_from_conditions(
1410
+ [
1411
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
1412
+ [opcode, arg],
1413
+ ],
1414
+ coin=TEST_COIN,
1415
+ )
1416
+ bundle_name = bundle.name()
1417
+ assert (await add_spendbundle(mempool_manager, bundle, bundle_name))[1] == mis.SUCCESS
1418
+ # make sure the spend was added correctly
1419
+ assert mempool_manager.get_spendbundle(bundle_name) == bundle
1420
+
1421
+ block_record = create_test_block_record(height=uint32(11), timestamp=uint64(10019))
1422
+ await mempool_manager.new_peak(block_record, None)
1423
+ invariant_check_mempool(mempool_manager.mempool)
1424
+
1425
+ still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
1426
+ assert still_in_pool != expect_eviction
1427
+ if still_in_pool:
1428
+ assert expect_limit is not None
1429
+ item = mempool_manager.get_mempool_item(bundle_name)
1430
+ assert item is not None
1431
+ if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
1432
+ assert item.assert_before_seconds == expect_limit
1433
+ elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
1434
+ assert item.assert_before_height == expect_limit
1435
+ else:
1436
+ assert False
1426
1437
 
1427
1438
 
1428
1439
  def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = False) -> SpendBundle:
@@ -1439,7 +1450,7 @@ def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = Fa
1439
1450
  async def send_spendbundle(
1440
1451
  mempool_manager: MempoolManager,
1441
1452
  sb: SpendBundle,
1442
- expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
1453
+ expected_result: tuple[MempoolInclusionStatus, Err | None] = (MempoolInclusionStatus.SUCCESS, None),
1443
1454
  ) -> None:
1444
1455
  result = await add_spendbundle(mempool_manager, sb, sb.name())
1445
1456
  assert (result[1], result[2]) == expected_result
@@ -1450,7 +1461,7 @@ async def make_and_send_spendbundle(
1450
1461
  coin: Coin,
1451
1462
  *,
1452
1463
  fee: int = 0,
1453
- expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
1464
+ expected_result: tuple[MempoolInclusionStatus, Err | None] = (MempoolInclusionStatus.SUCCESS, None),
1454
1465
  ) -> SpendBundle:
1455
1466
  sb = make_test_spendbundle(coin, fee=fee)
1456
1467
  await send_spendbundle(mempool_manager, sb, expected_result)
@@ -1467,125 +1478,125 @@ def assert_sb_not_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> N
1467
1478
 
1468
1479
  @pytest.mark.anyio
1469
1480
  async def test_insufficient_fee_increase() -> None:
1470
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1471
- sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1472
- sb1_2 = await make_and_send_spendbundle(
1473
- mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1474
- )
1475
- # The old spendbundle must stay
1476
- assert_sb_in_pool(mempool_manager, sb1_1)
1477
- assert_sb_not_in_pool(mempool_manager, sb1_2)
1481
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1482
+ sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1483
+ sb1_2 = await make_and_send_spendbundle(
1484
+ mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1485
+ )
1486
+ # The old spendbundle must stay
1487
+ assert_sb_in_pool(mempool_manager, sb1_1)
1488
+ assert_sb_not_in_pool(mempool_manager, sb1_2)
1478
1489
 
1479
1490
 
1480
1491
  @pytest.mark.anyio
1481
1492
  async def test_sufficient_fee_increase() -> None:
1482
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1483
- sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1484
- sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
1485
- # sb1_1 gets replaced with sb1_2
1486
- assert_sb_not_in_pool(mempool_manager, sb1_1)
1487
- assert_sb_in_pool(mempool_manager, sb1_2)
1493
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1494
+ sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1495
+ sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
1496
+ # sb1_1 gets replaced with sb1_2
1497
+ assert_sb_not_in_pool(mempool_manager, sb1_1)
1498
+ assert_sb_in_pool(mempool_manager, sb1_2)
1488
1499
 
1489
1500
 
1490
1501
  @pytest.mark.anyio
1491
1502
  async def test_superset() -> None:
1492
1503
  # Aggregated spendbundle sb12 replaces sb1 since it spends a superset
1493
1504
  # of coins spent in sb1
1494
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1495
- sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1496
- sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
1497
- sb12 = SpendBundle.aggregate([sb2, sb1])
1498
- await send_spendbundle(mempool_manager, sb12)
1499
- assert_sb_in_pool(mempool_manager, sb12)
1500
- assert_sb_not_in_pool(mempool_manager, sb1)
1505
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1506
+ sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
1507
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
1508
+ sb12 = SpendBundle.aggregate([sb2, sb1])
1509
+ await send_spendbundle(mempool_manager, sb12)
1510
+ assert_sb_in_pool(mempool_manager, sb12)
1511
+ assert_sb_not_in_pool(mempool_manager, sb1)
1501
1512
 
1502
1513
 
1503
1514
  @pytest.mark.anyio
1504
1515
  async def test_superset_violation() -> None:
1505
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1506
- sb1 = make_test_spendbundle(coins[0])
1507
- sb2 = make_test_spendbundle(coins[1])
1508
- sb12 = SpendBundle.aggregate([sb1, sb2])
1509
- await send_spendbundle(mempool_manager, sb12)
1510
- assert_sb_in_pool(mempool_manager, sb12)
1511
- # sb23 must not replace existing sb12 as the former does not spend all
1512
- # coins that are spent in the latter (specifically, the first coin)
1513
- sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
1514
- sb23 = SpendBundle.aggregate([sb2, sb3])
1515
- await send_spendbundle(
1516
- mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1517
- )
1518
- assert_sb_in_pool(mempool_manager, sb12)
1519
- assert_sb_not_in_pool(mempool_manager, sb23)
1516
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1517
+ sb1 = make_test_spendbundle(coins[0])
1518
+ sb2 = make_test_spendbundle(coins[1])
1519
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1520
+ await send_spendbundle(mempool_manager, sb12)
1521
+ assert_sb_in_pool(mempool_manager, sb12)
1522
+ # sb23 must not replace existing sb12 as the former does not spend all
1523
+ # coins that are spent in the latter (specifically, the first coin)
1524
+ sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
1525
+ sb23 = SpendBundle.aggregate([sb2, sb3])
1526
+ await send_spendbundle(
1527
+ mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1528
+ )
1529
+ assert_sb_in_pool(mempool_manager, sb12)
1530
+ assert_sb_not_in_pool(mempool_manager, sb23)
1520
1531
 
1521
1532
 
1522
1533
  @pytest.mark.anyio
1523
1534
  async def test_total_fpc_decrease() -> None:
1524
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1525
- sb1 = make_test_spendbundle(coins[0])
1526
- sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1527
- sb12 = SpendBundle.aggregate([sb1, sb2])
1528
- await send_spendbundle(mempool_manager, sb12)
1529
- sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1530
- assert_sb_in_pool(mempool_manager, sb12)
1531
- assert_sb_in_pool(mempool_manager, sb3)
1532
- # sb1234 should not be in pool as it decreases total fees per cost
1533
- sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
1534
- sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1535
- await send_spendbundle(
1536
- mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1537
- )
1538
- assert_sb_not_in_pool(mempool_manager, sb1234)
1535
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1536
+ sb1 = make_test_spendbundle(coins[0])
1537
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1538
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1539
+ await send_spendbundle(mempool_manager, sb12)
1540
+ sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1541
+ assert_sb_in_pool(mempool_manager, sb12)
1542
+ assert_sb_in_pool(mempool_manager, sb3)
1543
+ # sb1234 should not be in pool as it decreases total fees per cost
1544
+ sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
1545
+ sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1546
+ await send_spendbundle(
1547
+ mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
1548
+ )
1549
+ assert_sb_not_in_pool(mempool_manager, sb1234)
1539
1550
 
1540
1551
 
1541
1552
  @pytest.mark.anyio
1542
1553
  async def test_sufficient_total_fpc_increase() -> None:
1543
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1544
- sb1 = make_test_spendbundle(coins[0])
1545
- sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1546
- sb12 = SpendBundle.aggregate([sb1, sb2])
1547
- await send_spendbundle(mempool_manager, sb12)
1548
- sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1549
- assert_sb_in_pool(mempool_manager, sb12)
1550
- assert_sb_in_pool(mempool_manager, sb3)
1551
- # sb1234 has a higher fee per cost than its conflicts and should get
1552
- # into the mempool
1553
- sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
1554
- sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1555
- await send_spendbundle(mempool_manager, sb1234)
1556
- assert_sb_in_pool(mempool_manager, sb1234)
1557
- assert_sb_not_in_pool(mempool_manager, sb12)
1558
- assert_sb_not_in_pool(mempool_manager, sb3)
1554
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1555
+ sb1 = make_test_spendbundle(coins[0])
1556
+ sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1557
+ sb12 = SpendBundle.aggregate([sb1, sb2])
1558
+ await send_spendbundle(mempool_manager, sb12)
1559
+ sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
1560
+ assert_sb_in_pool(mempool_manager, sb12)
1561
+ assert_sb_in_pool(mempool_manager, sb3)
1562
+ # sb1234 has a higher fee per cost than its conflicts and should get
1563
+ # into the mempool
1564
+ sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
1565
+ sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
1566
+ await send_spendbundle(mempool_manager, sb1234)
1567
+ assert_sb_in_pool(mempool_manager, sb1234)
1568
+ assert_sb_not_in_pool(mempool_manager, sb12)
1569
+ assert_sb_not_in_pool(mempool_manager, sb3)
1559
1570
 
1560
1571
 
1561
1572
  @pytest.mark.anyio
1562
1573
  async def test_replace_with_extra_eligible_coin() -> None:
1563
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1564
- sb1234 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(4)])
1565
- await send_spendbundle(mempool_manager, sb1234)
1566
- assert_sb_in_pool(mempool_manager, sb1234)
1567
- # Replace sb1234 with sb1234_2 which spends an eligible coin additionally
1568
- eligible_sb = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE, eligible_spend=True)
1569
- sb1234_2 = SpendBundle.aggregate([sb1234, eligible_sb])
1570
- await send_spendbundle(mempool_manager, sb1234_2)
1571
- assert_sb_not_in_pool(mempool_manager, sb1234)
1572
- assert_sb_in_pool(mempool_manager, sb1234_2)
1574
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1575
+ sb1234 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(4)])
1576
+ await send_spendbundle(mempool_manager, sb1234)
1577
+ assert_sb_in_pool(mempool_manager, sb1234)
1578
+ # Replace sb1234 with sb1234_2 which spends an eligible coin additionally
1579
+ eligible_sb = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE, eligible_spend=True)
1580
+ sb1234_2 = SpendBundle.aggregate([sb1234, eligible_sb])
1581
+ await send_spendbundle(mempool_manager, sb1234_2)
1582
+ assert_sb_not_in_pool(mempool_manager, sb1234)
1583
+ assert_sb_in_pool(mempool_manager, sb1234_2)
1573
1584
 
1574
1585
 
1575
1586
  @pytest.mark.anyio
1576
1587
  async def test_replacing_one_with_an_eligible_coin() -> None:
1577
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
1578
- sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1579
- eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1580
- sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1581
- await send_spendbundle(mempool_manager, sb123e)
1582
- assert_sb_in_pool(mempool_manager, sb123e)
1583
- # Replace sb123e with sb123e4
1584
- sb4 = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE)
1585
- sb123e4 = SpendBundle.aggregate([sb123e, sb4])
1586
- await send_spendbundle(mempool_manager, sb123e4)
1587
- assert_sb_not_in_pool(mempool_manager, sb123e)
1588
- assert_sb_in_pool(mempool_manager, sb123e4)
1588
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
1589
+ sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1590
+ eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1591
+ sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1592
+ await send_spendbundle(mempool_manager, sb123e)
1593
+ assert_sb_in_pool(mempool_manager, sb123e)
1594
+ # Replace sb123e with sb123e4
1595
+ sb4 = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE)
1596
+ sb123e4 = SpendBundle.aggregate([sb123e, sb4])
1597
+ await send_spendbundle(mempool_manager, sb123e4)
1598
+ assert_sb_not_in_pool(mempool_manager, sb123e)
1599
+ assert_sb_in_pool(mempool_manager, sb123e4)
1589
1600
 
1590
1601
 
1591
1602
  def test_dedup_info_nothing_to_do() -> None:
@@ -1757,64 +1768,64 @@ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(ne
1757
1768
  ret.append(r)
1758
1769
  return ret
1759
1770
 
1760
- mempool_manager = await instantiate_mempool_manager(get_coin_records)
1761
- # Create a bunch of mempool items that spend the coin in different ways
1762
- # only the first one will be accepted
1763
- for i in range(3):
1764
- _, _, result = await generate_and_add_spendbundle(
1765
- mempool_manager,
1766
- [
1767
- [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount],
1768
- [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, uint64(i)],
1769
- ],
1770
- coin,
1771
- )
1772
- if i == 0:
1773
- assert result[1] == MempoolInclusionStatus.SUCCESS
1774
- else:
1775
- assert result[1] == MempoolInclusionStatus.PENDING
1776
- assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 1
1777
- assert mempool_manager.mempool.size() == 1
1778
- assert len(list(mempool_manager.mempool.items_by_feerate())) == 1
1779
- # Setup a new peak where the incoming block has spent the coin
1780
- # Mark this coin as spent
1781
- test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
1782
- block_record = create_test_block_record(height=new_height)
1783
- await mempool_manager.new_peak(block_record, [coin_id])
1784
- invariant_check_mempool(mempool_manager.mempool)
1785
- # As the coin was a spend in all the mempool items we had, nothing should be left now
1786
- assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 0
1787
- assert mempool_manager.mempool.size() == 0
1788
- assert len(list(mempool_manager.mempool.items_by_feerate())) == 0
1771
+ async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
1772
+ # Create a bunch of mempool items that spend the coin in different ways
1773
+ # only the first one will be accepted
1774
+ for i in range(3):
1775
+ _, _, result = await generate_and_add_spendbundle(
1776
+ mempool_manager,
1777
+ [
1778
+ [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount],
1779
+ [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, uint64(i)],
1780
+ ],
1781
+ coin,
1782
+ )
1783
+ if i == 0:
1784
+ assert result[1] == MempoolInclusionStatus.SUCCESS
1785
+ else:
1786
+ assert result[1] == MempoolInclusionStatus.PENDING
1787
+ assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 1
1788
+ assert mempool_manager.mempool.size() == 1
1789
+ assert len(list(mempool_manager.mempool.items_by_feerate())) == 1
1790
+ # Setup a new peak where the incoming block has spent the coin
1791
+ # Mark this coin as spent
1792
+ test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
1793
+ block_record = create_test_block_record(height=new_height)
1794
+ await mempool_manager.new_peak(block_record, [coin_id])
1795
+ invariant_check_mempool(mempool_manager.mempool)
1796
+ # As the coin was a spend in all the mempool items we had, nothing should be left now
1797
+ assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 0
1798
+ assert mempool_manager.mempool.size() == 0
1799
+ assert len(list(mempool_manager.mempool.items_by_feerate())) == 0
1789
1800
 
1790
1801
 
1791
1802
  @pytest.mark.anyio
1792
1803
  async def test_bundle_coin_spends() -> None:
1793
1804
  # This tests the construction of bundle_coin_spends map for mempool items
1794
1805
  # We're creating sb123e with 4 coins, one of them being eligible
1795
- mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000005)))
1796
- sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1797
- eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1798
- sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1799
- await send_spendbundle(mempool_manager, sb123e)
1800
- mi123e = mempool_manager.get_mempool_item(sb123e.name())
1801
- assert mi123e is not None
1802
- execution_cost = 44
1803
- for i in range(3):
1804
- assert mi123e.bundle_coin_spends[coins[i].name()] == BundleCoinSpend(
1805
- coin_spend=sb123.coin_spends[i],
1806
- eligible_for_dedup=False,
1807
- additions=[Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, coins[i].amount)],
1808
- cost=uint64(ConditionCost.CREATE_COIN.value + ConditionCost.AGG_SIG.value + execution_cost),
1806
+ async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000005))) as (mempool_manager, coins):
1807
+ sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
1808
+ eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
1809
+ sb123e = SpendBundle.aggregate([sb123, eligible_sb])
1810
+ await send_spendbundle(mempool_manager, sb123e)
1811
+ mi123e = mempool_manager.get_mempool_item(sb123e.name())
1812
+ assert mi123e is not None
1813
+ execution_cost = 44
1814
+ for i in range(3):
1815
+ assert mi123e.bundle_coin_spends[coins[i].name()] == BundleCoinSpend(
1816
+ coin_spend=sb123.coin_spends[i],
1817
+ eligible_for_dedup=False,
1818
+ additions=[Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, coins[i].amount)],
1819
+ cost=uint64(ConditionCost.CREATE_COIN.value + ConditionCost.AGG_SIG.value + execution_cost),
1820
+ latest_singleton_lineage=None,
1821
+ )
1822
+ assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
1823
+ coin_spend=eligible_sb.coin_spends[0],
1824
+ eligible_for_dedup=True,
1825
+ additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
1826
+ cost=uint64(ConditionCost.CREATE_COIN.value + execution_cost),
1809
1827
  latest_singleton_lineage=None,
1810
1828
  )
1811
- assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
1812
- coin_spend=eligible_sb.coin_spends[0],
1813
- eligible_for_dedup=True,
1814
- additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
1815
- cost=uint64(ConditionCost.CREATE_COIN.value + execution_cost),
1816
- latest_singleton_lineage=None,
1817
- )
1818
1829
 
1819
1830
 
1820
1831
  @pytest.mark.anyio
@@ -2131,7 +2142,7 @@ async def test_identical_spend_aggregation_e2e(
2131
2142
  ),
2132
2143
  ],
2133
2144
  )
2134
- async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected: Optional[Err]) -> None:
2145
+ async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected: Err | None) -> None:
2135
2146
  coins = []
2136
2147
  test_coin_records = {}
2137
2148
 
@@ -2150,27 +2161,26 @@ async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expec
2150
2161
  ret.append(r)
2151
2162
  return ret
2152
2163
 
2153
- mempool_manager = await instantiate_mempool_manager(
2164
+ async with instantiate_mempool_manager(
2154
2165
  get_coin_records, block_height=uint32(21), block_timestamp=uint64(2010)
2155
- )
2156
-
2157
- coin_spends = [
2158
- make_spend(coins[0], IDENTITY_PUZZLE, Program.to([cond1])),
2159
- make_spend(coins[1], IDENTITY_PUZZLE, Program.to([cond2])),
2160
- ]
2161
-
2162
- bundle = SpendBundle(coin_spends, G2Element())
2163
- bundle_name = bundle.name()
2164
- try:
2165
- result = await add_spendbundle(mempool_manager, bundle, bundle_name)
2166
- print(result)
2167
- if expected is not None:
2168
- assert result == (None, MempoolInclusionStatus.FAILED, expected)
2169
- else:
2170
- assert result[0] is not None
2171
- assert result[1] != MempoolInclusionStatus.FAILED
2172
- except ValidationError as e:
2173
- assert e.code == expected
2166
+ ) as mempool_manager:
2167
+ coin_spends = [
2168
+ make_spend(coins[0], IDENTITY_PUZZLE, Program.to([cond1])),
2169
+ make_spend(coins[1], IDENTITY_PUZZLE, Program.to([cond2])),
2170
+ ]
2171
+
2172
+ bundle = SpendBundle(coin_spends, G2Element())
2173
+ bundle_name = bundle.name()
2174
+ try:
2175
+ result = await add_spendbundle(mempool_manager, bundle, bundle_name)
2176
+ print(result)
2177
+ if expected is not None:
2178
+ assert result == (None, MempoolInclusionStatus.FAILED, expected)
2179
+ else:
2180
+ assert result[0] is not None
2181
+ assert result[1] != MempoolInclusionStatus.FAILED
2182
+ except ValidationError as e:
2183
+ assert e.code == expected
2174
2184
 
2175
2185
 
2176
2186
  TEST_FILL_RATE_ITEM_COST = 144_720_020
@@ -2288,14 +2298,14 @@ async def test_fill_rate_block_validation(
2288
2298
 
2289
2299
  @pytest.mark.parametrize("optimized_path", [True, False])
2290
2300
  @pytest.mark.anyio
2291
- async def test_height_added_to_mempool(optimized_path: bool) -> None:
2301
+ async def test_height_added_to_mempool(optimized_path: bool, test_coins_mempool_manager: MempoolManager) -> None:
2292
2302
  """
2293
2303
  This test covers scenarios when the mempool is updated or rebuilt, to make
2294
2304
  sure that mempool items maintain correct height added to mempool values.
2295
2305
  We control whether we're updating the mempool or rebuilding it, through the
2296
2306
  `optimized_path` param.
2297
2307
  """
2298
- mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
2308
+ mempool_manager = test_coins_mempool_manager
2299
2309
  assert mempool_manager.peak is not None
2300
2310
  assert mempool_manager.peak.height == TEST_HEIGHT
2301
2311
  assert mempool_manager.peak.header_hash == height_hash(TEST_HEIGHT)
@@ -2346,9 +2356,9 @@ class TestCoins:
2346
2356
  self.lineage_info[ph] = UnspentLineageInfo(c.name(), c.parent_coin_info, bytes32([42] * 32))
2347
2357
 
2348
2358
  def spend_coin(self, coin_id: bytes32, height: uint32 = uint32(10)) -> None:
2349
- self.coin_records[coin_id] = dataclasses.replace(self.coin_records[coin_id], spent_block_index=height)
2359
+ self.coin_records[coin_id] = self.coin_records[coin_id].replace(spent_block_index=height)
2350
2360
 
2351
- def update_lineage(self, puzzle_hash: bytes32, coin: Optional[Coin]) -> None:
2361
+ def update_lineage(self, puzzle_hash: bytes32, coin: Coin | None) -> None:
2352
2362
  if coin is None:
2353
2363
  self.lineage_info.pop(puzzle_hash)
2354
2364
  else:
@@ -2365,7 +2375,7 @@ class TestCoins:
2365
2375
 
2366
2376
  return ret
2367
2377
 
2368
- async def get_unspent_lineage_info(self, ph: bytes32) -> Optional[UnspentLineageInfo]:
2378
+ async def get_unspent_lineage_info(self, ph: bytes32) -> UnspentLineageInfo | None:
2369
2379
  return self.lineage_info.get(ph)
2370
2380
 
2371
2381
 
@@ -2396,15 +2406,16 @@ def make_singleton_spend(
2396
2406
  return ret
2397
2407
 
2398
2408
 
2399
- async def setup_mempool(coins: TestCoins) -> MempoolManager:
2400
- mempool_manager = MempoolManager(
2409
+ @asynccontextmanager
2410
+ async def setup_mempool(coins: TestCoins) -> AsyncGenerator[MempoolManager, None]:
2411
+ async with MempoolManager.managed(
2401
2412
  coins.get_coin_records,
2402
2413
  coins.get_unspent_lineage_info,
2403
2414
  DEFAULT_CONSTANTS,
2404
- )
2405
- test_block_record = create_test_block_record(height=uint32(5000000), timestamp=uint64(12345678))
2406
- await mempool_manager.new_peak(test_block_record, None)
2407
- return mempool_manager
2415
+ ) as mempool_manager:
2416
+ test_block_record = create_test_block_record(height=uint32(5000000), timestamp=uint64(12345678))
2417
+ await mempool_manager.new_peak(test_block_record, None)
2418
+ yield mempool_manager
2408
2419
 
2409
2420
 
2410
2421
  # adds a new peak to the memepool manager with the specified coin IDs spent
@@ -2452,55 +2463,54 @@ async def test_new_peak_ff_eviction(
2452
2463
 
2453
2464
  coins = TestCoins([singleton_spend.coin, TEST_COIN], {singleton_spend.coin.puzzle_hash: singleton_spend.coin})
2454
2465
 
2455
- mempool_manager = await setup_mempool(coins)
2466
+ async with setup_mempool(coins) as mempool_manager:
2467
+ bundle_add_info = await mempool_manager.add_spend_bundle(
2468
+ bundle,
2469
+ make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
2470
+ bundle.name(),
2471
+ first_added_height=uint32(1),
2472
+ )
2456
2473
 
2457
- bundle_add_info = await mempool_manager.add_spend_bundle(
2458
- bundle,
2459
- make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
2460
- bundle.name(),
2461
- first_added_height=uint32(1),
2462
- )
2474
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2475
+ item = mempool_manager.get_mempool_item(bundle.name())
2476
+ assert item is not None
2477
+ singleton_name = singleton_spend.coin.name()
2478
+ assert item.bundle_coin_spends[singleton_name].supports_fast_forward
2479
+ latest_singleton_lineage = item.bundle_coin_spends[singleton_name].latest_singleton_lineage
2480
+ assert latest_singleton_lineage is not None
2481
+ assert latest_singleton_lineage.coin_id == singleton_name
2463
2482
 
2464
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2465
- item = mempool_manager.get_mempool_item(bundle.name())
2466
- assert item is not None
2467
- singleton_name = singleton_spend.coin.name()
2468
- assert item.bundle_coin_spends[singleton_name].supports_fast_forward
2469
- latest_singleton_lineage = item.bundle_coin_spends[singleton_name].latest_singleton_lineage
2470
- assert latest_singleton_lineage is not None
2471
- assert latest_singleton_lineage.coin_id == singleton_name
2472
-
2473
- spent_coins: list[bytes32] = []
2474
-
2475
- if spend_singleton:
2476
- # pretend that we melted the singleton, the FF spend
2477
- coins.update_lineage(singleton_spend.coin.puzzle_hash, None)
2478
- coins.spend_coin(singleton_spend.coin.name(), uint32(11))
2479
- spent_coins.append(singleton_spend.coin.name())
2480
-
2481
- if spend_plain:
2482
- # pretend that we spend singleton, the FF spend
2483
- coins.spend_coin(coin_spend.coin.name(), uint32(11))
2484
- spent_coins.append(coin_spend.coin.name())
2485
-
2486
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2487
- invariant_check_mempool(mempool_manager.mempool)
2483
+ spent_coins: list[bytes32] = []
2488
2484
 
2489
- if reverse_spend_order:
2490
- spent_coins.reverse()
2485
+ if spend_singleton:
2486
+ # pretend that we melted the singleton, the FF spend
2487
+ coins.update_lineage(singleton_spend.coin.puzzle_hash, None)
2488
+ coins.spend_coin(singleton_spend.coin.name(), uint32(11))
2489
+ spent_coins.append(singleton_spend.coin.name())
2491
2490
 
2492
- await advance_mempool(mempool_manager, spent_coins, use_optimization=use_optimization)
2491
+ if spend_plain:
2492
+ # pretend that we spend singleton, the FF spend
2493
+ coins.spend_coin(coin_spend.coin.name(), uint32(11))
2494
+ spent_coins.append(coin_spend.coin.name())
2493
2495
 
2494
- # make sure the mempool item is evicted
2495
- if spend_singleton or spend_plain:
2496
- assert mempool_manager.get_mempool_item(bundle.name()) is None
2497
- else:
2498
- item = mempool_manager.get_mempool_item(bundle.name())
2499
- assert item is not None
2500
- assert item.bundle_coin_spends[singleton_spend.coin.name()].supports_fast_forward
2501
- latest_singleton_lineage = item.bundle_coin_spends[singleton_spend.coin.name()].latest_singleton_lineage
2502
- assert latest_singleton_lineage is not None
2503
- assert latest_singleton_lineage.coin_id == singleton_spend.coin.name()
2496
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2497
+ invariant_check_mempool(mempool_manager.mempool)
2498
+
2499
+ if reverse_spend_order:
2500
+ spent_coins.reverse()
2501
+
2502
+ await advance_mempool(mempool_manager, spent_coins, use_optimization=use_optimization)
2503
+
2504
+ # make sure the mempool item is evicted
2505
+ if spend_singleton or spend_plain:
2506
+ assert mempool_manager.get_mempool_item(bundle.name()) is None
2507
+ else:
2508
+ item = mempool_manager.get_mempool_item(bundle.name())
2509
+ assert item is not None
2510
+ assert item.bundle_coin_spends[singleton_spend.coin.name()].supports_fast_forward
2511
+ latest_singleton_lineage = item.bundle_coin_spends[singleton_spend.coin.name()].latest_singleton_lineage
2512
+ assert latest_singleton_lineage is not None
2513
+ assert latest_singleton_lineage.coin_id == singleton_spend.coin.name()
2504
2514
 
2505
2515
 
2506
2516
  @pytest.mark.anyio
@@ -2534,45 +2544,44 @@ async def test_multiple_ff(use_optimization: bool) -> None:
2534
2544
  singleton_ph = singleton_spend2.coin.puzzle_hash
2535
2545
  coins = TestCoins([singleton_spend1.coin, singleton_spend2.coin, TEST_COIN], {singleton_ph: singleton_spend2.coin})
2536
2546
 
2537
- mempool_manager = await setup_mempool(coins)
2538
-
2539
- bundle_add_info = await mempool_manager.add_spend_bundle(
2540
- bundle,
2541
- make_test_conds(
2542
- spend_ids=[
2543
- (singleton_spend1.coin, ELIGIBLE_FOR_FF),
2544
- (singleton_spend2.coin, ELIGIBLE_FOR_FF),
2545
- (TEST_COIN, 0),
2546
- ],
2547
- cost=1000000,
2548
- ),
2549
- bundle.name(),
2550
- first_added_height=uint32(1),
2551
- )
2552
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2553
- invariant_check_mempool(mempool_manager.mempool)
2547
+ async with setup_mempool(coins) as mempool_manager:
2548
+ bundle_add_info = await mempool_manager.add_spend_bundle(
2549
+ bundle,
2550
+ make_test_conds(
2551
+ spend_ids=[
2552
+ (singleton_spend1.coin, ELIGIBLE_FOR_FF),
2553
+ (singleton_spend2.coin, ELIGIBLE_FOR_FF),
2554
+ (TEST_COIN, 0),
2555
+ ],
2556
+ cost=1000000,
2557
+ ),
2558
+ bundle.name(),
2559
+ first_added_height=uint32(1),
2560
+ )
2561
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2562
+ invariant_check_mempool(mempool_manager.mempool)
2554
2563
 
2555
- item = mempool_manager.get_mempool_item(bundle.name())
2556
- assert item is not None
2557
- assert item.bundle_coin_spends[singleton_spend1.coin.name()].supports_fast_forward
2558
- assert item.bundle_coin_spends[singleton_spend2.coin.name()].supports_fast_forward
2559
- assert not item.bundle_coin_spends[coin_spend.coin.name()].supports_fast_forward
2564
+ item = mempool_manager.get_mempool_item(bundle.name())
2565
+ assert item is not None
2566
+ assert item.bundle_coin_spends[singleton_spend1.coin.name()].supports_fast_forward
2567
+ assert item.bundle_coin_spends[singleton_spend2.coin.name()].supports_fast_forward
2568
+ assert not item.bundle_coin_spends[coin_spend.coin.name()].supports_fast_forward
2560
2569
 
2561
- # spend the singleton coin2 and make coin3 the latest version
2562
- coins.update_lineage(singleton_ph, singleton_spend3.coin)
2563
- coins.spend_coin(singleton_spend2.coin.name(), uint32(11))
2570
+ # spend the singleton coin2 and make coin3 the latest version
2571
+ coins.update_lineage(singleton_ph, singleton_spend3.coin)
2572
+ coins.spend_coin(singleton_spend2.coin.name(), uint32(11))
2564
2573
 
2565
- await advance_mempool(mempool_manager, [singleton_spend2.coin.name()], use_optimization=use_optimization)
2574
+ await advance_mempool(mempool_manager, [singleton_spend2.coin.name()], use_optimization=use_optimization)
2566
2575
 
2567
- # we can still fast-forward the singleton spends, the bundle should still be valid
2568
- item = mempool_manager.get_mempool_item(bundle.name())
2569
- assert item is not None
2570
- spend = item.bundle_coin_spends[singleton_spend1.coin.name()]
2571
- assert spend.latest_singleton_lineage is not None
2572
- assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
2573
- spend = item.bundle_coin_spends[singleton_spend2.coin.name()]
2574
- assert spend.latest_singleton_lineage is not None
2575
- assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
2576
+ # we can still fast-forward the singleton spends, the bundle should still be valid
2577
+ item = mempool_manager.get_mempool_item(bundle.name())
2578
+ assert item is not None
2579
+ spend = item.bundle_coin_spends[singleton_spend1.coin.name()]
2580
+ assert spend.latest_singleton_lineage is not None
2581
+ assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
2582
+ spend = item.bundle_coin_spends[singleton_spend2.coin.name()]
2583
+ assert spend.latest_singleton_lineage is not None
2584
+ assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
2576
2585
 
2577
2586
 
2578
2587
  @pytest.mark.anyio
@@ -2604,47 +2613,46 @@ async def test_advancing_ff(use_optimization: bool) -> None:
2604
2613
  singleton_ph = spend_a.coin.puzzle_hash
2605
2614
  coins = TestCoins([spend_a.coin, spend_b.coin, spend_c.coin, TEST_COIN], {singleton_ph: spend_a.coin})
2606
2615
 
2607
- mempool_manager = await setup_mempool(coins)
2608
-
2609
- bundle_add_info = await mempool_manager.add_spend_bundle(
2610
- bundle,
2611
- make_test_conds(spend_ids=[(spend_a.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
2612
- bundle.name(),
2613
- first_added_height=uint32(1),
2614
- )
2615
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2616
- invariant_check_mempool(mempool_manager.mempool)
2616
+ async with setup_mempool(coins) as mempool_manager:
2617
+ bundle_add_info = await mempool_manager.add_spend_bundle(
2618
+ bundle,
2619
+ make_test_conds(spend_ids=[(spend_a.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
2620
+ bundle.name(),
2621
+ first_added_height=uint32(1),
2622
+ )
2623
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2624
+ invariant_check_mempool(mempool_manager.mempool)
2617
2625
 
2618
- item = mempool_manager.get_mempool_item(bundle.name())
2619
- assert item is not None
2620
- spend = item.bundle_coin_spends[spend_a.coin.name()]
2621
- assert spend.supports_fast_forward
2622
- assert spend.latest_singleton_lineage is not None
2623
- assert spend.latest_singleton_lineage.coin_id == spend_a.coin.name()
2626
+ item = mempool_manager.get_mempool_item(bundle.name())
2627
+ assert item is not None
2628
+ spend = item.bundle_coin_spends[spend_a.coin.name()]
2629
+ assert spend.supports_fast_forward
2630
+ assert spend.latest_singleton_lineage is not None
2631
+ assert spend.latest_singleton_lineage.coin_id == spend_a.coin.name()
2624
2632
 
2625
- coins.update_lineage(singleton_ph, spend_b.coin)
2626
- coins.spend_coin(spend_a.coin.name(), uint32(11))
2633
+ coins.update_lineage(singleton_ph, spend_b.coin)
2634
+ coins.spend_coin(spend_a.coin.name(), uint32(11))
2627
2635
 
2628
- await advance_mempool(mempool_manager, [spend_a.coin.name()])
2636
+ await advance_mempool(mempool_manager, [spend_a.coin.name()])
2629
2637
 
2630
- item = mempool_manager.get_mempool_item(bundle.name())
2631
- assert item is not None
2632
- spend = item.bundle_coin_spends[spend_a.coin.name()]
2633
- assert spend.supports_fast_forward
2634
- assert spend.latest_singleton_lineage is not None
2635
- assert spend.latest_singleton_lineage.coin_id == spend_b.coin.name()
2638
+ item = mempool_manager.get_mempool_item(bundle.name())
2639
+ assert item is not None
2640
+ spend = item.bundle_coin_spends[spend_a.coin.name()]
2641
+ assert spend.supports_fast_forward
2642
+ assert spend.latest_singleton_lineage is not None
2643
+ assert spend.latest_singleton_lineage.coin_id == spend_b.coin.name()
2636
2644
 
2637
- coins.update_lineage(singleton_ph, spend_c.coin)
2638
- coins.spend_coin(spend_b.coin.name(), uint32(12))
2645
+ coins.update_lineage(singleton_ph, spend_c.coin)
2646
+ coins.spend_coin(spend_b.coin.name(), uint32(12))
2639
2647
 
2640
- await advance_mempool(mempool_manager, [spend_b.coin.name()], use_optimization=use_optimization)
2648
+ await advance_mempool(mempool_manager, [spend_b.coin.name()], use_optimization=use_optimization)
2641
2649
 
2642
- item = mempool_manager.get_mempool_item(bundle.name())
2643
- assert item is not None
2644
- spend = item.bundle_coin_spends[spend_a.coin.name()]
2645
- assert spend.supports_fast_forward
2646
- assert spend.latest_singleton_lineage is not None
2647
- assert spend.latest_singleton_lineage.coin_id == spend_c.coin.name()
2650
+ item = mempool_manager.get_mempool_item(bundle.name())
2651
+ assert item is not None
2652
+ spend = item.bundle_coin_spends[spend_a.coin.name()]
2653
+ assert spend.supports_fast_forward
2654
+ assert spend.latest_singleton_lineage is not None
2655
+ assert spend.latest_singleton_lineage.coin_id == spend_c.coin.name()
2648
2656
 
2649
2657
 
2650
2658
  @pytest.mark.parametrize("old", [True, False])
@@ -2653,15 +2661,14 @@ def test_no_peak(old: bool, transactions_1000: list[SpendBundle]) -> None:
2653
2661
  all_coins = [s.coin for b in bundles for s in b.coin_spends]
2654
2662
  coins = TestCoins(all_coins, {})
2655
2663
 
2656
- mempool_manager = MempoolManager(
2664
+ with MempoolManager(
2657
2665
  coins.get_coin_records,
2658
2666
  coins.get_unspent_lineage_info,
2659
2667
  DEFAULT_CONSTANTS,
2660
- )
2661
-
2662
- create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2668
+ ) as mempool_manager:
2669
+ create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2663
2670
 
2664
- assert create_block(bytes32([1] * 32), 10.0) is None
2671
+ assert create_block(bytes32([1] * 32), 10.0) is None
2665
2672
 
2666
2673
 
2667
2674
  @pytest.fixture(name="test_wallet")
@@ -2766,77 +2773,76 @@ async def test_create_block_generator(
2766
2773
  rng = random.Random(seed)
2767
2774
 
2768
2775
  # run the test multiple times, generating different combinations of mempools
2769
- mempool_manager = await setup_mempool(coins)
2770
-
2771
- included_bundles = rng.sample(bundles, mempool_size)
2772
- expected_additions: set[Coin] = set()
2773
- expected_removals: set[Coin] = set()
2774
- expected_signature = G2Element()
2775
- for sb in included_bundles:
2776
- pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
2777
- bundle_add_info = await mempool_manager.add_spend_bundle(
2778
- sb, pre_validation, sb.name(), first_added_height=uint32(1)
2776
+ async with setup_mempool(coins) as mempool_manager:
2777
+ included_bundles = rng.sample(bundles, mempool_size)
2778
+ expected_additions: set[Coin] = set()
2779
+ expected_removals: set[Coin] = set()
2780
+ expected_signature = G2Element()
2781
+ for sb in included_bundles:
2782
+ pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
2783
+ bundle_add_info = await mempool_manager.add_spend_bundle(
2784
+ sb, pre_validation, sb.name(), first_added_height=uint32(1)
2785
+ )
2786
+ expected_additions.update(sb.additions())
2787
+ expected_removals.update(sb.removals())
2788
+
2789
+ expected_signature += sb.aggregated_signature
2790
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2791
+ item = mempool_manager.get_mempool_item(sb.name())
2792
+ assert item is not None
2793
+ all_items = mempool_manager.mempool.all_items()
2794
+ assert len(list(all_items)) == len(included_bundles)
2795
+
2796
+ invariant_check_mempool(mempool_manager.mempool)
2797
+
2798
+ assert mempool_manager.peak is not None
2799
+ create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2800
+ new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2801
+ assert new_block_gen is not None
2802
+
2803
+ # now, make sure the generator we got is valid
2804
+
2805
+ if expect_failure:
2806
+ assert len(expected_additions) != len(new_block_gen.additions)
2807
+ assert expected_additions != set(new_block_gen.additions)
2808
+ assert len(expected_removals) != len(new_block_gen.removals)
2809
+ assert expected_removals != set(new_block_gen.removals)
2810
+ assert expected_signature != new_block_gen.signature
2811
+ else:
2812
+ assert len(expected_additions) == len(new_block_gen.additions)
2813
+ assert expected_additions == set(new_block_gen.additions)
2814
+ assert len(expected_removals) == len(new_block_gen.removals)
2815
+ assert expected_removals == set(new_block_gen.removals)
2816
+ assert expected_signature == new_block_gen.signature
2817
+
2818
+ err, conds = run_block_generator2(
2819
+ bytes(new_block_gen.program),
2820
+ new_block_gen.generator_refs,
2821
+ DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
2822
+ DEFAULT_FLAGS,
2823
+ new_block_gen.signature,
2824
+ None,
2825
+ DEFAULT_CONSTANTS,
2779
2826
  )
2780
- expected_additions.update(sb.additions())
2781
- expected_removals.update(sb.removals())
2782
2827
 
2783
- expected_signature += sb.aggregated_signature
2784
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2785
- item = mempool_manager.get_mempool_item(sb.name())
2786
- assert item is not None
2787
- all_items = mempool_manager.mempool.all_items()
2788
- assert len(list(all_items)) == len(included_bundles)
2789
-
2790
- invariant_check_mempool(mempool_manager.mempool)
2828
+ assert err is None
2829
+ assert conds is not None
2791
2830
 
2792
- assert mempool_manager.peak is not None
2793
- create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2794
- new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2795
- assert new_block_gen is not None
2796
-
2797
- # now, make sure the generator we got is valid
2798
-
2799
- if expect_failure:
2800
- assert len(expected_additions) != len(new_block_gen.additions)
2801
- assert expected_additions != set(new_block_gen.additions)
2802
- assert len(expected_removals) != len(new_block_gen.removals)
2803
- assert expected_removals != set(new_block_gen.removals)
2804
- assert expected_signature != new_block_gen.signature
2805
- else:
2806
- assert len(expected_additions) == len(new_block_gen.additions)
2807
- assert expected_additions == set(new_block_gen.additions)
2808
- assert len(expected_removals) == len(new_block_gen.removals)
2809
- assert expected_removals == set(new_block_gen.removals)
2810
- assert expected_signature == new_block_gen.signature
2811
-
2812
- err, conds = run_block_generator2(
2813
- bytes(new_block_gen.program),
2814
- new_block_gen.generator_refs,
2815
- DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
2816
- DEFAULT_FLAGS,
2817
- new_block_gen.signature,
2818
- None,
2819
- DEFAULT_CONSTANTS,
2820
- )
2821
-
2822
- assert err is None
2823
- assert conds is not None
2824
-
2825
- if expect_failure:
2826
- assert len(conds.spends) != len(expected_removals)
2827
- else:
2828
- assert len(conds.spends) == len(expected_removals)
2829
- assert conds.cost < DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
2830
- assert new_block_gen.cost == conds.cost
2831
+ if expect_failure:
2832
+ assert len(conds.spends) != len(expected_removals)
2833
+ else:
2834
+ assert len(conds.spends) == len(expected_removals)
2835
+ assert conds.cost < DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
2836
+ assert new_block_gen.cost == conds.cost
2831
2837
 
2832
- num_additions = 0
2833
- for spend in conds.spends:
2834
- assert Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)) in expected_removals
2835
- for add2 in spend.create_coin:
2836
- assert Coin(spend.coin_id, add2[0], uint64(add2[1])) in expected_additions
2837
- num_additions += 1
2838
+ num_additions = 0
2839
+ for spend in conds.spends:
2840
+ assert Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)) in expected_removals
2841
+ for add2 in spend.create_coin:
2842
+ assert Coin(spend.coin_id, add2[0], uint64(add2[1])) in expected_additions
2843
+ num_additions += 1
2838
2844
 
2839
- assert num_additions == len(new_block_gen.additions)
2845
+ assert num_additions == len(new_block_gen.additions)
2840
2846
 
2841
2847
 
2842
2848
  # if we try to fill the mempool with more than 550, all spends won't
@@ -2850,58 +2856,57 @@ async def test_create_block_generator_real_bundles(seed: int, old: bool, test_bu
2850
2856
 
2851
2857
  rng = random.Random(seed)
2852
2858
 
2853
- mempool_manager = await setup_mempool(coins)
2859
+ async with setup_mempool(coins) as mempool_manager:
2860
+ included_bundles = rng.sample(test_bundles, len(test_bundles) // 5)
2861
+ for sb in included_bundles:
2862
+ pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
2863
+ bundle_add_info = await mempool_manager.add_spend_bundle(
2864
+ sb, pre_validation, sb.name(), first_added_height=uint32(1)
2865
+ )
2854
2866
 
2855
- included_bundles = rng.sample(test_bundles, len(test_bundles) // 5)
2856
- for sb in included_bundles:
2857
- pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
2858
- bundle_add_info = await mempool_manager.add_spend_bundle(
2859
- sb, pre_validation, sb.name(), first_added_height=uint32(1)
2867
+ # in the test bundles, we have some duplicate spends
2868
+ # just ignore them for now
2869
+ if bundle_add_info.status == MempoolInclusionStatus.FAILED:
2870
+ assert bundle_add_info.error == Err.DOUBLE_SPEND
2871
+ continue
2872
+ assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2873
+ item = mempool_manager.get_mempool_item(sb.name())
2874
+ assert item is not None
2875
+
2876
+ invariant_check_mempool(mempool_manager.mempool)
2877
+
2878
+ assert mempool_manager.peak is not None
2879
+ create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2880
+ new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2881
+ assert new_block_gen is not None
2882
+
2883
+ # now, make sure the generator we got is valid
2884
+
2885
+ err, conds = run_block_generator2(
2886
+ bytes(new_block_gen.program),
2887
+ new_block_gen.generator_refs,
2888
+ DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
2889
+ DEFAULT_FLAGS,
2890
+ new_block_gen.signature,
2891
+ None,
2892
+ DEFAULT_CONSTANTS,
2860
2893
  )
2861
2894
 
2862
- # in the test bundles, we have some duplicate spends
2863
- # just ignore them for now
2864
- if bundle_add_info.status == MempoolInclusionStatus.FAILED:
2865
- assert bundle_add_info.error == Err.DOUBLE_SPEND
2866
- continue
2867
- assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
2868
- item = mempool_manager.get_mempool_item(sb.name())
2869
- assert item is not None
2895
+ assert err is None
2896
+ assert conds is not None
2870
2897
 
2871
- invariant_check_mempool(mempool_manager.mempool)
2898
+ assert conds.cost == new_block_gen.cost
2872
2899
 
2873
- assert mempool_manager.peak is not None
2874
- create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2875
- new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2876
- assert new_block_gen is not None
2877
-
2878
- # now, make sure the generator we got is valid
2879
-
2880
- err, conds = run_block_generator2(
2881
- bytes(new_block_gen.program),
2882
- new_block_gen.generator_refs,
2883
- DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
2884
- DEFAULT_FLAGS,
2885
- new_block_gen.signature,
2886
- None,
2887
- DEFAULT_CONSTANTS,
2888
- )
2889
-
2890
- assert err is None
2891
- assert conds is not None
2900
+ removals: set[Coin] = set()
2901
+ additions: set[Coin] = set()
2892
2902
 
2893
- assert conds.cost == new_block_gen.cost
2903
+ for spend in conds.spends:
2904
+ removals.add(Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)))
2905
+ for add in spend.create_coin:
2906
+ additions.add(Coin(spend.coin_id, add[0], uint64(add[1])))
2894
2907
 
2895
- removals: set[Coin] = set()
2896
- additions: set[Coin] = set()
2897
-
2898
- for spend in conds.spends:
2899
- removals.add(Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)))
2900
- for add in spend.create_coin:
2901
- additions.add(Coin(spend.coin_id, add[0], uint64(add[1])))
2902
-
2903
- assert removals == set(new_block_gen.removals)
2904
- assert additions == set(new_block_gen.additions)
2908
+ assert removals == set(new_block_gen.removals)
2909
+ assert additions == set(new_block_gen.additions)
2905
2910
 
2906
2911
 
2907
2912
  @pytest.mark.anyio
@@ -2920,30 +2925,33 @@ async def test_spending_singleton_to_invalidate_existing_ff_spends() -> None:
2920
2925
  coins=[singleton_spend1.coin, singleton_spend2.coin, TEST_COIN, TEST_COIN2],
2921
2926
  lineage={singleton_spend2.coin.puzzle_hash: singleton_spend2.coin},
2922
2927
  )
2923
- mempool_manager = await setup_mempool(coins)
2924
- coin_spend1 = make_spend(
2925
- TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2926
- )
2927
- sb1 = SpendBundle([singleton_spend1, coin_spend1], G2Element())
2928
- sb1_conds = make_test_conds(spend_ids=[(singleton_spend1.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=100_000_000)
2929
- bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
2930
- assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
2931
- invariant_check_mempool(mempool_manager.mempool)
2932
- # Trying to spend the same singleton with a different child amount should
2933
- # trigger a conflict on any replace by fee attempt.
2934
- coin_spend2 = make_spend(
2935
- TEST_COIN2, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2936
- )
2937
- sb2 = SpendBundle([singleton_spend2, coin_spend1, coin_spend2], G2Element())
2938
- # This singleton spend is not eligible for fast forward as its next
2939
- # iteration has a different amount.
2940
- sb2_conds = make_test_conds(spend_ids=[(singleton_spend2.coin, 0), (TEST_COIN, 0), (TEST_COIN2, 0)], cost=1337)
2941
- # This transaction conflicts with the previous one no matter what fee you
2942
- # pay, because we're changing the fast forward eligibility flag for the
2943
- # singleton spend.
2944
- bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
2945
- assert bundle_add_info2.error == Err.MEMPOOL_CONFLICT
2946
- assert bundle_add_info2.status == MempoolInclusionStatus.PENDING
2928
+
2929
+ async with setup_mempool(coins) as mempool_manager:
2930
+ coin_spend1 = make_spend(
2931
+ TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2932
+ )
2933
+ sb1 = SpendBundle([singleton_spend1, coin_spend1], G2Element())
2934
+ sb1_conds = make_test_conds(
2935
+ spend_ids=[(singleton_spend1.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=100_000_000
2936
+ )
2937
+ bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
2938
+ assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
2939
+ invariant_check_mempool(mempool_manager.mempool)
2940
+ # Trying to spend the same singleton with a different child amount should
2941
+ # trigger a conflict on any replace by fee attempt.
2942
+ coin_spend2 = make_spend(
2943
+ TEST_COIN2, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2944
+ )
2945
+ sb2 = SpendBundle([singleton_spend2, coin_spend1, coin_spend2], G2Element())
2946
+ # This singleton spend is not eligible for fast forward as its next
2947
+ # iteration has a different amount.
2948
+ sb2_conds = make_test_conds(spend_ids=[(singleton_spend2.coin, 0), (TEST_COIN, 0), (TEST_COIN2, 0)], cost=1337)
2949
+ # This transaction conflicts with the previous one no matter what fee you
2950
+ # pay, because we're changing the fast forward eligibility flag for the
2951
+ # singleton spend.
2952
+ bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
2953
+ assert bundle_add_info2.error == Err.MEMPOOL_CONFLICT
2954
+ assert bundle_add_info2.status == MempoolInclusionStatus.PENDING
2947
2955
 
2948
2956
 
2949
2957
  @pytest.mark.parametrize("flags", [ELIGIBLE_FOR_DEDUP, ELIGIBLE_FOR_FF, ELIGIBLE_FOR_FF | ELIGIBLE_FOR_DEDUP])
@@ -2956,37 +2964,38 @@ async def test_check_removals_with_block_creation(flags: int, old: bool) -> None
2956
2964
  coins = TestCoins(
2957
2965
  coins=[singleton_spend.coin, TEST_COIN], lineage={singleton_spend.coin.puzzle_hash: singleton_spend.coin}
2958
2966
  )
2959
- mempool_manager = await setup_mempool(coins)
2960
- sb1 = SpendBundle([singleton_spend], G2Element())
2961
- sb1_conds = make_test_conds(
2962
- spend_ids=[(singleton_spend.coin, 0)],
2963
- created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)]],
2964
- cost=100_000_000,
2965
- )
2966
- bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
2967
- assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
2968
- invariant_check_mempool(mempool_manager.mempool)
2969
- extra_spend = make_spend(
2970
- TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2971
- )
2972
- sb2 = SpendBundle([singleton_spend, extra_spend], G2Element())
2973
- sb2_conds = make_test_conds(
2974
- spend_ids=[(singleton_spend.coin, flags), (TEST_COIN, 0)],
2975
- created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)], []],
2976
- cost=1337,
2977
- )
2978
- bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
2979
- assert bundle_add_info2.status == MempoolInclusionStatus.SUCCESS
2980
- assert mempool_manager.peak is not None
2981
- create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2982
- new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2983
- assert new_block_gen is not None
2984
- assert len(new_block_gen.additions) == 1
2985
- assert set(new_block_gen.additions) == {
2986
- Coin(singleton_spend.coin.name(), singleton_spend.coin.puzzle_hash, uint64(1))
2987
- }
2988
- assert len(new_block_gen.removals) == 2
2989
- assert set(new_block_gen.removals) == {singleton_spend.coin, TEST_COIN}
2967
+
2968
+ async with setup_mempool(coins) as mempool_manager:
2969
+ sb1 = SpendBundle([singleton_spend], G2Element())
2970
+ sb1_conds = make_test_conds(
2971
+ spend_ids=[(singleton_spend.coin, 0)],
2972
+ created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)]],
2973
+ cost=100_000_000,
2974
+ )
2975
+ bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
2976
+ assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
2977
+ invariant_check_mempool(mempool_manager.mempool)
2978
+ extra_spend = make_spend(
2979
+ TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2980
+ )
2981
+ sb2 = SpendBundle([singleton_spend, extra_spend], G2Element())
2982
+ sb2_conds = make_test_conds(
2983
+ spend_ids=[(singleton_spend.coin, flags), (TEST_COIN, 0)],
2984
+ created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)], []],
2985
+ cost=1337,
2986
+ )
2987
+ bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
2988
+ assert bundle_add_info2.status == MempoolInclusionStatus.SUCCESS
2989
+ assert mempool_manager.peak is not None
2990
+ create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
2991
+ new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
2992
+ assert new_block_gen is not None
2993
+ assert len(new_block_gen.additions) == 1
2994
+ assert set(new_block_gen.additions) == {
2995
+ Coin(singleton_spend.coin.name(), singleton_spend.coin.puzzle_hash, uint64(1))
2996
+ }
2997
+ assert len(new_block_gen.removals) == 2
2998
+ assert set(new_block_gen.removals) == {singleton_spend.coin, TEST_COIN}
2990
2999
 
2991
3000
 
2992
3001
  @pytest.mark.anyio
@@ -2994,12 +3003,12 @@ async def test_dedup_not_canonical() -> None:
2994
3003
  # this is ((1)), but with a non-canonical encoding
2995
3004
  coin_spend = mk_coin_spend(TEST_COIN, solution="ffffc001018080")
2996
3005
  coins = TestCoins([TEST_COIN], lineage={})
2997
- mempool_manager = await setup_mempool(coins)
2998
- sb = SpendBundle([coin_spend], G2Element())
2999
- sb_conds = make_test_conds(spend_ids=[(TEST_COIN, ELIGIBLE_FOR_DEDUP)])
3000
- bundle_add_info = await mempool_manager.add_spend_bundle(sb, sb_conds, sb.name(), uint32(1))
3001
- assert bundle_add_info.status == MempoolInclusionStatus.FAILED
3002
- assert bundle_add_info.error == Err.INVALID_COIN_SOLUTION
3006
+ async with setup_mempool(coins) as mempool_manager:
3007
+ sb = SpendBundle([coin_spend], G2Element())
3008
+ sb_conds = make_test_conds(spend_ids=[(TEST_COIN, ELIGIBLE_FOR_DEDUP)])
3009
+ bundle_add_info = await mempool_manager.add_spend_bundle(sb, sb_conds, sb.name(), uint32(1))
3010
+ assert bundle_add_info.status == MempoolInclusionStatus.FAILED
3011
+ assert bundle_add_info.error == Err.INVALID_COIN_SOLUTION
3003
3012
 
3004
3013
 
3005
3014
  def make_coin_record(coin: Coin, spent_block_index: int = 0) -> CoinRecord:
@@ -3012,7 +3021,7 @@ class CheckRemovalsCase:
3012
3021
  removals: dict[bytes32, CoinRecord]
3013
3022
  bundle_coin_spends: dict[bytes32, BundleCoinSpend] = dataclasses.field(default_factory=dict)
3014
3023
  conflicting_mempool_items: dict[bytes32, list[MempoolItem]] = dataclasses.field(default_factory=dict)
3015
- expected_result: tuple[Optional[Err], list[MempoolItem]] = dataclasses.field(default_factory=lambda: (None, []))
3024
+ expected_result: tuple[Err | None, list[MempoolItem]] = dataclasses.field(default_factory=lambda: (None, []))
3016
3025
  marks: Marks = ()
3017
3026
 
3018
3027
 
@@ -3161,6 +3170,119 @@ def test_check_removals(case: CheckRemovalsCase) -> None:
3161
3170
  assert set(conflicts) == set(expected_conflicts)
3162
3171
 
3163
3172
 
3173
+ # this puzzle just creates coins, however many are requested by the solution
3174
+ # (mod (A)
3175
+ # (defun loop (n)
3176
+ # (if (= n 1)
3177
+ # (list)
3178
+ # (c (list 51 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff n) (loop (- n 1))))
3179
+ # )
3180
+ # (loop A)
3181
+ # )
3182
+ create_coins_loop: str = (
3183
+ "ff02ffff01ff02ff02ffff04ff02ffff04ff05ff80808080ffff04ffff01ff02"
3184
+ "ffff03ffff09ff05ffff010180ff80ffff01ff04ffff04ffff0133ffff04ffff"
3185
+ "01a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3186
+ "ffffffff04ff05ff80808080ffff02ff02ffff04ff02ffff04ffff11ff05ffff"
3187
+ "010180ff808080808080ff0180ff018080"
3188
+ )
3189
+
3190
+ # (mod (A)
3191
+ # (defun loop (n)
3192
+ # (if (= n 0) (list) (c n (loop (- n 1))))
3193
+ # )
3194
+ # (c (c 1 (loop A)) ())
3195
+ # )
3196
+ deep_recursion: str = (
3197
+ "ff02ffff01ff04ffff04ffff0101ffff02ff02ffff04ff02ffff04ff05ff8080"
3198
+ "808080ff8080ffff04ffff01ff02ffff03ffff09ff05ff8080ff80ffff01ff04"
3199
+ "ff05ffff02ff02ffff04ff02ffff04ffff11ff05ffff010180ff808080808080"
3200
+ "ff0180ff018080"
3201
+ )
3202
+
3203
+
3204
+ # this test uses artificial puzzles just to exercise the block creation. These
3205
+ # spends are expected not to verify any signatures
3206
+ # This is to keep the test simple.
3207
+ @pytest.mark.parametrize(
3208
+ "puzzle, solution",
3209
+ [
3210
+ pytest.param(create_coins_loop, "ff8207d180", id="2000-coins"),
3211
+ pytest.param(create_coins_loop, "ff8203e980", id="1000-coins"),
3212
+ pytest.param(create_coins_loop, "ff8201f580", id="500 coins"),
3213
+ pytest.param(deep_recursion, "ff830f424080", id="recurse-1000000"),
3214
+ pytest.param(deep_recursion, "ff82271080", id="recurse-10000"),
3215
+ pytest.param(deep_recursion, "ff6480", id="recurse-100"),
3216
+ ],
3217
+ )
3218
+ @pytest.mark.parametrize("old", [True, False])
3219
+ @pytest.mark.anyio
3220
+ async def test_create_block_generator_custom_spend(
3221
+ puzzle: str, solution: str, old: bool, softfork_height: uint32
3222
+ ) -> None:
3223
+ solution_str = SerializedProgram.fromhex(solution)
3224
+ puzzle_reveal = SerializedProgram.fromhex(puzzle)
3225
+ puzzle_hash = puzzle_reveal.get_tree_hash()
3226
+
3227
+ async with setup_mempool_with_coins(
3228
+ coin_amounts=list(range(100000000, 100000022)), puzzle_hash=puzzle_hash, height=softfork_height
3229
+ ) as (mempool_manager, coins):
3230
+ spend_bundles = [
3231
+ SpendBundle(
3232
+ coin_spends=[CoinSpend(coin, puzzle_reveal=puzzle_reveal, solution=solution_str)],
3233
+ aggregated_signature=G2Element(),
3234
+ )
3235
+ for coin in coins
3236
+ ]
3237
+
3238
+ for sb in spend_bundles:
3239
+ try:
3240
+ conds2 = await mempool_manager.pre_validate_spendbundle(sb)
3241
+ await mempool_manager.add_spend_bundle(sb, conds2, sb.name(), softfork_height)
3242
+ invariant_check_mempool(mempool_manager.mempool)
3243
+ except Exception as e:
3244
+ print(f"not adding bundle: {e}")
3245
+ # we don't expect this coin to be spent by the resulting generator
3246
+ # so remove it from the list
3247
+ for cs in sb.coin_spends:
3248
+ coins.remove(cs.coin)
3249
+
3250
+ create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
3251
+ assert mempool_manager.peak is not None
3252
+ generator = create_block(mempool_manager.peak.header_hash, 10.0)
3253
+
3254
+ if len(coins) == 0:
3255
+ assert generator is None
3256
+ else:
3257
+ assert generator is not None
3258
+
3259
+ assert generator.signature == G2Element()
3260
+
3261
+ removals = set(generator.removals)
3262
+
3263
+ err, conds = run_block_generator2(
3264
+ bytes(generator.program),
3265
+ generator.generator_refs,
3266
+ DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
3267
+ 0,
3268
+ generator.signature,
3269
+ None,
3270
+ DEFAULT_CONSTANTS,
3271
+ )
3272
+
3273
+ assert err is None
3274
+ assert conds is not None
3275
+
3276
+ assert len(conds.spends) == len(removals)
3277
+
3278
+ for spend in conds.spends:
3279
+ removal = Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount))
3280
+ assert removal in coins
3281
+ assert removal in removals
3282
+
3283
+ invariant_check_mempool(mempool_manager.mempool)
3284
+
3285
+
3164
3286
  @pytest.mark.anyio
3165
3287
  async def test_new_peak_deferred_ff_items() -> None:
3166
3288
  """
@@ -3178,37 +3300,37 @@ async def test_new_peak_deferred_ff_items() -> None:
3178
3300
  singleton_spend2.coin.puzzle_hash: singleton_spend2.coin,
3179
3301
  },
3180
3302
  )
3181
- mempool_manager = await setup_mempool(coins)
3182
- # Let's submit the two singletons transactions to the mempool
3183
- sb_names = []
3184
- for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
3185
- sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
3186
- sb_name = sb.name()
3187
- await mempool_manager.add_spend_bundle(
3188
- sb,
3189
- make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
3190
- sb_name,
3191
- uint32(1),
3192
- )
3193
- assert mempool_manager.get_mempool_item(sb_name) is not None
3194
- sb_names.append(sb_name)
3195
- # Let's advance the mempool by spending these singletons into new lineages
3196
- singleton1_new_latest = Coin(singleton1_id, singleton_spend1.coin.puzzle_hash, singleton_spend1.coin.amount)
3197
- coins.update_lineage(singleton_spend1.coin.puzzle_hash, singleton1_new_latest)
3198
- singleton2_new_latest = Coin(singleton2_id, singleton_spend2.coin.puzzle_hash, singleton_spend2.coin.amount)
3199
- coins.update_lineage(singleton_spend2.coin.puzzle_hash, singleton2_new_latest)
3200
- await advance_mempool(mempool_manager, [singleton1_id, singleton2_id], use_optimization=True)
3201
- # Both items should get updated with their related latest lineages
3202
- mi1 = mempool_manager.get_mempool_item(sb_names[0])
3203
- assert mi1 is not None
3204
- latest_singleton_lineage1 = mi1.bundle_coin_spends[singleton1_id].latest_singleton_lineage
3205
- assert latest_singleton_lineage1 is not None
3206
- assert latest_singleton_lineage1.coin_id == singleton1_new_latest.name()
3207
- mi2 = mempool_manager.get_mempool_item(sb_names[1])
3208
- assert mi2 is not None
3209
- latest_singleton_lineage2 = mi2.bundle_coin_spends[singleton2_id].latest_singleton_lineage
3210
- assert latest_singleton_lineage2 is not None
3211
- assert latest_singleton_lineage2.coin_id == singleton2_new_latest.name()
3303
+ async with setup_mempool(coins) as mempool_manager:
3304
+ # Let's submit the two singletons transactions to the mempool
3305
+ sb_names = []
3306
+ for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
3307
+ sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
3308
+ sb_name = sb.name()
3309
+ await mempool_manager.add_spend_bundle(
3310
+ sb,
3311
+ make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
3312
+ sb_name,
3313
+ uint32(1),
3314
+ )
3315
+ assert mempool_manager.get_mempool_item(sb_name) is not None
3316
+ sb_names.append(sb_name)
3317
+ # Let's advance the mempool by spending these singletons into new lineages
3318
+ singleton1_new_latest = Coin(singleton1_id, singleton_spend1.coin.puzzle_hash, singleton_spend1.coin.amount)
3319
+ coins.update_lineage(singleton_spend1.coin.puzzle_hash, singleton1_new_latest)
3320
+ singleton2_new_latest = Coin(singleton2_id, singleton_spend2.coin.puzzle_hash, singleton_spend2.coin.amount)
3321
+ coins.update_lineage(singleton_spend2.coin.puzzle_hash, singleton2_new_latest)
3322
+ await advance_mempool(mempool_manager, [singleton1_id, singleton2_id], use_optimization=True)
3323
+ # Both items should get updated with their related latest lineages
3324
+ mi1 = mempool_manager.get_mempool_item(sb_names[0])
3325
+ assert mi1 is not None
3326
+ latest_singleton_lineage1 = mi1.bundle_coin_spends[singleton1_id].latest_singleton_lineage
3327
+ assert latest_singleton_lineage1 is not None
3328
+ assert latest_singleton_lineage1.coin_id == singleton1_new_latest.name()
3329
+ mi2 = mempool_manager.get_mempool_item(sb_names[1])
3330
+ assert mi2 is not None
3331
+ latest_singleton_lineage2 = mi2.bundle_coin_spends[singleton2_id].latest_singleton_lineage
3332
+ assert latest_singleton_lineage2 is not None
3333
+ assert latest_singleton_lineage2.coin_id == singleton2_new_latest.name()
3212
3334
 
3213
3335
 
3214
3336
  @pytest.mark.anyio
@@ -3226,47 +3348,47 @@ async def test_different_ff_versions() -> None:
3226
3348
  coins = TestCoins(
3227
3349
  [singleton_spend1.coin, singleton_spend2.coin, TEST_COIN, TEST_COIN2], {singleton_ph: singleton_spend2.coin}
3228
3350
  )
3229
- mempool_manager = await setup_mempool(coins)
3230
- mempool_items: list[MempoolItem] = []
3231
- for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
3232
- sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
3233
- sb_name = sb.name()
3234
- await mempool_manager.add_spend_bundle(
3235
- sb,
3236
- make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
3237
- sb_name,
3238
- uint32(1),
3239
- )
3240
- mi = mempool_manager.get_mempool_item(sb_name)
3241
- assert mi is not None
3242
- mempool_items.append(mi)
3243
- [mi1, mi2] = mempool_items
3244
- latest_lineage_id = version2_id
3245
- assert latest_lineage_id != version1_id
3246
- # Bundle coin spends key points to version 1 but the lineage is latest (v2)
3247
- latest_singleton_lineage1 = mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
3248
- assert latest_singleton_lineage1 is not None
3249
- assert latest_singleton_lineage1.coin_id == latest_lineage_id
3250
- # Both the bundle coin spends key and the lineage point to latest (v2)
3251
- latest_singleton_lineage2 = mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
3252
- assert latest_singleton_lineage2 is not None
3253
- assert latest_singleton_lineage2.coin_id == latest_lineage_id
3254
- # Let's update the lineage with a new version of the singleton
3255
- new_latest_lineage = Coin(version2_id, singleton_ph, singleton_spend2.coin.amount)
3256
- new_latest_lineage_id = new_latest_lineage.name()
3257
- coins.update_lineage(singleton_ph, new_latest_lineage)
3258
- await advance_mempool(mempool_manager, [version1_id, version2_id], use_optimization=True)
3259
- # Both items should get updated with the latest lineage
3260
- new_mi1 = mempool_manager.get_mempool_item(mi1.spend_bundle_name)
3261
- assert new_mi1 is not None
3262
- latest_singleton_lineage1 = new_mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
3263
- assert latest_singleton_lineage1 is not None
3264
- assert latest_singleton_lineage1.coin_id == new_latest_lineage_id
3265
- new_mi2 = mempool_manager.get_mempool_item(mi2.spend_bundle_name)
3266
- assert new_mi2 is not None
3267
- latest_singleton_lineage2 = new_mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
3268
- assert latest_singleton_lineage2 is not None
3269
- assert latest_singleton_lineage2.coin_id == new_latest_lineage_id
3351
+ async with setup_mempool(coins) as mempool_manager:
3352
+ mempool_items: list[MempoolItem] = []
3353
+ for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
3354
+ sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
3355
+ sb_name = sb.name()
3356
+ await mempool_manager.add_spend_bundle(
3357
+ sb,
3358
+ make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
3359
+ sb_name,
3360
+ uint32(1),
3361
+ )
3362
+ mi = mempool_manager.get_mempool_item(sb_name)
3363
+ assert mi is not None
3364
+ mempool_items.append(mi)
3365
+ [mi1, mi2] = mempool_items
3366
+ latest_lineage_id = version2_id
3367
+ assert latest_lineage_id != version1_id
3368
+ # Bundle coin spends key points to version 1 but the lineage is latest (v2)
3369
+ latest_singleton_lineage1 = mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
3370
+ assert latest_singleton_lineage1 is not None
3371
+ assert latest_singleton_lineage1.coin_id == latest_lineage_id
3372
+ # Both the bundle coin spends key and the lineage point to latest (v2)
3373
+ latest_singleton_lineage2 = mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
3374
+ assert latest_singleton_lineage2 is not None
3375
+ assert latest_singleton_lineage2.coin_id == latest_lineage_id
3376
+ # Let's update the lineage with a new version of the singleton
3377
+ new_latest_lineage = Coin(version2_id, singleton_ph, singleton_spend2.coin.amount)
3378
+ new_latest_lineage_id = new_latest_lineage.name()
3379
+ coins.update_lineage(singleton_ph, new_latest_lineage)
3380
+ await advance_mempool(mempool_manager, [version1_id, version2_id], use_optimization=True)
3381
+ # Both items should get updated with the latest lineage
3382
+ new_mi1 = mempool_manager.get_mempool_item(mi1.spend_bundle_name)
3383
+ assert new_mi1 is not None
3384
+ latest_singleton_lineage1 = new_mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
3385
+ assert latest_singleton_lineage1 is not None
3386
+ assert latest_singleton_lineage1.coin_id == new_latest_lineage_id
3387
+ new_mi2 = mempool_manager.get_mempool_item(mi2.spend_bundle_name)
3388
+ assert new_mi2 is not None
3389
+ latest_singleton_lineage2 = new_mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
3390
+ assert latest_singleton_lineage2 is not None
3391
+ assert latest_singleton_lineage2.coin_id == new_latest_lineage_id
3270
3392
 
3271
3393
 
3272
3394
  @pytest.mark.anyio
@@ -3284,32 +3406,32 @@ async def test_new_peak_txs_added(condition_and_error: tuple[ConditionOpcode, Er
3284
3406
  time-lock allows them to be reconsidered.
3285
3407
  """
3286
3408
  coins = TestCoins([TEST_COIN], {})
3287
- mempool_manager = await setup_mempool(coins)
3288
- # Add an item that should go to the pending cache
3289
- assert mempool_manager.peak is not None
3290
- condition_height = mempool_manager.peak.height + 1
3291
- condition, expected_error = condition_and_error
3292
- _, sb_name, result = await generate_and_add_spendbundle(mempool_manager, [[condition, condition_height]])
3293
- _, status, error = result
3294
- assert status == MempoolInclusionStatus.PENDING
3295
- assert error == expected_error
3296
- # Advance the mempool beyond the asserted height to retry the test item
3297
- if optimized_path:
3298
- spent_coins: Optional[list[bytes32]] = []
3409
+ async with setup_mempool(coins) as mempool_manager:
3410
+ # Add an item that should go to the pending cache
3411
+ assert mempool_manager.peak is not None
3412
+ condition_height = mempool_manager.peak.height + 1
3413
+ condition, expected_error = condition_and_error
3414
+ _, sb_name, result = await generate_and_add_spendbundle(mempool_manager, [[condition, condition_height]])
3415
+ _, status, error = result
3416
+ assert status == MempoolInclusionStatus.PENDING
3417
+ assert error == expected_error
3418
+ # Advance the mempool beyond the asserted height to retry the test item
3419
+ if optimized_path:
3420
+ spent_coins: list[bytes32] | None = []
3421
+ new_peak_info = await mempool_manager.new_peak(
3422
+ create_test_block_record(height=uint32(condition_height)), spent_coins
3423
+ )
3424
+ # We're not there yet (needs to be higher, not equal)
3425
+ assert new_peak_info.spend_bundle_ids == []
3426
+ assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is None
3427
+ else:
3428
+ spent_coins = None
3299
3429
  new_peak_info = await mempool_manager.new_peak(
3300
- create_test_block_record(height=uint32(condition_height)), spent_coins
3430
+ create_test_block_record(height=uint32(condition_height + 1)), spent_coins
3301
3431
  )
3302
- # We're not there yet (needs to be higher, not equal)
3303
- assert new_peak_info.spend_bundle_ids == []
3304
- assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is None
3305
- else:
3306
- spent_coins = None
3307
- new_peak_info = await mempool_manager.new_peak(
3308
- create_test_block_record(height=uint32(condition_height + 1)), spent_coins
3309
- )
3310
- # The item gets retried successfully now
3311
- assert new_peak_info.spend_bundle_ids == [sb_name]
3312
- assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is not None
3432
+ # The item gets retried successfully now
3433
+ assert new_peak_info.spend_bundle_ids == [sb_name]
3434
+ assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is not None
3313
3435
 
3314
3436
 
3315
3437
  @pytest.mark.anyio
@@ -3318,13 +3440,15 @@ async def test_mempool_item_to_spend_bundle() -> None:
3318
3440
  Tests that we can properly go back to a `SpendBundle` from a `MempoolItem`.
3319
3441
  """
3320
3442
  coins = [Coin(bytes32.random(), IDENTITY_PUZZLE_HASH, uint64(i + 1)) for i in range(random.randint(42, 1337))]
3321
- mempool_manager = await setup_mempool(TestCoins(coins, {}))
3322
- random_sample = random.sample(coins, 42)
3323
- sb = SpendBundle([CoinSpend(c, IDENTITY_PUZZLE, SerializedProgram.to(None)) for c in random_sample], G2Element())
3324
- sb_name = sb.name()
3325
- await add_spendbundle(mempool_manager, sb, sb_name)
3326
- mi = mempool_manager.get_mempool_item(sb_name)
3327
- assert mi is not None
3328
- result = mi.to_spend_bundle()
3329
- assert result == sb
3330
- assert result.name() == sb_name
3443
+ async with setup_mempool(TestCoins(coins, {})) as mempool_manager:
3444
+ random_sample = random.sample(coins, 42)
3445
+ sb = SpendBundle(
3446
+ [CoinSpend(c, IDENTITY_PUZZLE, SerializedProgram.to(None)) for c in random_sample], G2Element()
3447
+ )
3448
+ sb_name = sb.name()
3449
+ await add_spendbundle(mempool_manager, sb, sb_name)
3450
+ mi = mempool_manager.get_mempool_item(sb_name)
3451
+ assert mi is not None
3452
+ result = mi.to_spend_bundle()
3453
+ assert result == sb
3454
+ assert result.name() == sb_name