chia-blockchain 2.5.7rc4__py3-none-any.whl → 2.6.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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_blockchain_transactions.py +5 -2
- 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 +66 -31
- 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 +273 -70
- chia/_tests/core/full_node/test_hard_fork_utils.py +92 -0
- 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 +198 -30
- chia/_tests/core/mempool/test_mempool.py +54 -50
- 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 +988 -854
- 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 +9 -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 +63 -60
- 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 +17 -17
- 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 +16 -15
- 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 +374 -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 +19 -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 +50 -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 +239 -223
- chia/full_node/full_node_api.py +197 -152
- chia/full_node/full_node_rpc_api.py +34 -32
- chia/full_node/full_node_rpc_client.py +18 -19
- chia/full_node/full_node_store.py +45 -43
- chia/full_node/hard_fork_utils.py +44 -0
- 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 +120 -36
- 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 +4 -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 +24 -10
- 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 +142 -106
- 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.6.0rc2.dist-info}/METADATA +6 -5
- {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.6.0rc2.dist-info}/RECORD +510 -517
- 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.6.0rc2.dist-info}/WHEEL +0 -0
- {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.6.0rc2.dist-info}/entry_points.txt +0 -0
- {chia_blockchain-2.5.7rc4.dist-info → chia_blockchain-2.6.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,20 +3,23 @@ from __future__ import annotations
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
import logging
|
|
5
5
|
import random
|
|
6
|
-
from collections.abc import Awaitable, Collection, Sequence
|
|
7
|
-
from
|
|
6
|
+
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Collection, Sequence
|
|
7
|
+
from contextlib import asynccontextmanager
|
|
8
|
+
from typing import Any, ClassVar
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
from chia_rs import (
|
|
11
12
|
ELIGIBLE_FOR_DEDUP,
|
|
12
13
|
ELIGIBLE_FOR_FF,
|
|
13
14
|
AugSchemeMPL,
|
|
15
|
+
CoinRecord,
|
|
14
16
|
CoinSpend,
|
|
15
17
|
ConsensusConstants,
|
|
16
18
|
G2Element,
|
|
17
19
|
SpendBundle,
|
|
18
20
|
SpendBundleConditions,
|
|
19
21
|
SpendConditions,
|
|
22
|
+
check_time_locks,
|
|
20
23
|
get_conditions_from_spendbundle,
|
|
21
24
|
run_block_generator2,
|
|
22
25
|
)
|
|
@@ -25,9 +28,9 @@ from chia_rs.sized_ints import uint8, uint32, uint64
|
|
|
25
28
|
from chiabip158 import PyBIP158
|
|
26
29
|
|
|
27
30
|
from chia._tests.conftest import ConsensusMode
|
|
31
|
+
from chia._tests.connection_utils import add_dummy_connection, connect_and_get_peer
|
|
28
32
|
from chia._tests.util.misc import Marks, datacases, invariant_check_mempool
|
|
29
33
|
from chia._tests.util.setup_nodes import OldSimulatorsAndWallets, setup_simulators_and_wallets
|
|
30
|
-
from chia.consensus.check_time_locks import check_time_locks
|
|
31
34
|
from chia.consensus.condition_costs import ConditionCost
|
|
32
35
|
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
33
36
|
from chia.full_node.eligible_coin_spends import (
|
|
@@ -53,6 +56,7 @@ from chia.full_node.mempool_manager import (
|
|
|
53
56
|
from chia.protocols import wallet_protocol
|
|
54
57
|
from chia.protocols.full_node_protocol import RequestBlock, RespondBlock
|
|
55
58
|
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
|
59
|
+
from chia.server.ws_connection import WSChiaConnection
|
|
56
60
|
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
57
61
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
58
62
|
from chia.simulator.wallet_tools import WalletTool
|
|
@@ -60,13 +64,11 @@ from chia.types.blockchain_format.coin import Coin
|
|
|
60
64
|
from chia.types.blockchain_format.program import DEFAULT_FLAGS, INFINITE_COST, Program
|
|
61
65
|
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
62
66
|
from chia.types.clvm_cost import CLVMCost
|
|
63
|
-
from chia.types.coin_record import CoinRecord
|
|
64
67
|
from chia.types.coin_spend import make_spend
|
|
65
68
|
from chia.types.condition_opcodes import ConditionOpcode
|
|
66
69
|
from chia.types.condition_with_args import ConditionWithArgs
|
|
67
70
|
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
68
71
|
from chia.types.mempool_item import BundleCoinSpend, MempoolItem, UnspentLineageInfo
|
|
69
|
-
from chia.types.peer_info import PeerInfo
|
|
70
72
|
from chia.util.casts import int_to_bytes
|
|
71
73
|
from chia.util.default_root import DEFAULT_ROOT_PATH
|
|
72
74
|
from chia.util.errors import Err, ValidationError
|
|
@@ -205,9 +207,9 @@ class TestBlockRecord:
|
|
|
205
207
|
|
|
206
208
|
header_hash: bytes32
|
|
207
209
|
height: uint32
|
|
208
|
-
timestamp:
|
|
210
|
+
timestamp: uint64 | None
|
|
209
211
|
prev_transaction_block_height: uint32
|
|
210
|
-
prev_transaction_block_hash:
|
|
212
|
+
prev_transaction_block_hash: bytes32 | None
|
|
211
213
|
|
|
212
214
|
@property
|
|
213
215
|
def is_transaction_block(self) -> bool:
|
|
@@ -219,7 +221,7 @@ async def zero_calls_get_coin_records(coin_ids: Collection[bytes32]) -> list[Coi
|
|
|
219
221
|
return []
|
|
220
222
|
|
|
221
223
|
|
|
222
|
-
async def zero_calls_get_unspent_lineage_info_for_puzzle_hash(_puzzle_hash: bytes32) ->
|
|
224
|
+
async def zero_calls_get_unspent_lineage_info_for_puzzle_hash(_puzzle_hash: bytes32) -> UnspentLineageInfo | None:
|
|
223
225
|
assert False # pragma no cover
|
|
224
226
|
|
|
225
227
|
|
|
@@ -252,37 +254,53 @@ def create_test_block_record(*, height: uint32 = TEST_HEIGHT, timestamp: uint64
|
|
|
252
254
|
)
|
|
253
255
|
|
|
254
256
|
|
|
257
|
+
@asynccontextmanager
|
|
255
258
|
async def instantiate_mempool_manager(
|
|
256
259
|
get_coin_records: Callable[[Collection[bytes32]], Awaitable[list[CoinRecord]]],
|
|
257
260
|
*,
|
|
258
261
|
block_height: uint32 = TEST_HEIGHT,
|
|
259
262
|
block_timestamp: uint64 = TEST_TIMESTAMP,
|
|
260
263
|
constants: ConsensusConstants = DEFAULT_CONSTANTS,
|
|
261
|
-
max_tx_clvm_cost:
|
|
262
|
-
) -> MempoolManager:
|
|
263
|
-
|
|
264
|
+
max_tx_clvm_cost: uint64 | None = None,
|
|
265
|
+
) -> AsyncGenerator[MempoolManager, None]:
|
|
266
|
+
async with MempoolManager.managed(
|
|
264
267
|
get_coin_records,
|
|
265
268
|
zero_calls_get_unspent_lineage_info_for_puzzle_hash,
|
|
266
269
|
constants,
|
|
267
270
|
max_tx_clvm_cost=max_tx_clvm_cost,
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
) as mempool_manager:
|
|
272
|
+
test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
|
|
273
|
+
await mempool_manager.new_peak(test_block_record, None)
|
|
274
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
275
|
+
yield mempool_manager
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@pytest.fixture(name="zero_mempool_manager", scope="function")
|
|
279
|
+
async def zero_mempool_manager_fixture() -> AsyncIterator[MempoolManager]:
|
|
280
|
+
async with instantiate_mempool_manager(zero_calls_get_coin_records) as mempool_manager:
|
|
281
|
+
yield mempool_manager
|
|
273
282
|
|
|
274
283
|
|
|
284
|
+
@pytest.fixture(name="test_coins_mempool_manager", scope="function")
|
|
285
|
+
async def test_coins_mempool_manager_fixture() -> AsyncIterator[MempoolManager]:
|
|
286
|
+
async with instantiate_mempool_manager(get_coin_records_for_test_coins) as mempool_manager:
|
|
287
|
+
yield mempool_manager
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@asynccontextmanager
|
|
275
291
|
async def setup_mempool_with_coins(
|
|
276
292
|
*,
|
|
277
293
|
coin_amounts: list[int],
|
|
278
|
-
max_block_clvm_cost:
|
|
279
|
-
max_tx_clvm_cost:
|
|
280
|
-
mempool_block_buffer:
|
|
281
|
-
|
|
294
|
+
max_block_clvm_cost: int | None = None,
|
|
295
|
+
max_tx_clvm_cost: uint64 | None = None,
|
|
296
|
+
mempool_block_buffer: int | None = None,
|
|
297
|
+
puzzle_hash: bytes32 = IDENTITY_PUZZLE_HASH,
|
|
298
|
+
height: uint32 = TEST_HEIGHT,
|
|
299
|
+
) -> AsyncGenerator[tuple[MempoolManager, list[Coin]], None]:
|
|
282
300
|
coins = []
|
|
283
301
|
test_coin_records = {}
|
|
284
302
|
for amount in coin_amounts:
|
|
285
|
-
coin = Coin(
|
|
303
|
+
coin = Coin(bytes32.random(), puzzle_hash, uint64(amount))
|
|
286
304
|
coins.append(coin)
|
|
287
305
|
test_coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
288
306
|
|
|
@@ -299,30 +317,31 @@ async def setup_mempool_with_coins(
|
|
|
299
317
|
constants = constants.replace(MAX_BLOCK_COST_CLVM=uint64(max_block_clvm_cost + TEST_BLOCK_OVERHEAD))
|
|
300
318
|
if mempool_block_buffer is not None:
|
|
301
319
|
constants = constants.replace(MEMPOOL_BLOCK_BUFFER=uint8(mempool_block_buffer))
|
|
302
|
-
mempool_manager = await instantiate_mempool_manager(
|
|
303
|
-
get_coin_records, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
|
|
304
|
-
)
|
|
305
|
-
return (mempool_manager, coins)
|
|
306
320
|
|
|
321
|
+
async with instantiate_mempool_manager(
|
|
322
|
+
get_coin_records, block_height=height, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
|
|
323
|
+
) as mempool_manager:
|
|
324
|
+
yield (mempool_manager, coins)
|
|
307
325
|
|
|
308
|
-
|
|
326
|
+
|
|
327
|
+
CreateCoin = tuple[bytes32, int, bytes | None]
|
|
309
328
|
|
|
310
329
|
|
|
311
330
|
def make_test_conds(
|
|
312
331
|
*,
|
|
313
|
-
birth_height:
|
|
314
|
-
birth_seconds:
|
|
315
|
-
height_relative:
|
|
332
|
+
birth_height: int | None = None,
|
|
333
|
+
birth_seconds: int | None = None,
|
|
334
|
+
height_relative: int | None = None,
|
|
316
335
|
height_absolute: int = 0,
|
|
317
|
-
seconds_relative:
|
|
336
|
+
seconds_relative: int | None = None,
|
|
318
337
|
seconds_absolute: int = 0,
|
|
319
|
-
before_height_relative:
|
|
320
|
-
before_height_absolute:
|
|
321
|
-
before_seconds_relative:
|
|
322
|
-
before_seconds_absolute:
|
|
338
|
+
before_height_relative: int | None = None,
|
|
339
|
+
before_height_absolute: int | None = None,
|
|
340
|
+
before_seconds_relative: int | None = None,
|
|
341
|
+
before_seconds_absolute: int | None = None,
|
|
323
342
|
cost: int = 0,
|
|
324
|
-
spend_ids: Sequence[tuple[
|
|
325
|
-
created_coins:
|
|
343
|
+
spend_ids: Sequence[tuple[bytes32 | Coin, int]] = [(TEST_COIN_ID, 0)],
|
|
344
|
+
created_coins: list[list[CreateCoin]] | None = None,
|
|
326
345
|
) -> SpendBundleConditions:
|
|
327
346
|
if created_coins is None:
|
|
328
347
|
created_coins = []
|
|
@@ -432,21 +451,23 @@ class TestCheckTimeLocks:
|
|
|
432
451
|
def test_conditions(
|
|
433
452
|
self,
|
|
434
453
|
conds: SpendBundleConditions,
|
|
435
|
-
expected:
|
|
454
|
+
expected: Err | None,
|
|
436
455
|
) -> None:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
self.PREV_BLOCK_TIMESTAMP,
|
|
443
|
-
)
|
|
444
|
-
== expected
|
|
456
|
+
res: int | None = check_time_locks(
|
|
457
|
+
dict(self.REMOVALS),
|
|
458
|
+
conds,
|
|
459
|
+
self.PREV_BLOCK_HEIGHT,
|
|
460
|
+
self.PREV_BLOCK_TIMESTAMP,
|
|
445
461
|
)
|
|
462
|
+
e: Err | None = None
|
|
463
|
+
if res is not None:
|
|
464
|
+
# TODO: remove when Rust errors and Python Errors are the same
|
|
465
|
+
e = Err(res)
|
|
466
|
+
assert e == expected
|
|
446
467
|
|
|
447
468
|
|
|
448
469
|
def expect(
|
|
449
|
-
*, height: int = 0, seconds: int = 0, before_height:
|
|
470
|
+
*, height: int = 0, seconds: int = 0, before_height: int | None = None, before_seconds: int | None = None
|
|
450
471
|
) -> TimelockConditions:
|
|
451
472
|
ret = TimelockConditions(uint32(height), uint64(seconds))
|
|
452
473
|
if before_height is not None:
|
|
@@ -538,7 +559,7 @@ def spend_bundle_from_conditions(
|
|
|
538
559
|
|
|
539
560
|
async def add_spendbundle(
|
|
540
561
|
mempool_manager: MempoolManager, sb: SpendBundle, sb_name: bytes32
|
|
541
|
-
) -> tuple[
|
|
562
|
+
) -> tuple[uint64 | None, MempoolInclusionStatus, Err | None]:
|
|
542
563
|
sbc = await mempool_manager.pre_validate_spendbundle(sb, sb_name)
|
|
543
564
|
ret = await mempool_manager.add_spend_bundle(sb, sbc, sb_name, TEST_HEIGHT)
|
|
544
565
|
invariant_check_mempool(mempool_manager.mempool)
|
|
@@ -550,7 +571,7 @@ async def generate_and_add_spendbundle(
|
|
|
550
571
|
conditions: list[list[Any]],
|
|
551
572
|
coin: Coin = TEST_COIN,
|
|
552
573
|
aggsig: G2Element = G2Element(),
|
|
553
|
-
) -> tuple[SpendBundle, bytes32, tuple[
|
|
574
|
+
) -> tuple[SpendBundle, bytes32, tuple[uint64 | None, MempoolInclusionStatus, Err | None]]:
|
|
554
575
|
sb = spend_bundle_from_conditions(conditions, coin, aggsig)
|
|
555
576
|
sb_name = sb.name()
|
|
556
577
|
result = await add_spendbundle(mempool_manager, sb, sb_name)
|
|
@@ -607,98 +628,89 @@ def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem:
|
|
|
607
628
|
|
|
608
629
|
|
|
609
630
|
@pytest.mark.anyio
|
|
610
|
-
async def test_empty_spend_bundle() -> None:
|
|
611
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
631
|
+
async def test_empty_spend_bundle(zero_mempool_manager: MempoolManager) -> None:
|
|
612
632
|
sb = SpendBundle([], G2Element())
|
|
613
633
|
with pytest.raises(ValidationError, match="INVALID_SPEND_BUNDLE"):
|
|
614
|
-
await
|
|
634
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
615
635
|
|
|
616
636
|
|
|
617
637
|
@pytest.mark.anyio
|
|
618
|
-
async def test_negative_addition_amount() -> None:
|
|
619
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
638
|
+
async def test_negative_addition_amount(zero_mempool_manager: MempoolManager) -> None:
|
|
620
639
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, -1]]
|
|
621
640
|
sb = spend_bundle_from_conditions(conditions)
|
|
622
641
|
with pytest.raises(ValidationError, match="COIN_AMOUNT_NEGATIVE"):
|
|
623
|
-
await
|
|
642
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
624
643
|
|
|
625
644
|
|
|
626
645
|
@pytest.mark.anyio
|
|
627
|
-
async def test_valid_addition_amount() -> None:
|
|
628
|
-
|
|
629
|
-
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
|
|
646
|
+
async def test_valid_addition_amount(zero_mempool_manager: MempoolManager) -> None:
|
|
647
|
+
max_amount = zero_mempool_manager.constants.MAX_COIN_AMOUNT
|
|
630
648
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount]]
|
|
631
649
|
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, max_amount)
|
|
632
650
|
sb = spend_bundle_from_conditions(conditions, coin)
|
|
633
651
|
# ensure this does not throw
|
|
634
|
-
_ = await
|
|
652
|
+
_ = await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
635
653
|
|
|
636
654
|
|
|
637
655
|
@pytest.mark.anyio
|
|
638
|
-
async def test_too_big_addition_amount() -> None:
|
|
639
|
-
|
|
640
|
-
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
|
|
656
|
+
async def test_too_big_addition_amount(zero_mempool_manager: MempoolManager) -> None:
|
|
657
|
+
max_amount = zero_mempool_manager.constants.MAX_COIN_AMOUNT
|
|
641
658
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount + 1]]
|
|
642
659
|
sb = spend_bundle_from_conditions(conditions)
|
|
643
660
|
with pytest.raises(ValidationError, match="COIN_AMOUNT_EXCEEDS_MAXIMUM"):
|
|
644
|
-
await
|
|
661
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
645
662
|
|
|
646
663
|
|
|
647
664
|
@pytest.mark.anyio
|
|
648
|
-
async def test_duplicate_output() -> None:
|
|
649
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
665
|
+
async def test_duplicate_output(zero_mempool_manager: MempoolManager) -> None:
|
|
650
666
|
conditions = [
|
|
651
667
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
652
668
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
653
669
|
]
|
|
654
670
|
sb = spend_bundle_from_conditions(conditions)
|
|
655
671
|
with pytest.raises(ValidationError, match="DUPLICATE_OUTPUT"):
|
|
656
|
-
await
|
|
672
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
657
673
|
|
|
658
674
|
|
|
659
675
|
@pytest.mark.anyio
|
|
660
|
-
async def test_block_cost_exceeds_max() -> None:
|
|
661
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
676
|
+
async def test_block_cost_exceeds_max(zero_mempool_manager: MempoolManager) -> None:
|
|
662
677
|
conditions = []
|
|
663
678
|
for i in range(2400):
|
|
664
679
|
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i])
|
|
665
680
|
sb = spend_bundle_from_conditions(conditions)
|
|
666
681
|
with pytest.raises(ValidationError, match="BLOCK_COST_EXCEEDS_MAX"):
|
|
667
|
-
await
|
|
682
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
668
683
|
|
|
669
684
|
|
|
670
685
|
@pytest.mark.anyio
|
|
671
|
-
async def test_double_spend_prevalidation() -> None:
|
|
672
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
686
|
+
async def test_double_spend_prevalidation(zero_mempool_manager: MempoolManager) -> None:
|
|
673
687
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
674
688
|
sb = spend_bundle_from_conditions(conditions)
|
|
675
689
|
sb_twice = SpendBundle.aggregate([sb, sb])
|
|
676
690
|
with pytest.raises(ValidationError, match="DOUBLE_SPEND"):
|
|
677
|
-
await
|
|
691
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb_twice)
|
|
678
692
|
|
|
679
693
|
|
|
680
694
|
@pytest.mark.anyio
|
|
681
|
-
async def test_minting_coin() -> None:
|
|
682
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
695
|
+
async def test_minting_coin(zero_mempool_manager: MempoolManager) -> None:
|
|
683
696
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT]]
|
|
684
697
|
sb = spend_bundle_from_conditions(conditions)
|
|
685
|
-
_ = await
|
|
698
|
+
_ = await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
686
699
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT + 1]]
|
|
687
700
|
sb = spend_bundle_from_conditions(conditions)
|
|
688
701
|
with pytest.raises(ValidationError, match="MINTING_COIN"):
|
|
689
|
-
await
|
|
702
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
690
703
|
|
|
691
704
|
|
|
692
705
|
@pytest.mark.anyio
|
|
693
|
-
async def test_reserve_fee_condition() -> None:
|
|
694
|
-
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
706
|
+
async def test_reserve_fee_condition(zero_mempool_manager: MempoolManager) -> None:
|
|
695
707
|
conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT]]
|
|
696
708
|
sb = spend_bundle_from_conditions(conditions)
|
|
697
|
-
_ = await
|
|
709
|
+
_ = await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
698
710
|
conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT + 1]]
|
|
699
711
|
sb = spend_bundle_from_conditions(conditions)
|
|
700
712
|
with pytest.raises(ValidationError, match="RESERVE_FEE_CONDITION_FAILED"):
|
|
701
|
-
await
|
|
713
|
+
await zero_mempool_manager.pre_validate_spendbundle(sb)
|
|
702
714
|
|
|
703
715
|
|
|
704
716
|
@pytest.mark.anyio
|
|
@@ -706,15 +718,15 @@ async def test_unknown_unspent() -> None:
|
|
|
706
718
|
async def get_coin_records(_: Collection[bytes32]) -> list[CoinRecord]:
|
|
707
719
|
return []
|
|
708
720
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
721
|
+
async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
|
|
722
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
723
|
+
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
|
|
724
|
+
assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
|
|
713
725
|
|
|
714
726
|
|
|
715
727
|
@pytest.mark.anyio
|
|
716
|
-
async def test_same_sb_twice_with_eligible_coin() -> None:
|
|
717
|
-
mempool_manager =
|
|
728
|
+
async def test_same_sb_twice_with_eligible_coin(test_coins_mempool_manager: MempoolManager) -> None:
|
|
729
|
+
mempool_manager = test_coins_mempool_manager
|
|
718
730
|
sb1_conditions = [
|
|
719
731
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
720
732
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
@@ -740,8 +752,10 @@ async def test_same_sb_twice_with_eligible_coin() -> None:
|
|
|
740
752
|
|
|
741
753
|
|
|
742
754
|
@pytest.mark.anyio
|
|
743
|
-
async def test_sb_twice_with_eligible_coin_and_different_spends_order(
|
|
744
|
-
|
|
755
|
+
async def test_sb_twice_with_eligible_coin_and_different_spends_order(
|
|
756
|
+
test_coins_mempool_manager: MempoolManager,
|
|
757
|
+
) -> None:
|
|
758
|
+
mempool_manager = test_coins_mempool_manager
|
|
745
759
|
sb1_conditions = [
|
|
746
760
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
747
761
|
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
@@ -838,29 +852,28 @@ async def test_ephemeral_timelock(
|
|
|
838
852
|
opcode: ConditionOpcode,
|
|
839
853
|
lock_value: int,
|
|
840
854
|
expected_status: MempoolInclusionStatus,
|
|
841
|
-
expected_error:
|
|
855
|
+
expected_error: Err | None,
|
|
842
856
|
) -> None:
|
|
843
|
-
|
|
857
|
+
async with instantiate_mempool_manager(
|
|
844
858
|
get_coin_records=get_coin_records_for_test_coins,
|
|
845
859
|
block_height=uint32(5),
|
|
846
860
|
block_timestamp=uint64(10050),
|
|
847
861
|
constants=DEFAULT_CONSTANTS,
|
|
848
|
-
)
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
assert expected_error == e.code
|
|
862
|
+
) as mempool_manager:
|
|
863
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
864
|
+
created_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))
|
|
865
|
+
sb1 = spend_bundle_from_conditions(conditions)
|
|
866
|
+
sb2 = spend_bundle_from_conditions([[opcode, lock_value]], created_coin)
|
|
867
|
+
# sb spends TEST_COIN and creates created_coin which gets spent too
|
|
868
|
+
sb = SpendBundle.aggregate([sb1, sb2])
|
|
869
|
+
# We shouldn't have a record of this ephemeral coin
|
|
870
|
+
assert await get_coin_records_for_test_coins([created_coin.name()]) == []
|
|
871
|
+
try:
|
|
872
|
+
_, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
873
|
+
assert (status, error) == (expected_status, expected_error)
|
|
874
|
+
except ValidationError as e:
|
|
875
|
+
assert expected_status == mis.FAILED
|
|
876
|
+
assert expected_error == e.code
|
|
864
877
|
|
|
865
878
|
|
|
866
879
|
def test_optional_min() -> None:
|
|
@@ -877,7 +890,7 @@ def test_optional_max() -> None:
|
|
|
877
890
|
assert optional_max(uint32(123), uint32(234)) == uint32(234)
|
|
878
891
|
|
|
879
892
|
|
|
880
|
-
def mk_coin_spend(coin: Coin, solution:
|
|
893
|
+
def mk_coin_spend(coin: Coin, solution: str | None = None) -> CoinSpend:
|
|
881
894
|
return make_spend(
|
|
882
895
|
coin,
|
|
883
896
|
SerializedProgram.to(None),
|
|
@@ -904,10 +917,10 @@ def mk_item(
|
|
|
904
917
|
*,
|
|
905
918
|
cost: int = 1,
|
|
906
919
|
fee: int = 0,
|
|
907
|
-
assert_height:
|
|
908
|
-
assert_before_height:
|
|
909
|
-
assert_before_seconds:
|
|
910
|
-
solution:
|
|
920
|
+
assert_height: int | None = None,
|
|
921
|
+
assert_before_height: int | None = None,
|
|
922
|
+
assert_before_seconds: int | None = None,
|
|
923
|
+
solution: str | None = None,
|
|
911
924
|
flags: list[int] = [],
|
|
912
925
|
) -> MempoolItem:
|
|
913
926
|
# we don't actually care about the puzzle and solutions for the purpose of
|
|
@@ -1126,8 +1139,8 @@ def test_can_replace(existing_items: list[MempoolItem], new_item: MempoolItem, e
|
|
|
1126
1139
|
|
|
1127
1140
|
|
|
1128
1141
|
@pytest.mark.anyio
|
|
1129
|
-
async def test_get_items_not_in_filter() -> None:
|
|
1130
|
-
mempool_manager =
|
|
1142
|
+
async def test_get_items_not_in_filter(test_coins_mempool_manager: MempoolManager) -> None:
|
|
1143
|
+
mempool_manager = test_coins_mempool_manager
|
|
1131
1144
|
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
1132
1145
|
sb1, sb1_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions)
|
|
1133
1146
|
conditions2 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
|
|
@@ -1138,7 +1151,7 @@ async def test_get_items_not_in_filter() -> None:
|
|
|
1138
1151
|
# Don't filter anything
|
|
1139
1152
|
empty_filter = PyBIP158([])
|
|
1140
1153
|
result = mempool_manager.get_items_not_in_filter(empty_filter)
|
|
1141
|
-
assert result == [sb3, sb2, sb1]
|
|
1154
|
+
assert [item.to_spend_bundle() for item in result] == [sb3, sb2, sb1]
|
|
1142
1155
|
|
|
1143
1156
|
# Filter everything
|
|
1144
1157
|
full_filter = PyBIP158([bytearray(sb1_name), bytearray(sb2_name), bytearray(sb3_name)])
|
|
@@ -1158,16 +1171,16 @@ async def test_get_items_not_in_filter() -> None:
|
|
|
1158
1171
|
|
|
1159
1172
|
# With a limit of one, sb2 has the highest FPC
|
|
1160
1173
|
result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=1)
|
|
1161
|
-
assert result == [sb2]
|
|
1174
|
+
assert [item.to_spend_bundle() for item in result] == [sb2]
|
|
1162
1175
|
|
|
1163
1176
|
# With a higher limit, all bundles aside from sb3 get included
|
|
1164
1177
|
result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=5)
|
|
1165
|
-
assert result == [sb2, sb1]
|
|
1178
|
+
assert [item.to_spend_bundle() for item in result] == [sb2, sb1]
|
|
1166
1179
|
|
|
1167
1180
|
# Filter two of the spend bundles
|
|
1168
1181
|
sb2_and_3_filter = PyBIP158([bytearray(sb2_name), bytearray(sb3_name)])
|
|
1169
1182
|
result = mempool_manager.get_items_not_in_filter(sb2_and_3_filter)
|
|
1170
|
-
assert result == [sb1]
|
|
1183
|
+
assert [item.to_spend_bundle() for item in result] == [sb1]
|
|
1171
1184
|
|
|
1172
1185
|
|
|
1173
1186
|
@pytest.mark.anyio
|
|
@@ -1182,29 +1195,29 @@ async def test_total_mempool_fees() -> None:
|
|
|
1182
1195
|
ret.append(r)
|
|
1183
1196
|
return ret
|
|
1184
1197
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1198
|
+
async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
|
|
1199
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
1200
|
+
|
|
1201
|
+
# the limit of total fees in the mempool is 2^63
|
|
1202
|
+
# the limit per mempool item is 2^50, that lets us add 8192 items with the
|
|
1203
|
+
# maximum amount of fee before reaching the total mempool limit
|
|
1204
|
+
amount = uint64(2**50)
|
|
1205
|
+
total_fee = 0
|
|
1206
|
+
for i in range(8192):
|
|
1207
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
|
|
1208
|
+
coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
1209
|
+
amount = uint64(amount - 1)
|
|
1210
|
+
# the fee is 1 less than the amount because we create a coin of 1 mojo
|
|
1211
|
+
total_fee += amount
|
|
1212
|
+
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
|
|
1213
|
+
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
1214
|
+
assert mempool_manager.mempool.total_mempool_fees() == total_fee
|
|
1187
1215
|
|
|
1188
|
-
# the limit of total fees in the mempool is 2^63
|
|
1189
|
-
# the limit per mempool item is 2^50, that lets us add 8192 items with the
|
|
1190
|
-
# maximum amount of fee before reaching the total mempool limit
|
|
1191
|
-
amount = uint64(2**50)
|
|
1192
|
-
total_fee = 0
|
|
1193
|
-
for i in range(8192):
|
|
1194
1216
|
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
|
|
1195
1217
|
coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
1196
|
-
amount = uint64(amount - 1)
|
|
1197
|
-
# the fee is 1 less than the amount because we create a coin of 1 mojo
|
|
1198
|
-
total_fee += amount
|
|
1199
1218
|
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
|
|
1200
|
-
assert result[1] == MempoolInclusionStatus.
|
|
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
|
|
1219
|
+
assert result[1] == MempoolInclusionStatus.FAILED
|
|
1220
|
+
assert result[2] == Err.INVALID_BLOCK_FEE_AMOUNT
|
|
1208
1221
|
|
|
1209
1222
|
|
|
1210
1223
|
@pytest.mark.parametrize("reverse_tx_order", [True, False])
|
|
@@ -1230,17 +1243,17 @@ async def test_create_bundle_from_mempool(reverse_tx_order: bool) -> None:
|
|
|
1230
1243
|
result = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
1231
1244
|
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
1232
1245
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1246
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(2000000000, 2000002200))) as (mempool_manager, coins):
|
|
1247
|
+
high_rate_spends = await make_coin_spends(coins[0:2200])
|
|
1248
|
+
low_rate_spends = await make_coin_spends(coins[2200:2400], high_fees=False)
|
|
1249
|
+
spends = low_rate_spends + high_rate_spends if reverse_tx_order else high_rate_spends + low_rate_spends
|
|
1250
|
+
await send_spends_to_mempool(spends)
|
|
1251
|
+
assert mempool_manager.peak is not None
|
|
1252
|
+
result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
|
|
1253
|
+
assert result is not None
|
|
1254
|
+
# Make sure we filled the block with only high rate spends
|
|
1255
|
+
assert len([s for s in high_rate_spends if s in result[0].coin_spends]) == len(result[0].coin_spends)
|
|
1256
|
+
assert len([s for s in low_rate_spends if s in result[0].coin_spends]) == 0
|
|
1244
1257
|
|
|
1245
1258
|
|
|
1246
1259
|
@pytest.mark.parametrize("num_skipped_items", [PRIORITY_TX_THRESHOLD, MAX_SKIPPED_ITEMS])
|
|
@@ -1257,105 +1270,105 @@ async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, ca
|
|
|
1257
1270
|
|
|
1258
1271
|
MAX_BLOCK_CLVM_COST = 550_000_000
|
|
1259
1272
|
|
|
1260
|
-
|
|
1273
|
+
async with setup_mempool_with_coins(
|
|
1261
1274
|
coin_amounts=list(range(1_000_000_000, 1_000_000_030)),
|
|
1262
1275
|
max_block_clvm_cost=MAX_BLOCK_CLVM_COST,
|
|
1263
1276
|
max_tx_clvm_cost=uint64(MAX_BLOCK_CLVM_COST),
|
|
1264
1277
|
mempool_block_buffer=20,
|
|
1265
|
-
)
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1278
|
+
) as (mempool_manager, coins):
|
|
1279
|
+
|
|
1280
|
+
async def make_and_send_big_cost_sb(coin: Coin) -> None:
|
|
1281
|
+
"""
|
|
1282
|
+
Creates a spend bundle with a big enough cost that gets it close to the
|
|
1283
|
+
maximum block clvm cost limit.
|
|
1284
|
+
"""
|
|
1285
|
+
conditions = []
|
|
1286
|
+
sk = AugSchemeMPL.key_gen(b"7" * 32)
|
|
1287
|
+
g1 = sk.get_g1()
|
|
1288
|
+
sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
|
|
1289
|
+
aggsig = G2Element()
|
|
1290
|
+
# Let's get as close to `MAX_BLOCK_CLVM_COST` (550_000_000) as possible.
|
|
1291
|
+
# We start by accounting for execution cost
|
|
1292
|
+
spend_bundle_cost = 44
|
|
1293
|
+
# And then the created coin
|
|
1294
|
+
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - 10_000_000])
|
|
1295
|
+
TEST_CREATE_COIN_SPEND_BYTESIZE = 93
|
|
1296
|
+
TEST_CREATE_COIN_CONDITION_COST = (
|
|
1297
|
+
ConditionCost.CREATE_COIN.value + TEST_CREATE_COIN_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
|
|
1298
|
+
)
|
|
1299
|
+
spend_bundle_cost += TEST_CREATE_COIN_CONDITION_COST
|
|
1300
|
+
# We're using agg sig conditions to increase the spend bundle's cost
|
|
1301
|
+
# and reach our target cost.
|
|
1302
|
+
TEST_AGG_SIG_SPEND_BYTESIZE = 88
|
|
1303
|
+
TEST_AGGSIG_CONDITION_COST = (
|
|
1304
|
+
ConditionCost.AGG_SIG.value + TEST_AGG_SIG_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
|
|
1305
|
+
)
|
|
1306
|
+
while spend_bundle_cost + TEST_AGGSIG_CONDITION_COST < MAX_BLOCK_CLVM_COST:
|
|
1307
|
+
conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH])
|
|
1308
|
+
aggsig += sig
|
|
1309
|
+
spend_bundle_cost += TEST_AGGSIG_CONDITION_COST
|
|
1310
|
+
# We now have a spend bundle with a big enough cost that gets it close to the limit
|
|
1311
|
+
_, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coin, aggsig)
|
|
1312
|
+
cost, status, _ = res
|
|
1313
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1314
|
+
assert cost == spend_bundle_cost
|
|
1315
|
+
|
|
1316
|
+
# Create the spend bundles with a big enough cost that they get close to the limit
|
|
1317
|
+
for i in range(num_skipped_items):
|
|
1318
|
+
await make_and_send_big_cost_sb(coins[i])
|
|
1319
|
+
|
|
1320
|
+
# Create a spend bundle with a relatively smaller cost.
|
|
1321
|
+
# Combined with a big cost spend bundle, we'd exceed the maximum block clvm cost
|
|
1322
|
+
sb2_coin = coins[num_skipped_items]
|
|
1323
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, sb2_coin.amount - 200_000]]
|
|
1324
|
+
sb2, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, sb2_coin)
|
|
1325
|
+
assert res[1] == MempoolInclusionStatus.SUCCESS
|
|
1326
|
+
sb2_addition = Coin(sb2_coin.name(), IDENTITY_PUZZLE_HASH, uint64(sb2_coin.amount - 200_000))
|
|
1327
|
+
# Create 4 extra spend bundles with smaller FPC and smaller costs
|
|
1328
|
+
extra_sbs = []
|
|
1329
|
+
extra_additions = []
|
|
1330
|
+
sk = AugSchemeMPL.key_gen(b"8" * 32)
|
|
1274
1331
|
g1 = sk.get_g1()
|
|
1275
|
-
sig = AugSchemeMPL.sign(sk,
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1332
|
+
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
|
1333
|
+
for i in range(num_skipped_items + 1, num_skipped_items + 5):
|
|
1334
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coins[i].amount]]
|
|
1335
|
+
# Make the first of these without eligible coins
|
|
1336
|
+
if i == num_skipped_items + 1:
|
|
1337
|
+
conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"])
|
|
1338
|
+
aggsig = sig
|
|
1339
|
+
else:
|
|
1340
|
+
aggsig = G2Element()
|
|
1341
|
+
sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
|
|
1342
|
+
extra_sbs.append(sb)
|
|
1343
|
+
coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount))
|
|
1344
|
+
extra_additions.append(coin)
|
|
1345
|
+
assert res[1] == MempoolInclusionStatus.SUCCESS
|
|
1346
|
+
|
|
1347
|
+
assert mempool_manager.peak is not None
|
|
1348
|
+
caplog.set_level(logging.DEBUG)
|
|
1349
|
+
result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
|
|
1350
|
+
assert result is not None
|
|
1351
|
+
agg, additions = result
|
|
1352
|
+
skipped_due_to_eligible_coins = sum(
|
|
1353
|
+
1 for line in caplog.text.split("\n") if "Skipping transaction with dedup or FF spends" in line
|
|
1292
1354
|
)
|
|
1293
|
-
|
|
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
|
|
1355
|
+
if num_skipped_items == PRIORITY_TX_THRESHOLD:
|
|
1356
|
+
# We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
|
|
1357
|
+
# so the first from the extra 4 (the one without eligible coins) went in,
|
|
1358
|
+
# and the other 3 were skipped (they have eligible coins)
|
|
1359
|
+
assert skipped_due_to_eligible_coins == 3
|
|
1360
|
+
assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
|
|
1361
|
+
assert additions == [sb2_addition, extra_additions[0]]
|
|
1362
|
+
assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
|
|
1363
|
+
elif num_skipped_items == MAX_SKIPPED_ITEMS:
|
|
1364
|
+
# We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
|
|
1365
|
+
# we didn't process any of the extra items
|
|
1366
|
+
assert skipped_due_to_eligible_coins == 0
|
|
1367
|
+
assert agg == SpendBundle.aggregate([sb2])
|
|
1368
|
+
assert additions == [sb2_addition]
|
|
1369
|
+
assert agg.removals() == [sb2_coin]
|
|
1326
1370
|
else:
|
|
1327
|
-
|
|
1328
|
-
sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
|
|
1329
|
-
extra_sbs.append(sb)
|
|
1330
|
-
coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount))
|
|
1331
|
-
extra_additions.append(coin)
|
|
1332
|
-
assert res[1] == MempoolInclusionStatus.SUCCESS
|
|
1333
|
-
|
|
1334
|
-
assert mempool_manager.peak is not None
|
|
1335
|
-
caplog.set_level(logging.DEBUG)
|
|
1336
|
-
result = mempool_manager.create_bundle_from_mempool(mempool_manager.peak.header_hash)
|
|
1337
|
-
assert result is not None
|
|
1338
|
-
agg, additions = result
|
|
1339
|
-
skipped_due_to_eligible_coins = sum(
|
|
1340
|
-
1 for line in caplog.text.split("\n") if "Skipping transaction with dedup or FF spends" in line
|
|
1341
|
-
)
|
|
1342
|
-
if num_skipped_items == PRIORITY_TX_THRESHOLD:
|
|
1343
|
-
# We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
|
|
1344
|
-
# so the first from the extra 4 (the one without eligible coins) went in,
|
|
1345
|
-
# and the other 3 were skipped (they have eligible coins)
|
|
1346
|
-
assert skipped_due_to_eligible_coins == 3
|
|
1347
|
-
assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
|
|
1348
|
-
assert additions == [sb2_addition, extra_additions[0]]
|
|
1349
|
-
assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
|
|
1350
|
-
elif num_skipped_items == MAX_SKIPPED_ITEMS:
|
|
1351
|
-
# We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
|
|
1352
|
-
# we didn't process any of the extra items
|
|
1353
|
-
assert skipped_due_to_eligible_coins == 0
|
|
1354
|
-
assert agg == SpendBundle.aggregate([sb2])
|
|
1355
|
-
assert additions == [sb2_addition]
|
|
1356
|
-
assert agg.removals() == [sb2_coin]
|
|
1357
|
-
else:
|
|
1358
|
-
raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
|
|
1371
|
+
raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
|
|
1359
1372
|
|
|
1360
1373
|
|
|
1361
1374
|
@pytest.mark.parametrize(
|
|
@@ -1377,7 +1390,7 @@ async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, ca
|
|
|
1377
1390
|
)
|
|
1378
1391
|
@pytest.mark.anyio
|
|
1379
1392
|
async def test_assert_before_expiration(
|
|
1380
|
-
opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit:
|
|
1393
|
+
opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: int | None
|
|
1381
1394
|
) -> None:
|
|
1382
1395
|
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
1383
1396
|
all_coins = {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}
|
|
@@ -1388,41 +1401,40 @@ async def test_assert_before_expiration(
|
|
|
1388
1401
|
ret.append(r)
|
|
1389
1402
|
return ret
|
|
1390
1403
|
|
|
1391
|
-
|
|
1404
|
+
async with instantiate_mempool_manager(
|
|
1392
1405
|
get_coin_records,
|
|
1393
1406
|
block_height=uint32(10),
|
|
1394
1407
|
block_timestamp=uint64(10000),
|
|
1395
1408
|
constants=DEFAULT_CONSTANTS,
|
|
1396
|
-
)
|
|
1397
|
-
|
|
1398
|
-
|
|
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
|
|
1409
|
+
) as mempool_manager:
|
|
1410
|
+
bundle = spend_bundle_from_conditions(
|
|
1411
|
+
[
|
|
1412
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1413
|
+
[opcode, arg],
|
|
1414
|
+
],
|
|
1415
|
+
coin=TEST_COIN,
|
|
1416
|
+
)
|
|
1417
|
+
bundle_name = bundle.name()
|
|
1418
|
+
assert (await add_spendbundle(mempool_manager, bundle, bundle_name))[1] == mis.SUCCESS
|
|
1419
|
+
# make sure the spend was added correctly
|
|
1420
|
+
assert mempool_manager.get_spendbundle(bundle_name) == bundle
|
|
1421
|
+
|
|
1422
|
+
block_record = create_test_block_record(height=uint32(11), timestamp=uint64(10019))
|
|
1423
|
+
await mempool_manager.new_peak(block_record, None)
|
|
1424
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
1425
|
+
|
|
1426
|
+
still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
|
|
1427
|
+
assert still_in_pool != expect_eviction
|
|
1428
|
+
if still_in_pool:
|
|
1429
|
+
assert expect_limit is not None
|
|
1430
|
+
item = mempool_manager.get_mempool_item(bundle_name)
|
|
1431
|
+
assert item is not None
|
|
1432
|
+
if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
|
|
1433
|
+
assert item.assert_before_seconds == expect_limit
|
|
1434
|
+
elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
|
|
1435
|
+
assert item.assert_before_height == expect_limit
|
|
1436
|
+
else:
|
|
1437
|
+
assert False
|
|
1426
1438
|
|
|
1427
1439
|
|
|
1428
1440
|
def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = False) -> SpendBundle:
|
|
@@ -1439,7 +1451,7 @@ def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = Fa
|
|
|
1439
1451
|
async def send_spendbundle(
|
|
1440
1452
|
mempool_manager: MempoolManager,
|
|
1441
1453
|
sb: SpendBundle,
|
|
1442
|
-
expected_result: tuple[MempoolInclusionStatus,
|
|
1454
|
+
expected_result: tuple[MempoolInclusionStatus, Err | None] = (MempoolInclusionStatus.SUCCESS, None),
|
|
1443
1455
|
) -> None:
|
|
1444
1456
|
result = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
1445
1457
|
assert (result[1], result[2]) == expected_result
|
|
@@ -1450,7 +1462,7 @@ async def make_and_send_spendbundle(
|
|
|
1450
1462
|
coin: Coin,
|
|
1451
1463
|
*,
|
|
1452
1464
|
fee: int = 0,
|
|
1453
|
-
expected_result: tuple[MempoolInclusionStatus,
|
|
1465
|
+
expected_result: tuple[MempoolInclusionStatus, Err | None] = (MempoolInclusionStatus.SUCCESS, None),
|
|
1454
1466
|
) -> SpendBundle:
|
|
1455
1467
|
sb = make_test_spendbundle(coin, fee=fee)
|
|
1456
1468
|
await send_spendbundle(mempool_manager, sb, expected_result)
|
|
@@ -1467,125 +1479,125 @@ def assert_sb_not_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> N
|
|
|
1467
1479
|
|
|
1468
1480
|
@pytest.mark.anyio
|
|
1469
1481
|
async def test_insufficient_fee_increase() -> None:
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1482
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1483
|
+
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1484
|
+
sb1_2 = await make_and_send_spendbundle(
|
|
1485
|
+
mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1486
|
+
)
|
|
1487
|
+
# The old spendbundle must stay
|
|
1488
|
+
assert_sb_in_pool(mempool_manager, sb1_1)
|
|
1489
|
+
assert_sb_not_in_pool(mempool_manager, sb1_2)
|
|
1478
1490
|
|
|
1479
1491
|
|
|
1480
1492
|
@pytest.mark.anyio
|
|
1481
1493
|
async def test_sufficient_fee_increase() -> None:
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1494
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1495
|
+
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1496
|
+
sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1497
|
+
# sb1_1 gets replaced with sb1_2
|
|
1498
|
+
assert_sb_not_in_pool(mempool_manager, sb1_1)
|
|
1499
|
+
assert_sb_in_pool(mempool_manager, sb1_2)
|
|
1488
1500
|
|
|
1489
1501
|
|
|
1490
1502
|
@pytest.mark.anyio
|
|
1491
1503
|
async def test_superset() -> None:
|
|
1492
1504
|
# Aggregated spendbundle sb12 replaces sb1 since it spends a superset
|
|
1493
1505
|
# of coins spent in sb1
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1506
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1507
|
+
sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1508
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1509
|
+
sb12 = SpendBundle.aggregate([sb2, sb1])
|
|
1510
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1511
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1512
|
+
assert_sb_not_in_pool(mempool_manager, sb1)
|
|
1501
1513
|
|
|
1502
1514
|
|
|
1503
1515
|
@pytest.mark.anyio
|
|
1504
1516
|
async def test_superset_violation() -> None:
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1517
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1518
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1519
|
+
sb2 = make_test_spendbundle(coins[1])
|
|
1520
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1521
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1522
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1523
|
+
# sb23 must not replace existing sb12 as the former does not spend all
|
|
1524
|
+
# coins that are spent in the latter (specifically, the first coin)
|
|
1525
|
+
sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1526
|
+
sb23 = SpendBundle.aggregate([sb2, sb3])
|
|
1527
|
+
await send_spendbundle(
|
|
1528
|
+
mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1529
|
+
)
|
|
1530
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1531
|
+
assert_sb_not_in_pool(mempool_manager, sb23)
|
|
1520
1532
|
|
|
1521
1533
|
|
|
1522
1534
|
@pytest.mark.anyio
|
|
1523
1535
|
async def test_total_fpc_decrease() -> None:
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1536
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1537
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1538
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1539
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1540
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1541
|
+
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1542
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1543
|
+
assert_sb_in_pool(mempool_manager, sb3)
|
|
1544
|
+
# sb1234 should not be in pool as it decreases total fees per cost
|
|
1545
|
+
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1546
|
+
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
|
|
1547
|
+
await send_spendbundle(
|
|
1548
|
+
mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1549
|
+
)
|
|
1550
|
+
assert_sb_not_in_pool(mempool_manager, sb1234)
|
|
1539
1551
|
|
|
1540
1552
|
|
|
1541
1553
|
@pytest.mark.anyio
|
|
1542
1554
|
async def test_sufficient_total_fpc_increase() -> None:
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1555
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1556
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1557
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1558
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1559
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1560
|
+
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1561
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1562
|
+
assert_sb_in_pool(mempool_manager, sb3)
|
|
1563
|
+
# sb1234 has a higher fee per cost than its conflicts and should get
|
|
1564
|
+
# into the mempool
|
|
1565
|
+
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
|
|
1566
|
+
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
|
|
1567
|
+
await send_spendbundle(mempool_manager, sb1234)
|
|
1568
|
+
assert_sb_in_pool(mempool_manager, sb1234)
|
|
1569
|
+
assert_sb_not_in_pool(mempool_manager, sb12)
|
|
1570
|
+
assert_sb_not_in_pool(mempool_manager, sb3)
|
|
1559
1571
|
|
|
1560
1572
|
|
|
1561
1573
|
@pytest.mark.anyio
|
|
1562
1574
|
async def test_replace_with_extra_eligible_coin() -> None:
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1575
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1576
|
+
sb1234 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(4)])
|
|
1577
|
+
await send_spendbundle(mempool_manager, sb1234)
|
|
1578
|
+
assert_sb_in_pool(mempool_manager, sb1234)
|
|
1579
|
+
# Replace sb1234 with sb1234_2 which spends an eligible coin additionally
|
|
1580
|
+
eligible_sb = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE, eligible_spend=True)
|
|
1581
|
+
sb1234_2 = SpendBundle.aggregate([sb1234, eligible_sb])
|
|
1582
|
+
await send_spendbundle(mempool_manager, sb1234_2)
|
|
1583
|
+
assert_sb_not_in_pool(mempool_manager, sb1234)
|
|
1584
|
+
assert_sb_in_pool(mempool_manager, sb1234_2)
|
|
1573
1585
|
|
|
1574
1586
|
|
|
1575
1587
|
@pytest.mark.anyio
|
|
1576
1588
|
async def test_replacing_one_with_an_eligible_coin() -> None:
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010))) as (mempool_manager, coins):
|
|
1590
|
+
sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
|
|
1591
|
+
eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
|
|
1592
|
+
sb123e = SpendBundle.aggregate([sb123, eligible_sb])
|
|
1593
|
+
await send_spendbundle(mempool_manager, sb123e)
|
|
1594
|
+
assert_sb_in_pool(mempool_manager, sb123e)
|
|
1595
|
+
# Replace sb123e with sb123e4
|
|
1596
|
+
sb4 = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1597
|
+
sb123e4 = SpendBundle.aggregate([sb123e, sb4])
|
|
1598
|
+
await send_spendbundle(mempool_manager, sb123e4)
|
|
1599
|
+
assert_sb_not_in_pool(mempool_manager, sb123e)
|
|
1600
|
+
assert_sb_in_pool(mempool_manager, sb123e4)
|
|
1589
1601
|
|
|
1590
1602
|
|
|
1591
1603
|
def test_dedup_info_nothing_to_do() -> None:
|
|
@@ -1757,64 +1769,64 @@ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(ne
|
|
|
1757
1769
|
ret.append(r)
|
|
1758
1770
|
return ret
|
|
1759
1771
|
|
|
1760
|
-
|
|
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
|
-
|
|
1772
|
+
async with instantiate_mempool_manager(get_coin_records) as mempool_manager:
|
|
1773
|
+
# Create a bunch of mempool items that spend the coin in different ways
|
|
1774
|
+
# only the first one will be accepted
|
|
1775
|
+
for i in range(3):
|
|
1776
|
+
_, _, result = await generate_and_add_spendbundle(
|
|
1777
|
+
mempool_manager,
|
|
1778
|
+
[
|
|
1779
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount],
|
|
1780
|
+
[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, uint64(i)],
|
|
1781
|
+
],
|
|
1782
|
+
coin,
|
|
1783
|
+
)
|
|
1784
|
+
if i == 0:
|
|
1785
|
+
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
1786
|
+
else:
|
|
1787
|
+
assert result[1] == MempoolInclusionStatus.PENDING
|
|
1788
|
+
assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 1
|
|
1789
|
+
assert mempool_manager.mempool.size() == 1
|
|
1790
|
+
assert len(list(mempool_manager.mempool.items_by_feerate())) == 1
|
|
1791
|
+
# Setup a new peak where the incoming block has spent the coin
|
|
1792
|
+
# Mark this coin as spent
|
|
1793
|
+
test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
|
|
1794
|
+
block_record = create_test_block_record(height=new_height)
|
|
1795
|
+
await mempool_manager.new_peak(block_record, [coin_id])
|
|
1796
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
1797
|
+
# As the coin was a spend in all the mempool items we had, nothing should be left now
|
|
1798
|
+
assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 0
|
|
1799
|
+
assert mempool_manager.mempool.size() == 0
|
|
1800
|
+
assert len(list(mempool_manager.mempool.items_by_feerate())) == 0
|
|
1789
1801
|
|
|
1790
1802
|
|
|
1791
1803
|
@pytest.mark.anyio
|
|
1792
1804
|
async def test_bundle_coin_spends() -> None:
|
|
1793
1805
|
# This tests the construction of bundle_coin_spends map for mempool items
|
|
1794
1806
|
# We're creating sb123e with 4 coins, one of them being eligible
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1807
|
+
async with setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000005))) as (mempool_manager, coins):
|
|
1808
|
+
sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
|
|
1809
|
+
eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
|
|
1810
|
+
sb123e = SpendBundle.aggregate([sb123, eligible_sb])
|
|
1811
|
+
await send_spendbundle(mempool_manager, sb123e)
|
|
1812
|
+
mi123e = mempool_manager.get_mempool_item(sb123e.name())
|
|
1813
|
+
assert mi123e is not None
|
|
1814
|
+
execution_cost = 44
|
|
1815
|
+
for i in range(3):
|
|
1816
|
+
assert mi123e.bundle_coin_spends[coins[i].name()] == BundleCoinSpend(
|
|
1817
|
+
coin_spend=sb123.coin_spends[i],
|
|
1818
|
+
eligible_for_dedup=False,
|
|
1819
|
+
additions=[Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, coins[i].amount)],
|
|
1820
|
+
cost=uint64(ConditionCost.CREATE_COIN.value + ConditionCost.AGG_SIG.value + execution_cost),
|
|
1821
|
+
latest_singleton_lineage=None,
|
|
1822
|
+
)
|
|
1823
|
+
assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
|
|
1824
|
+
coin_spend=eligible_sb.coin_spends[0],
|
|
1825
|
+
eligible_for_dedup=True,
|
|
1826
|
+
additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
|
|
1827
|
+
cost=uint64(ConditionCost.CREATE_COIN.value + execution_cost),
|
|
1809
1828
|
latest_singleton_lineage=None,
|
|
1810
1829
|
)
|
|
1811
|
-
assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
|
|
1812
|
-
coin_spend=eligible_sb.coin_spends[0],
|
|
1813
|
-
eligible_for_dedup=True,
|
|
1814
|
-
additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
|
|
1815
|
-
cost=uint64(ConditionCost.CREATE_COIN.value + execution_cost),
|
|
1816
|
-
latest_singleton_lineage=None,
|
|
1817
|
-
)
|
|
1818
1830
|
|
|
1819
1831
|
|
|
1820
1832
|
@pytest.mark.anyio
|
|
@@ -1831,9 +1843,13 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1831
1843
|
}
|
|
1832
1844
|
|
|
1833
1845
|
async def send_to_mempool(
|
|
1834
|
-
full_node: FullNodeSimulator,
|
|
1846
|
+
full_node: FullNodeSimulator,
|
|
1847
|
+
wallet_peer: WSChiaConnection,
|
|
1848
|
+
spend_bundle: SpendBundle,
|
|
1849
|
+
*,
|
|
1850
|
+
expecting_conflict: bool = False,
|
|
1835
1851
|
) -> None:
|
|
1836
|
-
res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle))
|
|
1852
|
+
res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle), wallet_peer)
|
|
1837
1853
|
assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
|
|
1838
1854
|
res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
|
|
1839
1855
|
if expecting_conflict:
|
|
@@ -1861,7 +1877,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1861
1877
|
await wallet.generate_signed_transaction([uint64(200)] * len(phs), phs, action_scope)
|
|
1862
1878
|
[tx] = action_scope.side_effects.transactions
|
|
1863
1879
|
assert tx.spend_bundle is not None
|
|
1864
|
-
await send_to_mempool(full_node_api, tx.spend_bundle)
|
|
1880
|
+
await send_to_mempool(full_node_api, wallet_peer, tx.spend_bundle)
|
|
1865
1881
|
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1866
1882
|
coins = list(await wallet_node.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(1))
|
|
1867
1883
|
# Two blocks farmed plus 3 transactions
|
|
@@ -1870,7 +1886,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1870
1886
|
|
|
1871
1887
|
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
1872
1888
|
server = full_node_api.full_node.server
|
|
1873
|
-
await
|
|
1889
|
+
wallet_peer = await connect_and_get_peer(server, wallet_server, self_hostname)
|
|
1874
1890
|
wallet, coins, ph = await make_setup_and_coins(full_node_api, wallet_node)
|
|
1875
1891
|
|
|
1876
1892
|
# Make sure spending AB then BC would generate a conflict for the latter
|
|
@@ -1885,10 +1901,10 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1885
1901
|
assert tx_b.spend_bundle is not None
|
|
1886
1902
|
assert tx_c.spend_bundle is not None
|
|
1887
1903
|
ab_bundle = SpendBundle.aggregate([tx_a.spend_bundle, tx_b.spend_bundle])
|
|
1888
|
-
await send_to_mempool(full_node_api, ab_bundle)
|
|
1904
|
+
await send_to_mempool(full_node_api, wallet_peer, ab_bundle)
|
|
1889
1905
|
# BC should conflict here (on B)
|
|
1890
1906
|
bc_bundle = SpendBundle.aggregate([tx_b.spend_bundle, tx_c.spend_bundle])
|
|
1891
|
-
await send_to_mempool(full_node_api, bc_bundle, expecting_conflict=True)
|
|
1907
|
+
await send_to_mempool(full_node_api, wallet_peer, bc_bundle, expecting_conflict=True)
|
|
1892
1908
|
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1893
1909
|
|
|
1894
1910
|
# Make sure DE and EF would aggregate on E when E is eligible for deduplication
|
|
@@ -1902,7 +1918,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1902
1918
|
)
|
|
1903
1919
|
[tx] = action_scope.side_effects.transactions
|
|
1904
1920
|
assert tx.spend_bundle is not None
|
|
1905
|
-
await send_to_mempool(full_node_api, tx.spend_bundle)
|
|
1921
|
+
await send_to_mempool(full_node_api, wallet_peer, tx.spend_bundle)
|
|
1906
1922
|
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1907
1923
|
# Grab the coin we created and make an eligible coin out of it
|
|
1908
1924
|
coins_with_identity_ph = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
|
|
@@ -1910,7 +1926,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1910
1926
|
)
|
|
1911
1927
|
coin = coins_with_identity_ph[0].coin
|
|
1912
1928
|
sb = spend_bundle_from_conditions([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount]], coin)
|
|
1913
|
-
await send_to_mempool(full_node_api, sb)
|
|
1929
|
+
await send_to_mempool(full_node_api, wallet_peer, sb)
|
|
1914
1930
|
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1915
1931
|
# Grab the eligible coin to spend as E in DE and EF transactions
|
|
1916
1932
|
e_coin = (await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, IDENTITY_PUZZLE_HASH))[
|
|
@@ -1953,10 +1969,10 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1953
1969
|
# Send DE and EF combinations to the mempool
|
|
1954
1970
|
sb_de = SpendBundle.aggregate([tx_d.spend_bundle, sb_e])
|
|
1955
1971
|
sb_de_name = sb_de.name()
|
|
1956
|
-
await send_to_mempool(full_node_api, sb_de)
|
|
1972
|
+
await send_to_mempool(full_node_api, wallet_peer, sb_de)
|
|
1957
1973
|
sb_ef = SpendBundle.aggregate([sb_e, tx_f.spend_bundle])
|
|
1958
1974
|
sb_ef_name = sb_ef.name()
|
|
1959
|
-
await send_to_mempool(full_node_api, sb_ef)
|
|
1975
|
+
await send_to_mempool(full_node_api, wallet_peer, sb_ef)
|
|
1960
1976
|
# Send also a transaction EG that spends E differently from DE and EF,
|
|
1961
1977
|
# to ensure it's rejected by the mempool
|
|
1962
1978
|
conditions = [
|
|
@@ -1976,7 +1992,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
1976
1992
|
[tx_g] = action_scope.side_effects.transactions
|
|
1977
1993
|
assert tx_g.spend_bundle is not None
|
|
1978
1994
|
sb_e2g = SpendBundle.aggregate([sb_e2, tx_g.spend_bundle])
|
|
1979
|
-
await send_to_mempool(full_node_api, sb_e2g, expecting_conflict=True)
|
|
1995
|
+
await send_to_mempool(full_node_api, wallet_peer, sb_e2g, expecting_conflict=True)
|
|
1980
1996
|
|
|
1981
1997
|
# Make sure our coin IDs to spend bundles mappings are correct
|
|
1982
1998
|
assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == {sb_de_name}
|
|
@@ -2131,7 +2147,7 @@ async def test_identical_spend_aggregation_e2e(
|
|
|
2131
2147
|
),
|
|
2132
2148
|
],
|
|
2133
2149
|
)
|
|
2134
|
-
async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected:
|
|
2150
|
+
async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected: Err | None) -> None:
|
|
2135
2151
|
coins = []
|
|
2136
2152
|
test_coin_records = {}
|
|
2137
2153
|
|
|
@@ -2150,27 +2166,26 @@ async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expec
|
|
|
2150
2166
|
ret.append(r)
|
|
2151
2167
|
return ret
|
|
2152
2168
|
|
|
2153
|
-
|
|
2169
|
+
async with instantiate_mempool_manager(
|
|
2154
2170
|
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
|
|
2171
|
+
) as mempool_manager:
|
|
2172
|
+
coin_spends = [
|
|
2173
|
+
make_spend(coins[0], IDENTITY_PUZZLE, Program.to([cond1])),
|
|
2174
|
+
make_spend(coins[1], IDENTITY_PUZZLE, Program.to([cond2])),
|
|
2175
|
+
]
|
|
2176
|
+
|
|
2177
|
+
bundle = SpendBundle(coin_spends, G2Element())
|
|
2178
|
+
bundle_name = bundle.name()
|
|
2179
|
+
try:
|
|
2180
|
+
result = await add_spendbundle(mempool_manager, bundle, bundle_name)
|
|
2181
|
+
print(result)
|
|
2182
|
+
if expected is not None:
|
|
2183
|
+
assert result == (None, MempoolInclusionStatus.FAILED, expected)
|
|
2184
|
+
else:
|
|
2185
|
+
assert result[0] is not None
|
|
2186
|
+
assert result[1] != MempoolInclusionStatus.FAILED
|
|
2187
|
+
except ValidationError as e:
|
|
2188
|
+
assert e.code == expected
|
|
2174
2189
|
|
|
2175
2190
|
|
|
2176
2191
|
TEST_FILL_RATE_ITEM_COST = 144_720_020
|
|
@@ -2202,6 +2217,7 @@ async def test_fill_rate_block_validation(
|
|
|
2202
2217
|
max_block_clvm_cost: uint64,
|
|
2203
2218
|
expected_block_items: int,
|
|
2204
2219
|
expected_block_cost: uint64,
|
|
2220
|
+
self_hostname: str,
|
|
2205
2221
|
) -> None:
|
|
2206
2222
|
"""
|
|
2207
2223
|
This test covers the case where we set the fill rate to 100% and ensure
|
|
@@ -2212,8 +2228,10 @@ async def test_fill_rate_block_validation(
|
|
|
2212
2228
|
expecting only one of the two test items to get included in the block.
|
|
2213
2229
|
"""
|
|
2214
2230
|
|
|
2215
|
-
async def send_to_mempool(
|
|
2216
|
-
|
|
2231
|
+
async def send_to_mempool(
|
|
2232
|
+
full_node: FullNodeSimulator, dummy_peer: WSChiaConnection, spend_bundle: SpendBundle
|
|
2233
|
+
) -> None:
|
|
2234
|
+
res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle), dummy_peer)
|
|
2217
2235
|
assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
|
|
2218
2236
|
res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
|
|
2219
2237
|
assert res_parsed.status == MempoolInclusionStatus.SUCCESS.value
|
|
@@ -2232,11 +2250,13 @@ async def test_fill_rate_block_validation(
|
|
|
2232
2250
|
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, ph)
|
|
2233
2251
|
coin = next(cr.coin for cr in coin_records if cr.coin.amount == 250_000_000_000)
|
|
2234
2252
|
coins_and_puzzles.append((coin, puzzle))
|
|
2253
|
+
_, dummy_node_id = await add_dummy_connection(full_node_api.server, self_hostname, 12312)
|
|
2254
|
+
dummy_peer = full_node_api.server.all_connections[dummy_node_id]
|
|
2235
2255
|
sbs_info = []
|
|
2236
2256
|
for coin, puzzle in coins_and_puzzles:
|
|
2237
2257
|
coin_spend = make_spend(coin, puzzle, SerializedProgram.to([]))
|
|
2238
2258
|
sb = SpendBundle([coin_spend], G2Element())
|
|
2239
|
-
await send_to_mempool(full_node_api, sb)
|
|
2259
|
+
await send_to_mempool(full_node_api, dummy_peer, sb)
|
|
2240
2260
|
sbs_info.append((coin.name(), puzzle, sb.name()))
|
|
2241
2261
|
return sbs_info
|
|
2242
2262
|
|
|
@@ -2288,14 +2308,14 @@ async def test_fill_rate_block_validation(
|
|
|
2288
2308
|
|
|
2289
2309
|
@pytest.mark.parametrize("optimized_path", [True, False])
|
|
2290
2310
|
@pytest.mark.anyio
|
|
2291
|
-
async def test_height_added_to_mempool(optimized_path: bool) -> None:
|
|
2311
|
+
async def test_height_added_to_mempool(optimized_path: bool, test_coins_mempool_manager: MempoolManager) -> None:
|
|
2292
2312
|
"""
|
|
2293
2313
|
This test covers scenarios when the mempool is updated or rebuilt, to make
|
|
2294
2314
|
sure that mempool items maintain correct height added to mempool values.
|
|
2295
2315
|
We control whether we're updating the mempool or rebuilding it, through the
|
|
2296
2316
|
`optimized_path` param.
|
|
2297
2317
|
"""
|
|
2298
|
-
mempool_manager =
|
|
2318
|
+
mempool_manager = test_coins_mempool_manager
|
|
2299
2319
|
assert mempool_manager.peak is not None
|
|
2300
2320
|
assert mempool_manager.peak.height == TEST_HEIGHT
|
|
2301
2321
|
assert mempool_manager.peak.header_hash == height_hash(TEST_HEIGHT)
|
|
@@ -2346,9 +2366,9 @@ class TestCoins:
|
|
|
2346
2366
|
self.lineage_info[ph] = UnspentLineageInfo(c.name(), c.parent_coin_info, bytes32([42] * 32))
|
|
2347
2367
|
|
|
2348
2368
|
def spend_coin(self, coin_id: bytes32, height: uint32 = uint32(10)) -> None:
|
|
2349
|
-
self.coin_records[coin_id] =
|
|
2369
|
+
self.coin_records[coin_id] = self.coin_records[coin_id].replace(spent_block_index=height)
|
|
2350
2370
|
|
|
2351
|
-
def update_lineage(self, puzzle_hash: bytes32, coin:
|
|
2371
|
+
def update_lineage(self, puzzle_hash: bytes32, coin: Coin | None) -> None:
|
|
2352
2372
|
if coin is None:
|
|
2353
2373
|
self.lineage_info.pop(puzzle_hash)
|
|
2354
2374
|
else:
|
|
@@ -2365,7 +2385,7 @@ class TestCoins:
|
|
|
2365
2385
|
|
|
2366
2386
|
return ret
|
|
2367
2387
|
|
|
2368
|
-
async def get_unspent_lineage_info(self, ph: bytes32) ->
|
|
2388
|
+
async def get_unspent_lineage_info(self, ph: bytes32) -> UnspentLineageInfo | None:
|
|
2369
2389
|
return self.lineage_info.get(ph)
|
|
2370
2390
|
|
|
2371
2391
|
|
|
@@ -2396,15 +2416,16 @@ def make_singleton_spend(
|
|
|
2396
2416
|
return ret
|
|
2397
2417
|
|
|
2398
2418
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2419
|
+
@asynccontextmanager
|
|
2420
|
+
async def setup_mempool(coins: TestCoins) -> AsyncGenerator[MempoolManager, None]:
|
|
2421
|
+
async with MempoolManager.managed(
|
|
2401
2422
|
coins.get_coin_records,
|
|
2402
2423
|
coins.get_unspent_lineage_info,
|
|
2403
2424
|
DEFAULT_CONSTANTS,
|
|
2404
|
-
)
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2425
|
+
) as mempool_manager:
|
|
2426
|
+
test_block_record = create_test_block_record(height=uint32(5000000), timestamp=uint64(12345678))
|
|
2427
|
+
await mempool_manager.new_peak(test_block_record, None)
|
|
2428
|
+
yield mempool_manager
|
|
2408
2429
|
|
|
2409
2430
|
|
|
2410
2431
|
# adds a new peak to the memepool manager with the specified coin IDs spent
|
|
@@ -2452,55 +2473,54 @@ async def test_new_peak_ff_eviction(
|
|
|
2452
2473
|
|
|
2453
2474
|
coins = TestCoins([singleton_spend.coin, TEST_COIN], {singleton_spend.coin.puzzle_hash: singleton_spend.coin})
|
|
2454
2475
|
|
|
2455
|
-
|
|
2476
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2477
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(
|
|
2478
|
+
bundle,
|
|
2479
|
+
make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
|
|
2480
|
+
bundle.name(),
|
|
2481
|
+
first_added_height=uint32(1),
|
|
2482
|
+
)
|
|
2456
2483
|
|
|
2457
|
-
|
|
2458
|
-
bundle
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2484
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2485
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2486
|
+
assert item is not None
|
|
2487
|
+
singleton_name = singleton_spend.coin.name()
|
|
2488
|
+
assert item.bundle_coin_spends[singleton_name].supports_fast_forward
|
|
2489
|
+
latest_singleton_lineage = item.bundle_coin_spends[singleton_name].latest_singleton_lineage
|
|
2490
|
+
assert latest_singleton_lineage is not None
|
|
2491
|
+
assert latest_singleton_lineage.coin_id == singleton_name
|
|
2463
2492
|
|
|
2464
|
-
|
|
2465
|
-
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2466
|
-
assert item is not None
|
|
2467
|
-
singleton_name = singleton_spend.coin.name()
|
|
2468
|
-
assert item.bundle_coin_spends[singleton_name].supports_fast_forward
|
|
2469
|
-
latest_singleton_lineage = item.bundle_coin_spends[singleton_name].latest_singleton_lineage
|
|
2470
|
-
assert latest_singleton_lineage is not None
|
|
2471
|
-
assert latest_singleton_lineage.coin_id == singleton_name
|
|
2472
|
-
|
|
2473
|
-
spent_coins: list[bytes32] = []
|
|
2474
|
-
|
|
2475
|
-
if spend_singleton:
|
|
2476
|
-
# pretend that we melted the singleton, the FF spend
|
|
2477
|
-
coins.update_lineage(singleton_spend.coin.puzzle_hash, None)
|
|
2478
|
-
coins.spend_coin(singleton_spend.coin.name(), uint32(11))
|
|
2479
|
-
spent_coins.append(singleton_spend.coin.name())
|
|
2480
|
-
|
|
2481
|
-
if spend_plain:
|
|
2482
|
-
# pretend that we spend singleton, the FF spend
|
|
2483
|
-
coins.spend_coin(coin_spend.coin.name(), uint32(11))
|
|
2484
|
-
spent_coins.append(coin_spend.coin.name())
|
|
2485
|
-
|
|
2486
|
-
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2487
|
-
invariant_check_mempool(mempool_manager.mempool)
|
|
2493
|
+
spent_coins: list[bytes32] = []
|
|
2488
2494
|
|
|
2489
|
-
|
|
2490
|
-
|
|
2495
|
+
if spend_singleton:
|
|
2496
|
+
# pretend that we melted the singleton, the FF spend
|
|
2497
|
+
coins.update_lineage(singleton_spend.coin.puzzle_hash, None)
|
|
2498
|
+
coins.spend_coin(singleton_spend.coin.name(), uint32(11))
|
|
2499
|
+
spent_coins.append(singleton_spend.coin.name())
|
|
2491
2500
|
|
|
2492
|
-
|
|
2501
|
+
if spend_plain:
|
|
2502
|
+
# pretend that we spend singleton, the FF spend
|
|
2503
|
+
coins.spend_coin(coin_spend.coin.name(), uint32(11))
|
|
2504
|
+
spent_coins.append(coin_spend.coin.name())
|
|
2493
2505
|
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2506
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2507
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2508
|
+
|
|
2509
|
+
if reverse_spend_order:
|
|
2510
|
+
spent_coins.reverse()
|
|
2511
|
+
|
|
2512
|
+
await advance_mempool(mempool_manager, spent_coins, use_optimization=use_optimization)
|
|
2513
|
+
|
|
2514
|
+
# make sure the mempool item is evicted
|
|
2515
|
+
if spend_singleton or spend_plain:
|
|
2516
|
+
assert mempool_manager.get_mempool_item(bundle.name()) is None
|
|
2517
|
+
else:
|
|
2518
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2519
|
+
assert item is not None
|
|
2520
|
+
assert item.bundle_coin_spends[singleton_spend.coin.name()].supports_fast_forward
|
|
2521
|
+
latest_singleton_lineage = item.bundle_coin_spends[singleton_spend.coin.name()].latest_singleton_lineage
|
|
2522
|
+
assert latest_singleton_lineage is not None
|
|
2523
|
+
assert latest_singleton_lineage.coin_id == singleton_spend.coin.name()
|
|
2504
2524
|
|
|
2505
2525
|
|
|
2506
2526
|
@pytest.mark.anyio
|
|
@@ -2534,45 +2554,44 @@ async def test_multiple_ff(use_optimization: bool) -> None:
|
|
|
2534
2554
|
singleton_ph = singleton_spend2.coin.puzzle_hash
|
|
2535
2555
|
coins = TestCoins([singleton_spend1.coin, singleton_spend2.coin, TEST_COIN], {singleton_ph: singleton_spend2.coin})
|
|
2536
2556
|
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
invariant_check_mempool(mempool_manager.mempool)
|
|
2557
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2558
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(
|
|
2559
|
+
bundle,
|
|
2560
|
+
make_test_conds(
|
|
2561
|
+
spend_ids=[
|
|
2562
|
+
(singleton_spend1.coin, ELIGIBLE_FOR_FF),
|
|
2563
|
+
(singleton_spend2.coin, ELIGIBLE_FOR_FF),
|
|
2564
|
+
(TEST_COIN, 0),
|
|
2565
|
+
],
|
|
2566
|
+
cost=1000000,
|
|
2567
|
+
),
|
|
2568
|
+
bundle.name(),
|
|
2569
|
+
first_added_height=uint32(1),
|
|
2570
|
+
)
|
|
2571
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2572
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2554
2573
|
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2574
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2575
|
+
assert item is not None
|
|
2576
|
+
assert item.bundle_coin_spends[singleton_spend1.coin.name()].supports_fast_forward
|
|
2577
|
+
assert item.bundle_coin_spends[singleton_spend2.coin.name()].supports_fast_forward
|
|
2578
|
+
assert not item.bundle_coin_spends[coin_spend.coin.name()].supports_fast_forward
|
|
2560
2579
|
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2580
|
+
# spend the singleton coin2 and make coin3 the latest version
|
|
2581
|
+
coins.update_lineage(singleton_ph, singleton_spend3.coin)
|
|
2582
|
+
coins.spend_coin(singleton_spend2.coin.name(), uint32(11))
|
|
2564
2583
|
|
|
2565
|
-
|
|
2584
|
+
await advance_mempool(mempool_manager, [singleton_spend2.coin.name()], use_optimization=use_optimization)
|
|
2566
2585
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2586
|
+
# we can still fast-forward the singleton spends, the bundle should still be valid
|
|
2587
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2588
|
+
assert item is not None
|
|
2589
|
+
spend = item.bundle_coin_spends[singleton_spend1.coin.name()]
|
|
2590
|
+
assert spend.latest_singleton_lineage is not None
|
|
2591
|
+
assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
|
|
2592
|
+
spend = item.bundle_coin_spends[singleton_spend2.coin.name()]
|
|
2593
|
+
assert spend.latest_singleton_lineage is not None
|
|
2594
|
+
assert spend.latest_singleton_lineage.coin_id == singleton_spend3.coin.name()
|
|
2576
2595
|
|
|
2577
2596
|
|
|
2578
2597
|
@pytest.mark.anyio
|
|
@@ -2604,47 +2623,46 @@ async def test_advancing_ff(use_optimization: bool) -> None:
|
|
|
2604
2623
|
singleton_ph = spend_a.coin.puzzle_hash
|
|
2605
2624
|
coins = TestCoins([spend_a.coin, spend_b.coin, spend_c.coin, TEST_COIN], {singleton_ph: spend_a.coin})
|
|
2606
2625
|
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
invariant_check_mempool(mempool_manager.mempool)
|
|
2626
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2627
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(
|
|
2628
|
+
bundle,
|
|
2629
|
+
make_test_conds(spend_ids=[(spend_a.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=1000000),
|
|
2630
|
+
bundle.name(),
|
|
2631
|
+
first_added_height=uint32(1),
|
|
2632
|
+
)
|
|
2633
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2634
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2617
2635
|
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2636
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2637
|
+
assert item is not None
|
|
2638
|
+
spend = item.bundle_coin_spends[spend_a.coin.name()]
|
|
2639
|
+
assert spend.supports_fast_forward
|
|
2640
|
+
assert spend.latest_singleton_lineage is not None
|
|
2641
|
+
assert spend.latest_singleton_lineage.coin_id == spend_a.coin.name()
|
|
2624
2642
|
|
|
2625
|
-
|
|
2626
|
-
|
|
2643
|
+
coins.update_lineage(singleton_ph, spend_b.coin)
|
|
2644
|
+
coins.spend_coin(spend_a.coin.name(), uint32(11))
|
|
2627
2645
|
|
|
2628
|
-
|
|
2646
|
+
await advance_mempool(mempool_manager, [spend_a.coin.name()])
|
|
2629
2647
|
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2648
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2649
|
+
assert item is not None
|
|
2650
|
+
spend = item.bundle_coin_spends[spend_a.coin.name()]
|
|
2651
|
+
assert spend.supports_fast_forward
|
|
2652
|
+
assert spend.latest_singleton_lineage is not None
|
|
2653
|
+
assert spend.latest_singleton_lineage.coin_id == spend_b.coin.name()
|
|
2636
2654
|
|
|
2637
|
-
|
|
2638
|
-
|
|
2655
|
+
coins.update_lineage(singleton_ph, spend_c.coin)
|
|
2656
|
+
coins.spend_coin(spend_b.coin.name(), uint32(12))
|
|
2639
2657
|
|
|
2640
|
-
|
|
2658
|
+
await advance_mempool(mempool_manager, [spend_b.coin.name()], use_optimization=use_optimization)
|
|
2641
2659
|
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2660
|
+
item = mempool_manager.get_mempool_item(bundle.name())
|
|
2661
|
+
assert item is not None
|
|
2662
|
+
spend = item.bundle_coin_spends[spend_a.coin.name()]
|
|
2663
|
+
assert spend.supports_fast_forward
|
|
2664
|
+
assert spend.latest_singleton_lineage is not None
|
|
2665
|
+
assert spend.latest_singleton_lineage.coin_id == spend_c.coin.name()
|
|
2648
2666
|
|
|
2649
2667
|
|
|
2650
2668
|
@pytest.mark.parametrize("old", [True, False])
|
|
@@ -2653,15 +2671,14 @@ def test_no_peak(old: bool, transactions_1000: list[SpendBundle]) -> None:
|
|
|
2653
2671
|
all_coins = [s.coin for b in bundles for s in b.coin_spends]
|
|
2654
2672
|
coins = TestCoins(all_coins, {})
|
|
2655
2673
|
|
|
2656
|
-
|
|
2674
|
+
with MempoolManager(
|
|
2657
2675
|
coins.get_coin_records,
|
|
2658
2676
|
coins.get_unspent_lineage_info,
|
|
2659
2677
|
DEFAULT_CONSTANTS,
|
|
2660
|
-
)
|
|
2661
|
-
|
|
2662
|
-
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
2678
|
+
) as mempool_manager:
|
|
2679
|
+
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
2663
2680
|
|
|
2664
|
-
|
|
2681
|
+
assert create_block(bytes32([1] * 32), 10.0) is None
|
|
2665
2682
|
|
|
2666
2683
|
|
|
2667
2684
|
@pytest.fixture(name="test_wallet")
|
|
@@ -2766,77 +2783,76 @@ async def test_create_block_generator(
|
|
|
2766
2783
|
rng = random.Random(seed)
|
|
2767
2784
|
|
|
2768
2785
|
# run the test multiple times, generating different combinations of mempools
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2786
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2787
|
+
included_bundles = rng.sample(bundles, mempool_size)
|
|
2788
|
+
expected_additions: set[Coin] = set()
|
|
2789
|
+
expected_removals: set[Coin] = set()
|
|
2790
|
+
expected_signature = G2Element()
|
|
2791
|
+
for sb in included_bundles:
|
|
2792
|
+
pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
|
|
2793
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(
|
|
2794
|
+
sb, pre_validation, sb.name(), first_added_height=uint32(1)
|
|
2795
|
+
)
|
|
2796
|
+
expected_additions.update(sb.additions())
|
|
2797
|
+
expected_removals.update(sb.removals())
|
|
2798
|
+
|
|
2799
|
+
expected_signature += sb.aggregated_signature
|
|
2800
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2801
|
+
item = mempool_manager.get_mempool_item(sb.name())
|
|
2802
|
+
assert item is not None
|
|
2803
|
+
all_items = mempool_manager.mempool.all_items()
|
|
2804
|
+
assert len(list(all_items)) == len(included_bundles)
|
|
2805
|
+
|
|
2806
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2807
|
+
|
|
2808
|
+
assert mempool_manager.peak is not None
|
|
2809
|
+
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
2810
|
+
new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
|
|
2811
|
+
assert new_block_gen is not None
|
|
2812
|
+
|
|
2813
|
+
# now, make sure the generator we got is valid
|
|
2814
|
+
|
|
2815
|
+
if expect_failure:
|
|
2816
|
+
assert len(expected_additions) != len(new_block_gen.additions)
|
|
2817
|
+
assert expected_additions != set(new_block_gen.additions)
|
|
2818
|
+
assert len(expected_removals) != len(new_block_gen.removals)
|
|
2819
|
+
assert expected_removals != set(new_block_gen.removals)
|
|
2820
|
+
assert expected_signature != new_block_gen.signature
|
|
2821
|
+
else:
|
|
2822
|
+
assert len(expected_additions) == len(new_block_gen.additions)
|
|
2823
|
+
assert expected_additions == set(new_block_gen.additions)
|
|
2824
|
+
assert len(expected_removals) == len(new_block_gen.removals)
|
|
2825
|
+
assert expected_removals == set(new_block_gen.removals)
|
|
2826
|
+
assert expected_signature == new_block_gen.signature
|
|
2827
|
+
|
|
2828
|
+
err, conds = run_block_generator2(
|
|
2829
|
+
bytes(new_block_gen.program),
|
|
2830
|
+
new_block_gen.generator_refs,
|
|
2831
|
+
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
|
2832
|
+
DEFAULT_FLAGS,
|
|
2833
|
+
new_block_gen.signature,
|
|
2834
|
+
None,
|
|
2835
|
+
DEFAULT_CONSTANTS,
|
|
2779
2836
|
)
|
|
2780
|
-
expected_additions.update(sb.additions())
|
|
2781
|
-
expected_removals.update(sb.removals())
|
|
2782
2837
|
|
|
2783
|
-
|
|
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)
|
|
2838
|
+
assert err is None
|
|
2839
|
+
assert conds is not None
|
|
2789
2840
|
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
# now, make sure the generator we got is valid
|
|
2798
|
-
|
|
2799
|
-
if expect_failure:
|
|
2800
|
-
assert len(expected_additions) != len(new_block_gen.additions)
|
|
2801
|
-
assert expected_additions != set(new_block_gen.additions)
|
|
2802
|
-
assert len(expected_removals) != len(new_block_gen.removals)
|
|
2803
|
-
assert expected_removals != set(new_block_gen.removals)
|
|
2804
|
-
assert expected_signature != new_block_gen.signature
|
|
2805
|
-
else:
|
|
2806
|
-
assert len(expected_additions) == len(new_block_gen.additions)
|
|
2807
|
-
assert expected_additions == set(new_block_gen.additions)
|
|
2808
|
-
assert len(expected_removals) == len(new_block_gen.removals)
|
|
2809
|
-
assert expected_removals == set(new_block_gen.removals)
|
|
2810
|
-
assert expected_signature == new_block_gen.signature
|
|
2811
|
-
|
|
2812
|
-
err, conds = run_block_generator2(
|
|
2813
|
-
bytes(new_block_gen.program),
|
|
2814
|
-
new_block_gen.generator_refs,
|
|
2815
|
-
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
|
2816
|
-
DEFAULT_FLAGS,
|
|
2817
|
-
new_block_gen.signature,
|
|
2818
|
-
None,
|
|
2819
|
-
DEFAULT_CONSTANTS,
|
|
2820
|
-
)
|
|
2821
|
-
|
|
2822
|
-
assert err is None
|
|
2823
|
-
assert conds is not None
|
|
2824
|
-
|
|
2825
|
-
if expect_failure:
|
|
2826
|
-
assert len(conds.spends) != len(expected_removals)
|
|
2827
|
-
else:
|
|
2828
|
-
assert len(conds.spends) == len(expected_removals)
|
|
2829
|
-
assert conds.cost < DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
|
|
2830
|
-
assert new_block_gen.cost == conds.cost
|
|
2841
|
+
if expect_failure:
|
|
2842
|
+
assert len(conds.spends) != len(expected_removals)
|
|
2843
|
+
else:
|
|
2844
|
+
assert len(conds.spends) == len(expected_removals)
|
|
2845
|
+
assert conds.cost < DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM
|
|
2846
|
+
assert new_block_gen.cost == conds.cost
|
|
2831
2847
|
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2848
|
+
num_additions = 0
|
|
2849
|
+
for spend in conds.spends:
|
|
2850
|
+
assert Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)) in expected_removals
|
|
2851
|
+
for add2 in spend.create_coin:
|
|
2852
|
+
assert Coin(spend.coin_id, add2[0], uint64(add2[1])) in expected_additions
|
|
2853
|
+
num_additions += 1
|
|
2838
2854
|
|
|
2839
|
-
|
|
2855
|
+
assert num_additions == len(new_block_gen.additions)
|
|
2840
2856
|
|
|
2841
2857
|
|
|
2842
2858
|
# if we try to fill the mempool with more than 550, all spends won't
|
|
@@ -2850,58 +2866,57 @@ async def test_create_block_generator_real_bundles(seed: int, old: bool, test_bu
|
|
|
2850
2866
|
|
|
2851
2867
|
rng = random.Random(seed)
|
|
2852
2868
|
|
|
2853
|
-
|
|
2869
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2870
|
+
included_bundles = rng.sample(test_bundles, len(test_bundles) // 5)
|
|
2871
|
+
for sb in included_bundles:
|
|
2872
|
+
pre_validation = await mempool_manager.pre_validate_spendbundle(sb)
|
|
2873
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(
|
|
2874
|
+
sb, pre_validation, sb.name(), first_added_height=uint32(1)
|
|
2875
|
+
)
|
|
2854
2876
|
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2877
|
+
# in the test bundles, we have some duplicate spends
|
|
2878
|
+
# just ignore them for now
|
|
2879
|
+
if bundle_add_info.status == MempoolInclusionStatus.FAILED:
|
|
2880
|
+
assert bundle_add_info.error == Err.DOUBLE_SPEND
|
|
2881
|
+
continue
|
|
2882
|
+
assert bundle_add_info.status == MempoolInclusionStatus.SUCCESS
|
|
2883
|
+
item = mempool_manager.get_mempool_item(sb.name())
|
|
2884
|
+
assert item is not None
|
|
2885
|
+
|
|
2886
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2887
|
+
|
|
2888
|
+
assert mempool_manager.peak is not None
|
|
2889
|
+
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
2890
|
+
new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
|
|
2891
|
+
assert new_block_gen is not None
|
|
2892
|
+
|
|
2893
|
+
# now, make sure the generator we got is valid
|
|
2894
|
+
|
|
2895
|
+
err, conds = run_block_generator2(
|
|
2896
|
+
bytes(new_block_gen.program),
|
|
2897
|
+
new_block_gen.generator_refs,
|
|
2898
|
+
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
|
2899
|
+
DEFAULT_FLAGS,
|
|
2900
|
+
new_block_gen.signature,
|
|
2901
|
+
None,
|
|
2902
|
+
DEFAULT_CONSTANTS,
|
|
2860
2903
|
)
|
|
2861
2904
|
|
|
2862
|
-
|
|
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
|
|
2870
|
-
|
|
2871
|
-
invariant_check_mempool(mempool_manager.mempool)
|
|
2872
|
-
|
|
2873
|
-
assert mempool_manager.peak is not None
|
|
2874
|
-
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
2875
|
-
new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
|
|
2876
|
-
assert new_block_gen is not None
|
|
2877
|
-
|
|
2878
|
-
# now, make sure the generator we got is valid
|
|
2879
|
-
|
|
2880
|
-
err, conds = run_block_generator2(
|
|
2881
|
-
bytes(new_block_gen.program),
|
|
2882
|
-
new_block_gen.generator_refs,
|
|
2883
|
-
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
|
2884
|
-
DEFAULT_FLAGS,
|
|
2885
|
-
new_block_gen.signature,
|
|
2886
|
-
None,
|
|
2887
|
-
DEFAULT_CONSTANTS,
|
|
2888
|
-
)
|
|
2889
|
-
|
|
2890
|
-
assert err is None
|
|
2891
|
-
assert conds is not None
|
|
2905
|
+
assert err is None
|
|
2906
|
+
assert conds is not None
|
|
2892
2907
|
|
|
2893
|
-
|
|
2908
|
+
assert conds.cost == new_block_gen.cost
|
|
2894
2909
|
|
|
2895
|
-
|
|
2896
|
-
|
|
2910
|
+
removals: set[Coin] = set()
|
|
2911
|
+
additions: set[Coin] = set()
|
|
2897
2912
|
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2913
|
+
for spend in conds.spends:
|
|
2914
|
+
removals.add(Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount)))
|
|
2915
|
+
for add in spend.create_coin:
|
|
2916
|
+
additions.add(Coin(spend.coin_id, add[0], uint64(add[1])))
|
|
2902
2917
|
|
|
2903
|
-
|
|
2904
|
-
|
|
2918
|
+
assert removals == set(new_block_gen.removals)
|
|
2919
|
+
assert additions == set(new_block_gen.additions)
|
|
2905
2920
|
|
|
2906
2921
|
|
|
2907
2922
|
@pytest.mark.anyio
|
|
@@ -2920,30 +2935,33 @@ async def test_spending_singleton_to_invalidate_existing_ff_spends() -> None:
|
|
|
2920
2935
|
coins=[singleton_spend1.coin, singleton_spend2.coin, TEST_COIN, TEST_COIN2],
|
|
2921
2936
|
lineage={singleton_spend2.coin.puzzle_hash: singleton_spend2.coin},
|
|
2922
2937
|
)
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2938
|
+
|
|
2939
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2940
|
+
coin_spend1 = make_spend(
|
|
2941
|
+
TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
|
|
2942
|
+
)
|
|
2943
|
+
sb1 = SpendBundle([singleton_spend1, coin_spend1], G2Element())
|
|
2944
|
+
sb1_conds = make_test_conds(
|
|
2945
|
+
spend_ids=[(singleton_spend1.coin, ELIGIBLE_FOR_FF), (TEST_COIN, 0)], cost=100_000_000
|
|
2946
|
+
)
|
|
2947
|
+
bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
|
|
2948
|
+
assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
|
|
2949
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2950
|
+
# Trying to spend the same singleton with a different child amount should
|
|
2951
|
+
# trigger a conflict on any replace by fee attempt.
|
|
2952
|
+
coin_spend2 = make_spend(
|
|
2953
|
+
TEST_COIN2, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
|
|
2954
|
+
)
|
|
2955
|
+
sb2 = SpendBundle([singleton_spend2, coin_spend1, coin_spend2], G2Element())
|
|
2956
|
+
# This singleton spend is not eligible for fast forward as its next
|
|
2957
|
+
# iteration has a different amount.
|
|
2958
|
+
sb2_conds = make_test_conds(spend_ids=[(singleton_spend2.coin, 0), (TEST_COIN, 0), (TEST_COIN2, 0)], cost=1337)
|
|
2959
|
+
# This transaction conflicts with the previous one no matter what fee you
|
|
2960
|
+
# pay, because we're changing the fast forward eligibility flag for the
|
|
2961
|
+
# singleton spend.
|
|
2962
|
+
bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
|
|
2963
|
+
assert bundle_add_info2.error == Err.MEMPOOL_CONFLICT
|
|
2964
|
+
assert bundle_add_info2.status == MempoolInclusionStatus.PENDING
|
|
2947
2965
|
|
|
2948
2966
|
|
|
2949
2967
|
@pytest.mark.parametrize("flags", [ELIGIBLE_FOR_DEDUP, ELIGIBLE_FOR_FF, ELIGIBLE_FOR_FF | ELIGIBLE_FOR_DEDUP])
|
|
@@ -2956,37 +2974,38 @@ async def test_check_removals_with_block_creation(flags: int, old: bool) -> None
|
|
|
2956
2974
|
coins = TestCoins(
|
|
2957
2975
|
coins=[singleton_spend.coin, TEST_COIN], lineage={singleton_spend.coin.puzzle_hash: singleton_spend.coin}
|
|
2958
2976
|
)
|
|
2959
|
-
|
|
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
|
-
|
|
2977
|
+
|
|
2978
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
2979
|
+
sb1 = SpendBundle([singleton_spend], G2Element())
|
|
2980
|
+
sb1_conds = make_test_conds(
|
|
2981
|
+
spend_ids=[(singleton_spend.coin, 0)],
|
|
2982
|
+
created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)]],
|
|
2983
|
+
cost=100_000_000,
|
|
2984
|
+
)
|
|
2985
|
+
bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
|
|
2986
|
+
assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
|
|
2987
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
2988
|
+
extra_spend = make_spend(
|
|
2989
|
+
TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
|
|
2990
|
+
)
|
|
2991
|
+
sb2 = SpendBundle([singleton_spend, extra_spend], G2Element())
|
|
2992
|
+
sb2_conds = make_test_conds(
|
|
2993
|
+
spend_ids=[(singleton_spend.coin, flags), (TEST_COIN, 0)],
|
|
2994
|
+
created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)], []],
|
|
2995
|
+
cost=1337,
|
|
2996
|
+
)
|
|
2997
|
+
bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
|
|
2998
|
+
assert bundle_add_info2.status == MempoolInclusionStatus.SUCCESS
|
|
2999
|
+
assert mempool_manager.peak is not None
|
|
3000
|
+
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
3001
|
+
new_block_gen = create_block(mempool_manager.peak.header_hash, 10.0)
|
|
3002
|
+
assert new_block_gen is not None
|
|
3003
|
+
assert len(new_block_gen.additions) == 1
|
|
3004
|
+
assert set(new_block_gen.additions) == {
|
|
3005
|
+
Coin(singleton_spend.coin.name(), singleton_spend.coin.puzzle_hash, uint64(1))
|
|
3006
|
+
}
|
|
3007
|
+
assert len(new_block_gen.removals) == 2
|
|
3008
|
+
assert set(new_block_gen.removals) == {singleton_spend.coin, TEST_COIN}
|
|
2990
3009
|
|
|
2991
3010
|
|
|
2992
3011
|
@pytest.mark.anyio
|
|
@@ -2994,12 +3013,12 @@ async def test_dedup_not_canonical() -> None:
|
|
|
2994
3013
|
# this is ((1)), but with a non-canonical encoding
|
|
2995
3014
|
coin_spend = mk_coin_spend(TEST_COIN, solution="ffffc001018080")
|
|
2996
3015
|
coins = TestCoins([TEST_COIN], lineage={})
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3016
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
3017
|
+
sb = SpendBundle([coin_spend], G2Element())
|
|
3018
|
+
sb_conds = make_test_conds(spend_ids=[(TEST_COIN, ELIGIBLE_FOR_DEDUP)])
|
|
3019
|
+
bundle_add_info = await mempool_manager.add_spend_bundle(sb, sb_conds, sb.name(), uint32(1))
|
|
3020
|
+
assert bundle_add_info.status == MempoolInclusionStatus.FAILED
|
|
3021
|
+
assert bundle_add_info.error == Err.INVALID_COIN_SOLUTION
|
|
3003
3022
|
|
|
3004
3023
|
|
|
3005
3024
|
def make_coin_record(coin: Coin, spent_block_index: int = 0) -> CoinRecord:
|
|
@@ -3012,7 +3031,7 @@ class CheckRemovalsCase:
|
|
|
3012
3031
|
removals: dict[bytes32, CoinRecord]
|
|
3013
3032
|
bundle_coin_spends: dict[bytes32, BundleCoinSpend] = dataclasses.field(default_factory=dict)
|
|
3014
3033
|
conflicting_mempool_items: dict[bytes32, list[MempoolItem]] = dataclasses.field(default_factory=dict)
|
|
3015
|
-
expected_result: tuple[
|
|
3034
|
+
expected_result: tuple[Err | None, list[MempoolItem]] = dataclasses.field(default_factory=lambda: (None, []))
|
|
3016
3035
|
marks: Marks = ()
|
|
3017
3036
|
|
|
3018
3037
|
|
|
@@ -3161,6 +3180,119 @@ def test_check_removals(case: CheckRemovalsCase) -> None:
|
|
|
3161
3180
|
assert set(conflicts) == set(expected_conflicts)
|
|
3162
3181
|
|
|
3163
3182
|
|
|
3183
|
+
# this puzzle just creates coins, however many are requested by the solution
|
|
3184
|
+
# (mod (A)
|
|
3185
|
+
# (defun loop (n)
|
|
3186
|
+
# (if (= n 1)
|
|
3187
|
+
# (list)
|
|
3188
|
+
# (c (list 51 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff n) (loop (- n 1))))
|
|
3189
|
+
# )
|
|
3190
|
+
# (loop A)
|
|
3191
|
+
# )
|
|
3192
|
+
create_coins_loop: str = (
|
|
3193
|
+
"ff02ffff01ff02ff02ffff04ff02ffff04ff05ff80808080ffff04ffff01ff02"
|
|
3194
|
+
"ffff03ffff09ff05ffff010180ff80ffff01ff04ffff04ffff0133ffff04ffff"
|
|
3195
|
+
"01a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
3196
|
+
"ffffffff04ff05ff80808080ffff02ff02ffff04ff02ffff04ffff11ff05ffff"
|
|
3197
|
+
"010180ff808080808080ff0180ff018080"
|
|
3198
|
+
)
|
|
3199
|
+
|
|
3200
|
+
# (mod (A)
|
|
3201
|
+
# (defun loop (n)
|
|
3202
|
+
# (if (= n 0) (list) (c n (loop (- n 1))))
|
|
3203
|
+
# )
|
|
3204
|
+
# (c (c 1 (loop A)) ())
|
|
3205
|
+
# )
|
|
3206
|
+
deep_recursion: str = (
|
|
3207
|
+
"ff02ffff01ff04ffff04ffff0101ffff02ff02ffff04ff02ffff04ff05ff8080"
|
|
3208
|
+
"808080ff8080ffff04ffff01ff02ffff03ffff09ff05ff8080ff80ffff01ff04"
|
|
3209
|
+
"ff05ffff02ff02ffff04ff02ffff04ffff11ff05ffff010180ff808080808080"
|
|
3210
|
+
"ff0180ff018080"
|
|
3211
|
+
)
|
|
3212
|
+
|
|
3213
|
+
|
|
3214
|
+
# this test uses artificial puzzles just to exercise the block creation. These
|
|
3215
|
+
# spends are expected not to verify any signatures
|
|
3216
|
+
# This is to keep the test simple.
|
|
3217
|
+
@pytest.mark.parametrize(
|
|
3218
|
+
"puzzle, solution",
|
|
3219
|
+
[
|
|
3220
|
+
pytest.param(create_coins_loop, "ff8207d180", id="2000-coins"),
|
|
3221
|
+
pytest.param(create_coins_loop, "ff8203e980", id="1000-coins"),
|
|
3222
|
+
pytest.param(create_coins_loop, "ff8201f580", id="500 coins"),
|
|
3223
|
+
pytest.param(deep_recursion, "ff830f424080", id="recurse-1000000"),
|
|
3224
|
+
pytest.param(deep_recursion, "ff82271080", id="recurse-10000"),
|
|
3225
|
+
pytest.param(deep_recursion, "ff6480", id="recurse-100"),
|
|
3226
|
+
],
|
|
3227
|
+
)
|
|
3228
|
+
@pytest.mark.parametrize("old", [True, False])
|
|
3229
|
+
@pytest.mark.anyio
|
|
3230
|
+
async def test_create_block_generator_custom_spend(
|
|
3231
|
+
puzzle: str, solution: str, old: bool, softfork_height: uint32
|
|
3232
|
+
) -> None:
|
|
3233
|
+
solution_str = SerializedProgram.fromhex(solution)
|
|
3234
|
+
puzzle_reveal = SerializedProgram.fromhex(puzzle)
|
|
3235
|
+
puzzle_hash = puzzle_reveal.get_tree_hash()
|
|
3236
|
+
|
|
3237
|
+
async with setup_mempool_with_coins(
|
|
3238
|
+
coin_amounts=list(range(100000000, 100000022)), puzzle_hash=puzzle_hash, height=softfork_height
|
|
3239
|
+
) as (mempool_manager, coins):
|
|
3240
|
+
spend_bundles = [
|
|
3241
|
+
SpendBundle(
|
|
3242
|
+
coin_spends=[CoinSpend(coin, puzzle_reveal=puzzle_reveal, solution=solution_str)],
|
|
3243
|
+
aggregated_signature=G2Element(),
|
|
3244
|
+
)
|
|
3245
|
+
for coin in coins
|
|
3246
|
+
]
|
|
3247
|
+
|
|
3248
|
+
for sb in spend_bundles:
|
|
3249
|
+
try:
|
|
3250
|
+
conds2 = await mempool_manager.pre_validate_spendbundle(sb)
|
|
3251
|
+
await mempool_manager.add_spend_bundle(sb, conds2, sb.name(), softfork_height)
|
|
3252
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
3253
|
+
except Exception as e:
|
|
3254
|
+
print(f"not adding bundle: {e}")
|
|
3255
|
+
# we don't expect this coin to be spent by the resulting generator
|
|
3256
|
+
# so remove it from the list
|
|
3257
|
+
for cs in sb.coin_spends:
|
|
3258
|
+
coins.remove(cs.coin)
|
|
3259
|
+
|
|
3260
|
+
create_block = mempool_manager.create_block_generator if old else mempool_manager.create_block_generator2
|
|
3261
|
+
assert mempool_manager.peak is not None
|
|
3262
|
+
generator = create_block(mempool_manager.peak.header_hash, 10.0)
|
|
3263
|
+
|
|
3264
|
+
if len(coins) == 0:
|
|
3265
|
+
assert generator is None
|
|
3266
|
+
else:
|
|
3267
|
+
assert generator is not None
|
|
3268
|
+
|
|
3269
|
+
assert generator.signature == G2Element()
|
|
3270
|
+
|
|
3271
|
+
removals = set(generator.removals)
|
|
3272
|
+
|
|
3273
|
+
err, conds = run_block_generator2(
|
|
3274
|
+
bytes(generator.program),
|
|
3275
|
+
generator.generator_refs,
|
|
3276
|
+
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
|
|
3277
|
+
0,
|
|
3278
|
+
generator.signature,
|
|
3279
|
+
None,
|
|
3280
|
+
DEFAULT_CONSTANTS,
|
|
3281
|
+
)
|
|
3282
|
+
|
|
3283
|
+
assert err is None
|
|
3284
|
+
assert conds is not None
|
|
3285
|
+
|
|
3286
|
+
assert len(conds.spends) == len(removals)
|
|
3287
|
+
|
|
3288
|
+
for spend in conds.spends:
|
|
3289
|
+
removal = Coin(spend.parent_id, spend.puzzle_hash, uint64(spend.coin_amount))
|
|
3290
|
+
assert removal in coins
|
|
3291
|
+
assert removal in removals
|
|
3292
|
+
|
|
3293
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
3294
|
+
|
|
3295
|
+
|
|
3164
3296
|
@pytest.mark.anyio
|
|
3165
3297
|
async def test_new_peak_deferred_ff_items() -> None:
|
|
3166
3298
|
"""
|
|
@@ -3178,37 +3310,37 @@ async def test_new_peak_deferred_ff_items() -> None:
|
|
|
3178
3310
|
singleton_spend2.coin.puzzle_hash: singleton_spend2.coin,
|
|
3179
3311
|
},
|
|
3180
3312
|
)
|
|
3181
|
-
|
|
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
|
-
|
|
3313
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
3314
|
+
# Let's submit the two singletons transactions to the mempool
|
|
3315
|
+
sb_names = []
|
|
3316
|
+
for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
|
|
3317
|
+
sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
|
|
3318
|
+
sb_name = sb.name()
|
|
3319
|
+
await mempool_manager.add_spend_bundle(
|
|
3320
|
+
sb,
|
|
3321
|
+
make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
|
|
3322
|
+
sb_name,
|
|
3323
|
+
uint32(1),
|
|
3324
|
+
)
|
|
3325
|
+
assert mempool_manager.get_mempool_item(sb_name) is not None
|
|
3326
|
+
sb_names.append(sb_name)
|
|
3327
|
+
# Let's advance the mempool by spending these singletons into new lineages
|
|
3328
|
+
singleton1_new_latest = Coin(singleton1_id, singleton_spend1.coin.puzzle_hash, singleton_spend1.coin.amount)
|
|
3329
|
+
coins.update_lineage(singleton_spend1.coin.puzzle_hash, singleton1_new_latest)
|
|
3330
|
+
singleton2_new_latest = Coin(singleton2_id, singleton_spend2.coin.puzzle_hash, singleton_spend2.coin.amount)
|
|
3331
|
+
coins.update_lineage(singleton_spend2.coin.puzzle_hash, singleton2_new_latest)
|
|
3332
|
+
await advance_mempool(mempool_manager, [singleton1_id, singleton2_id], use_optimization=True)
|
|
3333
|
+
# Both items should get updated with their related latest lineages
|
|
3334
|
+
mi1 = mempool_manager.get_mempool_item(sb_names[0])
|
|
3335
|
+
assert mi1 is not None
|
|
3336
|
+
latest_singleton_lineage1 = mi1.bundle_coin_spends[singleton1_id].latest_singleton_lineage
|
|
3337
|
+
assert latest_singleton_lineage1 is not None
|
|
3338
|
+
assert latest_singleton_lineage1.coin_id == singleton1_new_latest.name()
|
|
3339
|
+
mi2 = mempool_manager.get_mempool_item(sb_names[1])
|
|
3340
|
+
assert mi2 is not None
|
|
3341
|
+
latest_singleton_lineage2 = mi2.bundle_coin_spends[singleton2_id].latest_singleton_lineage
|
|
3342
|
+
assert latest_singleton_lineage2 is not None
|
|
3343
|
+
assert latest_singleton_lineage2.coin_id == singleton2_new_latest.name()
|
|
3212
3344
|
|
|
3213
3345
|
|
|
3214
3346
|
@pytest.mark.anyio
|
|
@@ -3226,47 +3358,47 @@ async def test_different_ff_versions() -> None:
|
|
|
3226
3358
|
coins = TestCoins(
|
|
3227
3359
|
[singleton_spend1.coin, singleton_spend2.coin, TEST_COIN, TEST_COIN2], {singleton_ph: singleton_spend2.coin}
|
|
3228
3360
|
)
|
|
3229
|
-
|
|
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
|
-
|
|
3361
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
3362
|
+
mempool_items: list[MempoolItem] = []
|
|
3363
|
+
for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
|
|
3364
|
+
sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
|
|
3365
|
+
sb_name = sb.name()
|
|
3366
|
+
await mempool_manager.add_spend_bundle(
|
|
3367
|
+
sb,
|
|
3368
|
+
make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
|
|
3369
|
+
sb_name,
|
|
3370
|
+
uint32(1),
|
|
3371
|
+
)
|
|
3372
|
+
mi = mempool_manager.get_mempool_item(sb_name)
|
|
3373
|
+
assert mi is not None
|
|
3374
|
+
mempool_items.append(mi)
|
|
3375
|
+
[mi1, mi2] = mempool_items
|
|
3376
|
+
latest_lineage_id = version2_id
|
|
3377
|
+
assert latest_lineage_id != version1_id
|
|
3378
|
+
# Bundle coin spends key points to version 1 but the lineage is latest (v2)
|
|
3379
|
+
latest_singleton_lineage1 = mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
|
|
3380
|
+
assert latest_singleton_lineage1 is not None
|
|
3381
|
+
assert latest_singleton_lineage1.coin_id == latest_lineage_id
|
|
3382
|
+
# Both the bundle coin spends key and the lineage point to latest (v2)
|
|
3383
|
+
latest_singleton_lineage2 = mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
|
|
3384
|
+
assert latest_singleton_lineage2 is not None
|
|
3385
|
+
assert latest_singleton_lineage2.coin_id == latest_lineage_id
|
|
3386
|
+
# Let's update the lineage with a new version of the singleton
|
|
3387
|
+
new_latest_lineage = Coin(version2_id, singleton_ph, singleton_spend2.coin.amount)
|
|
3388
|
+
new_latest_lineage_id = new_latest_lineage.name()
|
|
3389
|
+
coins.update_lineage(singleton_ph, new_latest_lineage)
|
|
3390
|
+
await advance_mempool(mempool_manager, [version1_id, version2_id], use_optimization=True)
|
|
3391
|
+
# Both items should get updated with the latest lineage
|
|
3392
|
+
new_mi1 = mempool_manager.get_mempool_item(mi1.spend_bundle_name)
|
|
3393
|
+
assert new_mi1 is not None
|
|
3394
|
+
latest_singleton_lineage1 = new_mi1.bundle_coin_spends[version1_id].latest_singleton_lineage
|
|
3395
|
+
assert latest_singleton_lineage1 is not None
|
|
3396
|
+
assert latest_singleton_lineage1.coin_id == new_latest_lineage_id
|
|
3397
|
+
new_mi2 = mempool_manager.get_mempool_item(mi2.spend_bundle_name)
|
|
3398
|
+
assert new_mi2 is not None
|
|
3399
|
+
latest_singleton_lineage2 = new_mi2.bundle_coin_spends[version2_id].latest_singleton_lineage
|
|
3400
|
+
assert latest_singleton_lineage2 is not None
|
|
3401
|
+
assert latest_singleton_lineage2.coin_id == new_latest_lineage_id
|
|
3270
3402
|
|
|
3271
3403
|
|
|
3272
3404
|
@pytest.mark.anyio
|
|
@@ -3284,32 +3416,32 @@ async def test_new_peak_txs_added(condition_and_error: tuple[ConditionOpcode, Er
|
|
|
3284
3416
|
time-lock allows them to be reconsidered.
|
|
3285
3417
|
"""
|
|
3286
3418
|
coins = TestCoins([TEST_COIN], {})
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3419
|
+
async with setup_mempool(coins) as mempool_manager:
|
|
3420
|
+
# Add an item that should go to the pending cache
|
|
3421
|
+
assert mempool_manager.peak is not None
|
|
3422
|
+
condition_height = mempool_manager.peak.height + 1
|
|
3423
|
+
condition, expected_error = condition_and_error
|
|
3424
|
+
_, sb_name, result = await generate_and_add_spendbundle(mempool_manager, [[condition, condition_height]])
|
|
3425
|
+
_, status, error = result
|
|
3426
|
+
assert status == MempoolInclusionStatus.PENDING
|
|
3427
|
+
assert error == expected_error
|
|
3428
|
+
# Advance the mempool beyond the asserted height to retry the test item
|
|
3429
|
+
if optimized_path:
|
|
3430
|
+
spent_coins: list[bytes32] | None = []
|
|
3431
|
+
new_peak_info = await mempool_manager.new_peak(
|
|
3432
|
+
create_test_block_record(height=uint32(condition_height)), spent_coins
|
|
3433
|
+
)
|
|
3434
|
+
# We're not there yet (needs to be higher, not equal)
|
|
3435
|
+
assert new_peak_info.spend_bundle_ids == []
|
|
3436
|
+
assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is None
|
|
3437
|
+
else:
|
|
3438
|
+
spent_coins = None
|
|
3299
3439
|
new_peak_info = await mempool_manager.new_peak(
|
|
3300
|
-
create_test_block_record(height=uint32(condition_height)), spent_coins
|
|
3440
|
+
create_test_block_record(height=uint32(condition_height + 1)), spent_coins
|
|
3301
3441
|
)
|
|
3302
|
-
#
|
|
3303
|
-
assert new_peak_info.spend_bundle_ids == []
|
|
3304
|
-
assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is None
|
|
3305
|
-
else:
|
|
3306
|
-
spent_coins = None
|
|
3307
|
-
new_peak_info = await mempool_manager.new_peak(
|
|
3308
|
-
create_test_block_record(height=uint32(condition_height + 1)), spent_coins
|
|
3309
|
-
)
|
|
3310
|
-
# The item gets retried successfully now
|
|
3311
|
-
assert new_peak_info.spend_bundle_ids == [sb_name]
|
|
3312
|
-
assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is not None
|
|
3442
|
+
# The item gets retried successfully now
|
|
3443
|
+
assert new_peak_info.spend_bundle_ids == [sb_name]
|
|
3444
|
+
assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is not None
|
|
3313
3445
|
|
|
3314
3446
|
|
|
3315
3447
|
@pytest.mark.anyio
|
|
@@ -3318,13 +3450,15 @@ async def test_mempool_item_to_spend_bundle() -> None:
|
|
|
3318
3450
|
Tests that we can properly go back to a `SpendBundle` from a `MempoolItem`.
|
|
3319
3451
|
"""
|
|
3320
3452
|
coins = [Coin(bytes32.random(), IDENTITY_PUZZLE_HASH, uint64(i + 1)) for i in range(random.randint(42, 1337))]
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3453
|
+
async with setup_mempool(TestCoins(coins, {})) as mempool_manager:
|
|
3454
|
+
random_sample = random.sample(coins, 42)
|
|
3455
|
+
sb = SpendBundle(
|
|
3456
|
+
[CoinSpend(c, IDENTITY_PUZZLE, SerializedProgram.to(None)) for c in random_sample], G2Element()
|
|
3457
|
+
)
|
|
3458
|
+
sb_name = sb.name()
|
|
3459
|
+
await add_spendbundle(mempool_manager, sb, sb_name)
|
|
3460
|
+
mi = mempool_manager.get_mempool_item(sb_name)
|
|
3461
|
+
assert mi is not None
|
|
3462
|
+
result = mi.to_spend_bundle()
|
|
3463
|
+
assert result == sb
|
|
3464
|
+
assert result.name() == sb_name
|