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