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