chia-blockchain 2.5.1rc1__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 +10 -0
- chia/__main__.py +5 -0
- chia/_tests/README.md +53 -0
- chia/_tests/__init__.py +0 -0
- chia/_tests/blockchain/__init__.py +0 -0
- chia/_tests/blockchain/blockchain_test_utils.py +195 -0
- chia/_tests/blockchain/config.py +4 -0
- chia/_tests/blockchain/test_augmented_chain.py +145 -0
- chia/_tests/blockchain/test_blockchain.py +4202 -0
- chia/_tests/blockchain/test_blockchain_transactions.py +1031 -0
- chia/_tests/blockchain/test_build_chains.py +59 -0
- chia/_tests/blockchain/test_get_block_generator.py +72 -0
- chia/_tests/blockchain/test_lookup_fork_chain.py +194 -0
- chia/_tests/build-init-files.py +92 -0
- chia/_tests/build-job-matrix.py +204 -0
- chia/_tests/check_pytest_monitor_output.py +34 -0
- chia/_tests/check_sql_statements.py +72 -0
- chia/_tests/chia-start-sim +42 -0
- chia/_tests/clvm/__init__.py +0 -0
- chia/_tests/clvm/benchmark_costs.py +23 -0
- chia/_tests/clvm/coin_store.py +149 -0
- chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
- chia/_tests/clvm/test_clvm_step.py +37 -0
- chia/_tests/clvm/test_condition_codes.py +13 -0
- chia/_tests/clvm/test_curry_and_treehash.py +55 -0
- chia/_tests/clvm/test_message_conditions.py +184 -0
- chia/_tests/clvm/test_program.py +150 -0
- chia/_tests/clvm/test_puzzle_compression.py +143 -0
- chia/_tests/clvm/test_puzzle_drivers.py +45 -0
- chia/_tests/clvm/test_puzzles.py +242 -0
- chia/_tests/clvm/test_singletons.py +540 -0
- chia/_tests/clvm/test_spend_sim.py +181 -0
- chia/_tests/cmds/__init__.py +0 -0
- chia/_tests/cmds/cmd_test_utils.py +469 -0
- chia/_tests/cmds/config.py +3 -0
- chia/_tests/cmds/conftest.py +23 -0
- chia/_tests/cmds/test_click_types.py +200 -0
- chia/_tests/cmds/test_cmd_framework.py +620 -0
- chia/_tests/cmds/test_cmds_util.py +97 -0
- chia/_tests/cmds/test_daemon.py +92 -0
- chia/_tests/cmds/test_dev_gh.py +131 -0
- chia/_tests/cmds/test_farm_cmd.py +66 -0
- chia/_tests/cmds/test_show.py +116 -0
- chia/_tests/cmds/test_sim.py +207 -0
- chia/_tests/cmds/test_timelock_args.py +75 -0
- chia/_tests/cmds/test_tx_config_args.py +154 -0
- chia/_tests/cmds/testing_classes.py +59 -0
- chia/_tests/cmds/wallet/__init__.py +0 -0
- chia/_tests/cmds/wallet/test_consts.py +47 -0
- chia/_tests/cmds/wallet/test_dao.py +565 -0
- chia/_tests/cmds/wallet/test_did.py +403 -0
- chia/_tests/cmds/wallet/test_nft.py +471 -0
- chia/_tests/cmds/wallet/test_notifications.py +124 -0
- chia/_tests/cmds/wallet/test_offer.toffer +1 -0
- chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
- chia/_tests/cmds/wallet/test_vcs.py +400 -0
- chia/_tests/cmds/wallet/test_wallet.py +1125 -0
- chia/_tests/cmds/wallet/test_wallet_check.py +109 -0
- chia/_tests/conftest.py +1419 -0
- chia/_tests/connection_utils.py +125 -0
- chia/_tests/core/__init__.py +0 -0
- chia/_tests/core/cmds/__init__.py +0 -0
- chia/_tests/core/cmds/test_beta.py +382 -0
- chia/_tests/core/cmds/test_keys.py +1734 -0
- chia/_tests/core/cmds/test_wallet.py +126 -0
- chia/_tests/core/config.py +3 -0
- chia/_tests/core/consensus/__init__.py +0 -0
- chia/_tests/core/consensus/test_block_creation.py +54 -0
- chia/_tests/core/consensus/test_pot_iterations.py +117 -0
- chia/_tests/core/custom_types/__init__.py +0 -0
- chia/_tests/core/custom_types/test_coin.py +107 -0
- chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
- chia/_tests/core/custom_types/test_spend_bundle.py +70 -0
- chia/_tests/core/daemon/__init__.py +0 -0
- chia/_tests/core/daemon/config.py +4 -0
- chia/_tests/core/daemon/test_daemon.py +2128 -0
- chia/_tests/core/daemon/test_daemon_register.py +109 -0
- chia/_tests/core/daemon/test_keychain_proxy.py +101 -0
- chia/_tests/core/data_layer/__init__.py +0 -0
- chia/_tests/core/data_layer/config.py +5 -0
- chia/_tests/core/data_layer/conftest.py +106 -0
- chia/_tests/core/data_layer/test_data_cli.py +56 -0
- chia/_tests/core/data_layer/test_data_layer.py +83 -0
- chia/_tests/core/data_layer/test_data_layer_util.py +218 -0
- chia/_tests/core/data_layer/test_data_rpc.py +3847 -0
- chia/_tests/core/data_layer/test_data_store.py +2424 -0
- chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
- chia/_tests/core/data_layer/test_plugin.py +91 -0
- chia/_tests/core/data_layer/util.py +233 -0
- chia/_tests/core/farmer/__init__.py +0 -0
- chia/_tests/core/farmer/config.py +3 -0
- chia/_tests/core/farmer/test_farmer_api.py +103 -0
- chia/_tests/core/full_node/__init__.py +0 -0
- chia/_tests/core/full_node/config.py +4 -0
- chia/_tests/core/full_node/dos/__init__.py +0 -0
- chia/_tests/core/full_node/dos/config.py +3 -0
- chia/_tests/core/full_node/full_sync/__init__.py +0 -0
- chia/_tests/core/full_node/full_sync/config.py +4 -0
- chia/_tests/core/full_node/full_sync/test_full_sync.py +443 -0
- chia/_tests/core/full_node/ram_db.py +27 -0
- chia/_tests/core/full_node/stores/__init__.py +0 -0
- chia/_tests/core/full_node/stores/config.py +4 -0
- chia/_tests/core/full_node/stores/test_block_store.py +590 -0
- chia/_tests/core/full_node/stores/test_coin_store.py +897 -0
- chia/_tests/core/full_node/stores/test_full_node_store.py +1219 -0
- chia/_tests/core/full_node/stores/test_hint_store.py +229 -0
- chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
- chia/_tests/core/full_node/test_address_manager.py +588 -0
- chia/_tests/core/full_node/test_block_height_map.py +556 -0
- chia/_tests/core/full_node/test_conditions.py +556 -0
- chia/_tests/core/full_node/test_full_node.py +2700 -0
- chia/_tests/core/full_node/test_generator_tools.py +82 -0
- chia/_tests/core/full_node/test_hint_management.py +104 -0
- chia/_tests/core/full_node/test_node_load.py +34 -0
- chia/_tests/core/full_node/test_performance.py +179 -0
- chia/_tests/core/full_node/test_subscriptions.py +492 -0
- chia/_tests/core/full_node/test_transactions.py +203 -0
- chia/_tests/core/full_node/test_tx_processing_queue.py +155 -0
- chia/_tests/core/large_block.py +2388 -0
- chia/_tests/core/make_block_generator.py +70 -0
- chia/_tests/core/mempool/__init__.py +0 -0
- chia/_tests/core/mempool/config.py +4 -0
- chia/_tests/core/mempool/test_mempool.py +3255 -0
- chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
- chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
- chia/_tests/core/mempool/test_mempool_item_queries.py +190 -0
- chia/_tests/core/mempool/test_mempool_manager.py +2084 -0
- chia/_tests/core/mempool/test_mempool_performance.py +64 -0
- chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
- chia/_tests/core/node_height.py +28 -0
- chia/_tests/core/server/__init__.py +0 -0
- chia/_tests/core/server/config.py +3 -0
- chia/_tests/core/server/flood.py +84 -0
- chia/_tests/core/server/serve.py +135 -0
- chia/_tests/core/server/test_api_protocol.py +21 -0
- chia/_tests/core/server/test_capabilities.py +66 -0
- chia/_tests/core/server/test_dos.py +319 -0
- chia/_tests/core/server/test_event_loop.py +109 -0
- chia/_tests/core/server/test_loop.py +294 -0
- chia/_tests/core/server/test_node_discovery.py +73 -0
- chia/_tests/core/server/test_rate_limits.py +482 -0
- chia/_tests/core/server/test_server.py +226 -0
- chia/_tests/core/server/test_upnp.py +8 -0
- chia/_tests/core/services/__init__.py +0 -0
- chia/_tests/core/services/config.py +3 -0
- chia/_tests/core/services/test_services.py +188 -0
- chia/_tests/core/ssl/__init__.py +0 -0
- chia/_tests/core/ssl/config.py +3 -0
- chia/_tests/core/ssl/test_ssl.py +202 -0
- chia/_tests/core/test_coins.py +33 -0
- chia/_tests/core/test_cost_calculation.py +313 -0
- chia/_tests/core/test_crawler.py +175 -0
- chia/_tests/core/test_crawler_rpc.py +53 -0
- chia/_tests/core/test_daemon_rpc.py +24 -0
- chia/_tests/core/test_db_conversion.py +130 -0
- chia/_tests/core/test_db_validation.py +162 -0
- chia/_tests/core/test_farmer_harvester_rpc.py +505 -0
- chia/_tests/core/test_filter.py +35 -0
- chia/_tests/core/test_full_node_rpc.py +768 -0
- chia/_tests/core/test_merkle_set.py +343 -0
- chia/_tests/core/test_program.py +47 -0
- chia/_tests/core/test_rpc_util.py +86 -0
- chia/_tests/core/test_seeder.py +420 -0
- chia/_tests/core/test_setproctitle.py +13 -0
- chia/_tests/core/util/__init__.py +0 -0
- chia/_tests/core/util/config.py +4 -0
- chia/_tests/core/util/test_block_cache.py +44 -0
- chia/_tests/core/util/test_cached_bls.py +57 -0
- chia/_tests/core/util/test_config.py +337 -0
- chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
- chia/_tests/core/util/test_files.py +391 -0
- chia/_tests/core/util/test_jsonify.py +146 -0
- chia/_tests/core/util/test_keychain.py +522 -0
- chia/_tests/core/util/test_keyring_wrapper.py +491 -0
- chia/_tests/core/util/test_lockfile.py +380 -0
- chia/_tests/core/util/test_log_exceptions.py +187 -0
- chia/_tests/core/util/test_lru_cache.py +56 -0
- chia/_tests/core/util/test_significant_bits.py +40 -0
- chia/_tests/core/util/test_streamable.py +883 -0
- chia/_tests/db/__init__.py +0 -0
- chia/_tests/db/test_db_wrapper.py +566 -0
- chia/_tests/environments/__init__.py +0 -0
- chia/_tests/environments/common.py +35 -0
- chia/_tests/environments/full_node.py +47 -0
- chia/_tests/environments/wallet.py +429 -0
- chia/_tests/ether.py +19 -0
- chia/_tests/farmer_harvester/__init__.py +0 -0
- chia/_tests/farmer_harvester/config.py +3 -0
- chia/_tests/farmer_harvester/test_farmer.py +1264 -0
- chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
- chia/_tests/farmer_harvester/test_filter_prefix_bits.py +131 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters.py +528 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
- chia/_tests/fee_estimation/__init__.py +0 -0
- chia/_tests/fee_estimation/config.py +3 -0
- chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
- chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
- chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +144 -0
- chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
- chia/_tests/generator/__init__.py +0 -0
- chia/_tests/generator/puzzles/__init__.py +0 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
- chia/_tests/generator/test_compression.py +201 -0
- chia/_tests/generator/test_generator_types.py +44 -0
- chia/_tests/generator/test_rom.py +180 -0
- chia/_tests/plot_sync/__init__.py +0 -0
- chia/_tests/plot_sync/config.py +3 -0
- chia/_tests/plot_sync/test_delta.py +101 -0
- chia/_tests/plot_sync/test_plot_sync.py +618 -0
- chia/_tests/plot_sync/test_receiver.py +451 -0
- chia/_tests/plot_sync/test_sender.py +116 -0
- chia/_tests/plot_sync/test_sync_simulated.py +451 -0
- chia/_tests/plot_sync/util.py +68 -0
- chia/_tests/plotting/__init__.py +0 -0
- chia/_tests/plotting/config.py +3 -0
- chia/_tests/plotting/test_plot_manager.py +781 -0
- chia/_tests/plotting/util.py +12 -0
- chia/_tests/pools/__init__.py +0 -0
- chia/_tests/pools/config.py +5 -0
- chia/_tests/pools/test_pool_cli_parsing.py +128 -0
- chia/_tests/pools/test_pool_cmdline.py +1001 -0
- chia/_tests/pools/test_pool_config.py +42 -0
- chia/_tests/pools/test_pool_puzzles_lifecycle.py +397 -0
- chia/_tests/pools/test_pool_rpc.py +1123 -0
- chia/_tests/pools/test_pool_wallet.py +205 -0
- chia/_tests/pools/test_wallet_pool_store.py +161 -0
- chia/_tests/process_junit.py +348 -0
- chia/_tests/rpc/__init__.py +0 -0
- chia/_tests/rpc/test_rpc_client.py +138 -0
- chia/_tests/rpc/test_rpc_server.py +183 -0
- chia/_tests/simulation/__init__.py +0 -0
- chia/_tests/simulation/config.py +6 -0
- chia/_tests/simulation/test_simulation.py +501 -0
- chia/_tests/simulation/test_simulator.py +232 -0
- chia/_tests/simulation/test_start_simulator.py +107 -0
- chia/_tests/testconfig.py +13 -0
- chia/_tests/timelord/__init__.py +0 -0
- chia/_tests/timelord/config.py +3 -0
- chia/_tests/timelord/test_new_peak.py +437 -0
- chia/_tests/timelord/test_timelord.py +11 -0
- chia/_tests/tools/1315537.json +170 -0
- chia/_tests/tools/1315544.json +160 -0
- chia/_tests/tools/1315630.json +150 -0
- chia/_tests/tools/300000.json +105 -0
- chia/_tests/tools/442734.json +140 -0
- chia/_tests/tools/466212.json +130 -0
- chia/_tests/tools/__init__.py +0 -0
- chia/_tests/tools/config.py +5 -0
- chia/_tests/tools/test-blockchain-db.sqlite +0 -0
- chia/_tests/tools/test_full_sync.py +30 -0
- chia/_tests/tools/test_legacy_keyring.py +82 -0
- chia/_tests/tools/test_run_block.py +128 -0
- chia/_tests/tools/test_virtual_project.py +591 -0
- chia/_tests/util/__init__.py +0 -0
- chia/_tests/util/benchmark_cost.py +170 -0
- chia/_tests/util/benchmarks.py +153 -0
- chia/_tests/util/bip39_test_vectors.json +148 -0
- chia/_tests/util/blockchain.py +134 -0
- chia/_tests/util/blockchain_mock.py +132 -0
- chia/_tests/util/build_network_protocol_files.py +302 -0
- chia/_tests/util/clvm_generator.bin +0 -0
- chia/_tests/util/config.py +3 -0
- chia/_tests/util/constants.py +20 -0
- chia/_tests/util/db_connection.py +37 -0
- chia/_tests/util/full_sync.py +253 -0
- chia/_tests/util/gen_ssl_certs.py +114 -0
- chia/_tests/util/generator_tools_testing.py +45 -0
- chia/_tests/util/get_name_puzzle_conditions.py +52 -0
- chia/_tests/util/key_tool.py +36 -0
- chia/_tests/util/misc.py +675 -0
- chia/_tests/util/network_protocol_data.py +1072 -0
- chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
- chia/_tests/util/protocol_messages_json.py +2701 -0
- chia/_tests/util/rpc.py +26 -0
- chia/_tests/util/run_block.py +163 -0
- chia/_tests/util/setup_nodes.py +481 -0
- chia/_tests/util/spend_sim.py +492 -0
- chia/_tests/util/split_managers.py +102 -0
- chia/_tests/util/temp_file.py +14 -0
- chia/_tests/util/test_action_scope.py +144 -0
- chia/_tests/util/test_async_pool.py +366 -0
- chia/_tests/util/test_build_job_matrix.py +42 -0
- chia/_tests/util/test_build_network_protocol_files.py +7 -0
- chia/_tests/util/test_chia_version.py +50 -0
- chia/_tests/util/test_collection.py +11 -0
- chia/_tests/util/test_condition_tools.py +229 -0
- chia/_tests/util/test_config.py +426 -0
- chia/_tests/util/test_dump_keyring.py +60 -0
- chia/_tests/util/test_errors.py +10 -0
- chia/_tests/util/test_full_block_utils.py +279 -0
- chia/_tests/util/test_installed.py +20 -0
- chia/_tests/util/test_limited_semaphore.py +53 -0
- chia/_tests/util/test_logging_filter.py +42 -0
- chia/_tests/util/test_misc.py +445 -0
- chia/_tests/util/test_network.py +73 -0
- chia/_tests/util/test_network_protocol_files.py +578 -0
- chia/_tests/util/test_network_protocol_json.py +267 -0
- chia/_tests/util/test_network_protocol_test.py +256 -0
- chia/_tests/util/test_paginator.py +71 -0
- chia/_tests/util/test_pprint.py +17 -0
- chia/_tests/util/test_priority_mutex.py +488 -0
- chia/_tests/util/test_recursive_replace.py +116 -0
- chia/_tests/util/test_replace_str_to_bytes.py +137 -0
- chia/_tests/util/test_service_groups.py +15 -0
- chia/_tests/util/test_ssl_check.py +31 -0
- chia/_tests/util/test_testnet_overrides.py +19 -0
- chia/_tests/util/test_tests_misc.py +38 -0
- chia/_tests/util/test_timing.py +37 -0
- chia/_tests/util/test_trusted_peer.py +51 -0
- chia/_tests/util/time_out_assert.py +191 -0
- chia/_tests/wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/config.py +4 -0
- chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
- chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
- chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1826 -0
- chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
- chia/_tests/wallet/cat_wallet/test_trades.py +2600 -0
- chia/_tests/wallet/clawback/__init__.py +0 -0
- chia/_tests/wallet/clawback/config.py +3 -0
- chia/_tests/wallet/clawback/test_clawback_decorator.py +78 -0
- chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
- chia/_tests/wallet/clawback/test_clawback_metadata.py +50 -0
- chia/_tests/wallet/config.py +4 -0
- chia/_tests/wallet/conftest.py +278 -0
- chia/_tests/wallet/dao_wallet/__init__.py +0 -0
- chia/_tests/wallet/dao_wallet/config.py +3 -0
- chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1330 -0
- chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
- chia/_tests/wallet/db_wallet/__init__.py +0 -0
- chia/_tests/wallet/db_wallet/config.py +3 -0
- chia/_tests/wallet/db_wallet/test_db_graftroot.py +141 -0
- chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
- chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
- chia/_tests/wallet/did_wallet/__init__.py +0 -0
- chia/_tests/wallet/did_wallet/config.py +4 -0
- chia/_tests/wallet/did_wallet/test_did.py +2284 -0
- chia/_tests/wallet/nft_wallet/__init__.py +0 -0
- chia/_tests/wallet/nft_wallet/config.py +4 -0
- chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1493 -0
- chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1024 -0
- chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +375 -0
- chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
- chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
- chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2584 -0
- chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
- chia/_tests/wallet/rpc/__init__.py +0 -0
- chia/_tests/wallet/rpc/config.py +4 -0
- chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +285 -0
- chia/_tests/wallet/rpc/test_wallet_rpc.py +3153 -0
- chia/_tests/wallet/simple_sync/__init__.py +0 -0
- chia/_tests/wallet/simple_sync/config.py +3 -0
- chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +718 -0
- chia/_tests/wallet/sync/__init__.py +0 -0
- chia/_tests/wallet/sync/config.py +4 -0
- chia/_tests/wallet/sync/test_wallet_sync.py +1692 -0
- chia/_tests/wallet/test_address_type.py +189 -0
- chia/_tests/wallet/test_bech32m.py +45 -0
- chia/_tests/wallet/test_clvm_streamable.py +244 -0
- chia/_tests/wallet/test_coin_management.py +354 -0
- chia/_tests/wallet/test_coin_selection.py +588 -0
- chia/_tests/wallet/test_conditions.py +400 -0
- chia/_tests/wallet/test_debug_spend_bundle.py +218 -0
- chia/_tests/wallet/test_new_wallet_protocol.py +1174 -0
- chia/_tests/wallet/test_nft_store.py +192 -0
- chia/_tests/wallet/test_notifications.py +196 -0
- chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
- chia/_tests/wallet/test_puzzle_store.py +132 -0
- chia/_tests/wallet/test_sign_coin_spends.py +159 -0
- chia/_tests/wallet/test_signer_protocol.py +947 -0
- chia/_tests/wallet/test_singleton.py +122 -0
- chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
- chia/_tests/wallet/test_singleton_store.py +152 -0
- chia/_tests/wallet/test_taproot.py +19 -0
- chia/_tests/wallet/test_transaction_store.py +945 -0
- chia/_tests/wallet/test_util.py +185 -0
- chia/_tests/wallet/test_wallet.py +2139 -0
- chia/_tests/wallet/test_wallet_action_scope.py +85 -0
- chia/_tests/wallet/test_wallet_blockchain.py +111 -0
- chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
- chia/_tests/wallet/test_wallet_interested_store.py +43 -0
- chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
- chia/_tests/wallet/test_wallet_node.py +780 -0
- chia/_tests/wallet/test_wallet_retry.py +95 -0
- chia/_tests/wallet/test_wallet_state_manager.py +259 -0
- chia/_tests/wallet/test_wallet_test_framework.py +275 -0
- chia/_tests/wallet/test_wallet_trade_store.py +218 -0
- chia/_tests/wallet/test_wallet_user_store.py +34 -0
- chia/_tests/wallet/test_wallet_utils.py +156 -0
- chia/_tests/wallet/vc_wallet/__init__.py +0 -0
- chia/_tests/wallet/vc_wallet/config.py +3 -0
- chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
- chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
- chia/_tests/wallet/vc_wallet/test_vc_wallet.py +830 -0
- chia/_tests/wallet/wallet_block_tools.py +327 -0
- chia/_tests/weight_proof/__init__.py +0 -0
- chia/_tests/weight_proof/config.py +3 -0
- chia/_tests/weight_proof/test_weight_proof.py +528 -0
- chia/apis.py +19 -0
- chia/clvm/__init__.py +0 -0
- chia/cmds/__init__.py +0 -0
- chia/cmds/beta.py +184 -0
- chia/cmds/beta_funcs.py +137 -0
- chia/cmds/check_wallet_db.py +420 -0
- chia/cmds/chia.py +151 -0
- chia/cmds/cmd_classes.py +323 -0
- chia/cmds/cmd_helpers.py +242 -0
- chia/cmds/cmds_util.py +488 -0
- chia/cmds/coin_funcs.py +275 -0
- chia/cmds/coins.py +182 -0
- chia/cmds/completion.py +49 -0
- chia/cmds/configure.py +332 -0
- chia/cmds/dao.py +1064 -0
- chia/cmds/dao_funcs.py +598 -0
- chia/cmds/data.py +708 -0
- chia/cmds/data_funcs.py +385 -0
- chia/cmds/db.py +87 -0
- chia/cmds/db_backup_func.py +77 -0
- chia/cmds/db_upgrade_func.py +452 -0
- chia/cmds/db_validate_func.py +184 -0
- chia/cmds/dev.py +18 -0
- chia/cmds/farm.py +100 -0
- chia/cmds/farm_funcs.py +200 -0
- chia/cmds/gh.py +275 -0
- chia/cmds/init.py +63 -0
- chia/cmds/init_funcs.py +367 -0
- chia/cmds/installers.py +131 -0
- chia/cmds/keys.py +527 -0
- chia/cmds/keys_funcs.py +863 -0
- chia/cmds/netspace.py +50 -0
- chia/cmds/netspace_funcs.py +54 -0
- chia/cmds/options.py +32 -0
- chia/cmds/param_types.py +238 -0
- chia/cmds/passphrase.py +131 -0
- chia/cmds/passphrase_funcs.py +292 -0
- chia/cmds/peer.py +51 -0
- chia/cmds/peer_funcs.py +129 -0
- chia/cmds/plotnft.py +260 -0
- chia/cmds/plotnft_funcs.py +405 -0
- chia/cmds/plots.py +230 -0
- chia/cmds/plotters.py +18 -0
- chia/cmds/rpc.py +208 -0
- chia/cmds/show.py +72 -0
- chia/cmds/show_funcs.py +215 -0
- chia/cmds/signer.py +296 -0
- chia/cmds/sim.py +225 -0
- chia/cmds/sim_funcs.py +509 -0
- chia/cmds/start.py +24 -0
- chia/cmds/start_funcs.py +109 -0
- chia/cmds/stop.py +62 -0
- chia/cmds/units.py +9 -0
- chia/cmds/wallet.py +1901 -0
- chia/cmds/wallet_funcs.py +1874 -0
- chia/consensus/__init__.py +0 -0
- chia/consensus/block_body_validation.py +562 -0
- chia/consensus/block_creation.py +546 -0
- chia/consensus/block_header_validation.py +1059 -0
- chia/consensus/block_record.py +31 -0
- chia/consensus/block_rewards.py +53 -0
- chia/consensus/blockchain.py +1087 -0
- chia/consensus/blockchain_interface.py +56 -0
- chia/consensus/coinbase.py +30 -0
- chia/consensus/condition_costs.py +9 -0
- chia/consensus/constants.py +49 -0
- chia/consensus/cost_calculator.py +15 -0
- chia/consensus/default_constants.py +89 -0
- chia/consensus/deficit.py +55 -0
- chia/consensus/difficulty_adjustment.py +412 -0
- chia/consensus/find_fork_point.py +111 -0
- chia/consensus/full_block_to_block_record.py +167 -0
- chia/consensus/get_block_challenge.py +106 -0
- chia/consensus/get_block_generator.py +27 -0
- chia/consensus/make_sub_epoch_summary.py +210 -0
- chia/consensus/multiprocess_validation.py +268 -0
- chia/consensus/pos_quality.py +19 -0
- chia/consensus/pot_iterations.py +67 -0
- chia/consensus/puzzles/__init__.py +0 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
- chia/consensus/vdf_info_computation.py +156 -0
- chia/daemon/__init__.py +0 -0
- chia/daemon/client.py +252 -0
- chia/daemon/keychain_proxy.py +502 -0
- chia/daemon/keychain_server.py +365 -0
- chia/daemon/server.py +1606 -0
- chia/daemon/windows_signal.py +56 -0
- chia/data_layer/__init__.py +0 -0
- chia/data_layer/data_layer.py +1291 -0
- chia/data_layer/data_layer_api.py +33 -0
- chia/data_layer/data_layer_errors.py +50 -0
- chia/data_layer/data_layer_server.py +170 -0
- chia/data_layer/data_layer_util.py +985 -0
- chia/data_layer/data_layer_wallet.py +1311 -0
- chia/data_layer/data_store.py +2267 -0
- chia/data_layer/dl_wallet_store.py +407 -0
- chia/data_layer/download_data.py +389 -0
- chia/data_layer/puzzles/__init__.py +0 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
- chia/data_layer/s3_plugin_config.yml +33 -0
- chia/data_layer/s3_plugin_service.py +468 -0
- chia/data_layer/util/__init__.py +0 -0
- chia/data_layer/util/benchmark.py +107 -0
- chia/data_layer/util/plugin.py +40 -0
- chia/farmer/__init__.py +0 -0
- chia/farmer/farmer.py +923 -0
- chia/farmer/farmer_api.py +820 -0
- chia/full_node/__init__.py +0 -0
- chia/full_node/bitcoin_fee_estimator.py +85 -0
- chia/full_node/block_height_map.py +271 -0
- chia/full_node/block_store.py +576 -0
- chia/full_node/bundle_tools.py +19 -0
- chia/full_node/coin_store.py +647 -0
- chia/full_node/fee_estimate.py +54 -0
- chia/full_node/fee_estimate_store.py +24 -0
- chia/full_node/fee_estimation.py +92 -0
- chia/full_node/fee_estimator.py +90 -0
- chia/full_node/fee_estimator_constants.py +38 -0
- chia/full_node/fee_estimator_interface.py +42 -0
- chia/full_node/fee_history.py +25 -0
- chia/full_node/fee_tracker.py +564 -0
- chia/full_node/full_node.py +3327 -0
- chia/full_node/full_node_api.py +2025 -0
- chia/full_node/full_node_store.py +1033 -0
- chia/full_node/hint_management.py +56 -0
- chia/full_node/hint_store.py +93 -0
- chia/full_node/mempool.py +589 -0
- chia/full_node/mempool_check_conditions.py +146 -0
- chia/full_node/mempool_manager.py +853 -0
- chia/full_node/pending_tx_cache.py +112 -0
- chia/full_node/puzzles/__init__.py +0 -0
- chia/full_node/puzzles/block_program_zero.clsp +14 -0
- chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
- chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
- chia/full_node/signage_point.py +16 -0
- chia/full_node/subscriptions.py +247 -0
- chia/full_node/sync_store.py +146 -0
- chia/full_node/tx_processing_queue.py +78 -0
- chia/full_node/util/__init__.py +0 -0
- chia/full_node/weight_proof.py +1720 -0
- chia/harvester/__init__.py +0 -0
- chia/harvester/harvester.py +272 -0
- chia/harvester/harvester_api.py +380 -0
- chia/introducer/__init__.py +0 -0
- chia/introducer/introducer.py +122 -0
- chia/introducer/introducer_api.py +70 -0
- chia/legacy/__init__.py +0 -0
- chia/legacy/keyring.py +155 -0
- chia/plot_sync/__init__.py +0 -0
- chia/plot_sync/delta.py +61 -0
- chia/plot_sync/exceptions.py +56 -0
- chia/plot_sync/receiver.py +386 -0
- chia/plot_sync/sender.py +340 -0
- chia/plot_sync/util.py +43 -0
- chia/plotters/__init__.py +0 -0
- chia/plotters/bladebit.py +388 -0
- chia/plotters/chiapos.py +63 -0
- chia/plotters/madmax.py +224 -0
- chia/plotters/plotters.py +577 -0
- chia/plotters/plotters_util.py +133 -0
- chia/plotting/__init__.py +0 -0
- chia/plotting/cache.py +213 -0
- chia/plotting/check_plots.py +283 -0
- chia/plotting/create_plots.py +278 -0
- chia/plotting/manager.py +436 -0
- chia/plotting/util.py +336 -0
- chia/pools/__init__.py +0 -0
- chia/pools/pool_config.py +110 -0
- chia/pools/pool_puzzles.py +459 -0
- chia/pools/pool_wallet.py +933 -0
- chia/pools/pool_wallet_info.py +118 -0
- chia/pools/puzzles/__init__.py +0 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
- chia/protocols/__init__.py +0 -0
- chia/protocols/farmer_protocol.py +102 -0
- chia/protocols/full_node_protocol.py +219 -0
- chia/protocols/harvester_protocol.py +216 -0
- chia/protocols/introducer_protocol.py +25 -0
- chia/protocols/pool_protocol.py +177 -0
- chia/protocols/protocol_message_types.py +139 -0
- chia/protocols/protocol_state_machine.py +87 -0
- chia/protocols/protocol_timing.py +8 -0
- chia/protocols/shared_protocol.py +86 -0
- chia/protocols/timelord_protocol.py +93 -0
- chia/protocols/wallet_protocol.py +401 -0
- chia/py.typed +0 -0
- chia/rpc/__init__.py +0 -0
- chia/rpc/crawler_rpc_api.py +80 -0
- chia/rpc/data_layer_rpc_api.py +644 -0
- chia/rpc/data_layer_rpc_client.py +188 -0
- chia/rpc/data_layer_rpc_util.py +58 -0
- chia/rpc/farmer_rpc_api.py +365 -0
- chia/rpc/farmer_rpc_client.py +86 -0
- chia/rpc/full_node_rpc_api.py +959 -0
- chia/rpc/full_node_rpc_client.py +292 -0
- chia/rpc/harvester_rpc_api.py +141 -0
- chia/rpc/harvester_rpc_client.py +54 -0
- chia/rpc/rpc_client.py +164 -0
- chia/rpc/rpc_server.py +521 -0
- chia/rpc/timelord_rpc_api.py +32 -0
- chia/rpc/util.py +93 -0
- chia/rpc/wallet_request_types.py +904 -0
- chia/rpc/wallet_rpc_api.py +4943 -0
- chia/rpc/wallet_rpc_client.py +1814 -0
- chia/seeder/__init__.py +0 -0
- chia/seeder/crawl_store.py +425 -0
- chia/seeder/crawler.py +410 -0
- chia/seeder/crawler_api.py +135 -0
- chia/seeder/dns_server.py +593 -0
- chia/seeder/peer_record.py +146 -0
- chia/seeder/start_crawler.py +92 -0
- chia/server/__init__.py +0 -0
- chia/server/address_manager.py +658 -0
- chia/server/address_manager_store.py +237 -0
- chia/server/api_protocol.py +116 -0
- chia/server/capabilities.py +24 -0
- chia/server/chia_policy.py +346 -0
- chia/server/introducer_peers.py +76 -0
- chia/server/node_discovery.py +714 -0
- chia/server/outbound_message.py +33 -0
- chia/server/rate_limit_numbers.py +214 -0
- chia/server/rate_limits.py +153 -0
- chia/server/server.py +741 -0
- chia/server/signal_handlers.py +120 -0
- chia/server/ssl_context.py +32 -0
- chia/server/start_data_layer.py +151 -0
- chia/server/start_farmer.py +98 -0
- chia/server/start_full_node.py +112 -0
- chia/server/start_harvester.py +93 -0
- chia/server/start_introducer.py +81 -0
- chia/server/start_service.py +316 -0
- chia/server/start_timelord.py +89 -0
- chia/server/start_wallet.py +113 -0
- chia/server/upnp.py +118 -0
- chia/server/ws_connection.py +766 -0
- chia/simulator/__init__.py +0 -0
- chia/simulator/add_blocks_in_batches.py +54 -0
- chia/simulator/block_tools.py +2054 -0
- chia/simulator/full_node_simulator.py +794 -0
- chia/simulator/keyring.py +128 -0
- chia/simulator/setup_services.py +506 -0
- chia/simulator/simulator_constants.py +13 -0
- chia/simulator/simulator_full_node_rpc_api.py +99 -0
- chia/simulator/simulator_full_node_rpc_client.py +60 -0
- chia/simulator/simulator_protocol.py +29 -0
- chia/simulator/simulator_test_tools.py +164 -0
- chia/simulator/socket.py +24 -0
- chia/simulator/ssl_certs.py +114 -0
- chia/simulator/ssl_certs_1.py +697 -0
- chia/simulator/ssl_certs_10.py +697 -0
- chia/simulator/ssl_certs_2.py +697 -0
- chia/simulator/ssl_certs_3.py +697 -0
- chia/simulator/ssl_certs_4.py +697 -0
- chia/simulator/ssl_certs_5.py +697 -0
- chia/simulator/ssl_certs_6.py +697 -0
- chia/simulator/ssl_certs_7.py +697 -0
- chia/simulator/ssl_certs_8.py +697 -0
- chia/simulator/ssl_certs_9.py +697 -0
- chia/simulator/start_simulator.py +143 -0
- chia/simulator/wallet_tools.py +246 -0
- chia/ssl/__init__.py +0 -0
- chia/ssl/chia_ca.crt +19 -0
- chia/ssl/chia_ca.key +28 -0
- chia/ssl/create_ssl.py +249 -0
- chia/ssl/dst_root_ca.pem +20 -0
- chia/timelord/__init__.py +0 -0
- chia/timelord/iters_from_block.py +50 -0
- chia/timelord/timelord.py +1226 -0
- chia/timelord/timelord_api.py +138 -0
- chia/timelord/timelord_launcher.py +190 -0
- chia/timelord/timelord_state.py +244 -0
- chia/timelord/types.py +22 -0
- chia/types/__init__.py +0 -0
- chia/types/aliases.py +35 -0
- chia/types/block_protocol.py +20 -0
- chia/types/blockchain_format/__init__.py +0 -0
- chia/types/blockchain_format/classgroup.py +5 -0
- chia/types/blockchain_format/coin.py +28 -0
- chia/types/blockchain_format/foliage.py +8 -0
- chia/types/blockchain_format/pool_target.py +5 -0
- chia/types/blockchain_format/program.py +269 -0
- chia/types/blockchain_format/proof_of_space.py +135 -0
- chia/types/blockchain_format/reward_chain_block.py +6 -0
- chia/types/blockchain_format/serialized_program.py +5 -0
- chia/types/blockchain_format/sized_bytes.py +11 -0
- chia/types/blockchain_format/slots.py +9 -0
- chia/types/blockchain_format/sub_epoch_summary.py +5 -0
- chia/types/blockchain_format/tree_hash.py +72 -0
- chia/types/blockchain_format/vdf.py +86 -0
- chia/types/clvm_cost.py +13 -0
- chia/types/coin_record.py +43 -0
- chia/types/coin_spend.py +115 -0
- chia/types/condition_opcodes.py +73 -0
- chia/types/condition_with_args.py +16 -0
- chia/types/eligible_coin_spends.py +365 -0
- chia/types/end_of_slot_bundle.py +5 -0
- chia/types/fee_rate.py +38 -0
- chia/types/full_block.py +5 -0
- chia/types/generator_types.py +13 -0
- chia/types/header_block.py +5 -0
- chia/types/internal_mempool_item.py +18 -0
- chia/types/mempool_inclusion_status.py +9 -0
- chia/types/mempool_item.py +85 -0
- chia/types/mempool_submission_status.py +30 -0
- chia/types/mojos.py +7 -0
- chia/types/peer_info.py +64 -0
- chia/types/signing_mode.py +29 -0
- chia/types/spend_bundle.py +30 -0
- chia/types/spend_bundle_conditions.py +7 -0
- chia/types/transaction_queue_entry.py +55 -0
- chia/types/unfinished_block.py +5 -0
- chia/types/unfinished_header_block.py +37 -0
- chia/types/validation_state.py +14 -0
- chia/types/weight_proof.py +49 -0
- chia/util/__init__.py +0 -0
- chia/util/action_scope.py +168 -0
- chia/util/async_pool.py +226 -0
- chia/util/augmented_chain.py +134 -0
- chia/util/batches.py +42 -0
- chia/util/bech32m.py +126 -0
- chia/util/beta_metrics.py +119 -0
- chia/util/block_cache.py +56 -0
- chia/util/byte_types.py +12 -0
- chia/util/check_fork_next_block.py +33 -0
- chia/util/chia_logging.py +144 -0
- chia/util/chia_version.py +33 -0
- chia/util/collection.py +17 -0
- chia/util/condition_tools.py +201 -0
- chia/util/config.py +367 -0
- chia/util/cpu.py +22 -0
- chia/util/db_synchronous.py +23 -0
- chia/util/db_version.py +32 -0
- chia/util/db_wrapper.py +430 -0
- chia/util/default_root.py +27 -0
- chia/util/dump_keyring.py +93 -0
- chia/util/english.txt +2048 -0
- chia/util/errors.py +353 -0
- chia/util/file_keyring.py +469 -0
- chia/util/files.py +97 -0
- chia/util/full_block_utils.py +345 -0
- chia/util/generator_tools.py +72 -0
- chia/util/hash.py +31 -0
- chia/util/initial-config.yaml +694 -0
- chia/util/inline_executor.py +26 -0
- chia/util/ints.py +19 -0
- chia/util/ip_address.py +39 -0
- chia/util/json_util.py +37 -0
- chia/util/keychain.py +676 -0
- chia/util/keyring_wrapper.py +327 -0
- chia/util/limited_semaphore.py +41 -0
- chia/util/lock.py +49 -0
- chia/util/log_exceptions.py +32 -0
- chia/util/logging.py +36 -0
- chia/util/lru_cache.py +31 -0
- chia/util/math.py +20 -0
- chia/util/network.py +182 -0
- chia/util/paginator.py +48 -0
- chia/util/path.py +31 -0
- chia/util/permissions.py +20 -0
- chia/util/prev_transaction_block.py +21 -0
- chia/util/priority_mutex.py +95 -0
- chia/util/profiler.py +197 -0
- chia/util/recursive_replace.py +24 -0
- chia/util/safe_cancel_task.py +16 -0
- chia/util/service_groups.py +47 -0
- chia/util/setproctitle.py +22 -0
- chia/util/significant_bits.py +32 -0
- chia/util/ssl_check.py +213 -0
- chia/util/streamable.py +642 -0
- chia/util/task_referencer.py +59 -0
- chia/util/task_timing.py +382 -0
- chia/util/timing.py +67 -0
- chia/util/vdf_prover.py +30 -0
- chia/util/virtual_project_analysis.py +540 -0
- chia/util/ws_message.py +66 -0
- chia/wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/cat_constants.py +75 -0
- chia/wallet/cat_wallet/cat_info.py +47 -0
- chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
- chia/wallet/cat_wallet/cat_utils.py +164 -0
- chia/wallet/cat_wallet/cat_wallet.py +855 -0
- chia/wallet/cat_wallet/dao_cat_info.py +28 -0
- chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
- chia/wallet/cat_wallet/lineage_store.py +74 -0
- chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
- chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
- chia/wallet/coin_selection.py +188 -0
- chia/wallet/conditions.py +1512 -0
- chia/wallet/dao_wallet/__init__.py +0 -0
- chia/wallet/dao_wallet/dao_info.py +61 -0
- chia/wallet/dao_wallet/dao_utils.py +811 -0
- chia/wallet/dao_wallet/dao_wallet.py +2119 -0
- chia/wallet/db_wallet/__init__.py +0 -0
- chia/wallet/db_wallet/db_wallet_puzzles.py +111 -0
- chia/wallet/derivation_record.py +30 -0
- chia/wallet/derive_keys.py +146 -0
- chia/wallet/did_wallet/__init__.py +0 -0
- chia/wallet/did_wallet/did_info.py +39 -0
- chia/wallet/did_wallet/did_wallet.py +1494 -0
- chia/wallet/did_wallet/did_wallet_puzzles.py +221 -0
- chia/wallet/did_wallet/puzzles/__init__.py +0 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
- chia/wallet/driver_protocol.py +26 -0
- chia/wallet/key_val_store.py +55 -0
- chia/wallet/lineage_proof.py +58 -0
- chia/wallet/nft_wallet/__init__.py +0 -0
- chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
- chia/wallet/nft_wallet/nft_info.py +120 -0
- chia/wallet/nft_wallet/nft_puzzles.py +305 -0
- chia/wallet/nft_wallet/nft_wallet.py +1687 -0
- chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
- chia/wallet/nft_wallet/uncurry_nft.py +217 -0
- chia/wallet/notification_manager.py +117 -0
- chia/wallet/notification_store.py +178 -0
- chia/wallet/outer_puzzles.py +84 -0
- chia/wallet/payment.py +33 -0
- chia/wallet/puzzle_drivers.py +118 -0
- chia/wallet/puzzles/__init__.py +0 -0
- chia/wallet/puzzles/augmented_condition.clsp +13 -0
- chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
- chia/wallet/puzzles/clawback/__init__.py +0 -0
- chia/wallet/puzzles/clawback/drivers.py +188 -0
- chia/wallet/puzzles/clawback/metadata.py +38 -0
- chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
- chia/wallet/puzzles/condition_codes.clib +77 -0
- chia/wallet/puzzles/curry-and-treehash.clib +102 -0
- chia/wallet/puzzles/curry.clib +135 -0
- chia/wallet/puzzles/curry_by_index.clib +16 -0
- chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
- chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp +35 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_lockup.clsp +288 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal.clsp +377 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_treasury.clsp +115 -0
- chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
- chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
- chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
- chia/wallet/puzzles/json.clib +25 -0
- chia/wallet/puzzles/load_clvm.py +161 -0
- chia/wallet/puzzles/merkle_utils.clib +18 -0
- chia/wallet/puzzles/notification.clsp +7 -0
- chia/wallet/puzzles/notification.clsp.hex +1 -0
- chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
- chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.clsp +3 -0
- chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.py +26 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_conditions.py +21 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle.py +34 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +160 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +21 -0
- chia/wallet/puzzles/p2_parent.clsp +19 -0
- chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.py +27 -0
- chia/wallet/puzzles/p2_singleton.clsp +30 -0
- chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/puzzle_utils.py +34 -0
- chia/wallet/puzzles/settlement_payments.clsp +49 -0
- chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
- chia/wallet/puzzles/sha256tree.clib +11 -0
- chia/wallet/puzzles/singleton_launcher.clsp +16 -0
- chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
- chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.py +296 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.py +345 -0
- chia/wallet/puzzles/singleton_truths.clib +21 -0
- chia/wallet/puzzles/tails.py +348 -0
- chia/wallet/puzzles/utility_macros.clib +48 -0
- chia/wallet/signer_protocol.py +125 -0
- chia/wallet/singleton.py +106 -0
- chia/wallet/singleton_record.py +30 -0
- chia/wallet/trade_manager.py +1102 -0
- chia/wallet/trade_record.py +67 -0
- chia/wallet/trading/__init__.py +0 -0
- chia/wallet/trading/offer.py +702 -0
- chia/wallet/trading/trade_status.py +13 -0
- chia/wallet/trading/trade_store.py +526 -0
- chia/wallet/transaction_record.py +158 -0
- chia/wallet/transaction_sorting.py +14 -0
- chia/wallet/uncurried_puzzle.py +17 -0
- chia/wallet/util/__init__.py +0 -0
- chia/wallet/util/address_type.py +55 -0
- chia/wallet/util/blind_signer_tl.py +164 -0
- chia/wallet/util/clvm_streamable.py +203 -0
- chia/wallet/util/compute_hints.py +66 -0
- chia/wallet/util/compute_memos.py +43 -0
- chia/wallet/util/curry_and_treehash.py +91 -0
- chia/wallet/util/debug_spend_bundle.py +232 -0
- chia/wallet/util/merkle_tree.py +100 -0
- chia/wallet/util/merkle_utils.py +102 -0
- chia/wallet/util/new_peak_queue.py +82 -0
- chia/wallet/util/notifications.py +12 -0
- chia/wallet/util/peer_request_cache.py +174 -0
- chia/wallet/util/pprint.py +39 -0
- chia/wallet/util/puzzle_compression.py +95 -0
- chia/wallet/util/puzzle_decorator.py +100 -0
- chia/wallet/util/puzzle_decorator_type.py +7 -0
- chia/wallet/util/query_filter.py +59 -0
- chia/wallet/util/transaction_type.py +23 -0
- chia/wallet/util/tx_config.py +158 -0
- chia/wallet/util/wallet_sync_utils.py +351 -0
- chia/wallet/util/wallet_types.py +72 -0
- chia/wallet/vc_wallet/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_cat_drivers.py +664 -0
- chia/wallet/vc_wallet/cr_cat_wallet.py +877 -0
- chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
- chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_drivers.py +838 -0
- chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_store.py +263 -0
- chia/wallet/vc_wallet/vc_wallet.py +638 -0
- chia/wallet/wallet.py +698 -0
- chia/wallet/wallet_action_scope.py +96 -0
- chia/wallet/wallet_blockchain.py +244 -0
- chia/wallet/wallet_coin_record.py +72 -0
- chia/wallet/wallet_coin_store.py +351 -0
- chia/wallet/wallet_info.py +35 -0
- chia/wallet/wallet_interested_store.py +188 -0
- chia/wallet/wallet_nft_store.py +279 -0
- chia/wallet/wallet_node.py +1765 -0
- chia/wallet/wallet_node_api.py +207 -0
- chia/wallet/wallet_pool_store.py +119 -0
- chia/wallet/wallet_protocol.py +90 -0
- chia/wallet/wallet_puzzle_store.py +396 -0
- chia/wallet/wallet_retry_store.py +70 -0
- chia/wallet/wallet_singleton_store.py +259 -0
- chia/wallet/wallet_spend_bundle.py +25 -0
- chia/wallet/wallet_state_manager.py +2819 -0
- chia/wallet/wallet_transaction_store.py +496 -0
- chia/wallet/wallet_user_store.py +110 -0
- chia/wallet/wallet_weight_proof_handler.py +126 -0
- chia_blockchain-2.5.1rc1.dist-info/LICENSE +201 -0
- chia_blockchain-2.5.1rc1.dist-info/METADATA +156 -0
- chia_blockchain-2.5.1rc1.dist-info/RECORD +1042 -0
- chia_blockchain-2.5.1rc1.dist-info/WHEEL +4 -0
- chia_blockchain-2.5.1rc1.dist-info/entry_points.txt +17 -0
- mozilla-ca/cacert.pem +3611 -0
|
@@ -0,0 +1,2084 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import logging
|
|
5
|
+
from collections.abc import Awaitable, Collection
|
|
6
|
+
from typing import Any, Callable, ClassVar, Optional
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from chia_rs import ELIGIBLE_FOR_DEDUP, ELIGIBLE_FOR_FF, AugSchemeMPL, G2Element, get_conditions_from_spendbundle
|
|
10
|
+
from chiabip158 import PyBIP158
|
|
11
|
+
|
|
12
|
+
from chia._tests.conftest import ConsensusMode
|
|
13
|
+
from chia._tests.util.misc import invariant_check_mempool
|
|
14
|
+
from chia._tests.util.setup_nodes import OldSimulatorsAndWallets, setup_simulators_and_wallets
|
|
15
|
+
from chia.consensus.condition_costs import ConditionCost
|
|
16
|
+
from chia.consensus.constants import ConsensusConstants
|
|
17
|
+
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
18
|
+
from chia.full_node.mempool import MAX_SKIPPED_ITEMS, PRIORITY_TX_THRESHOLD
|
|
19
|
+
from chia.full_node.mempool_check_conditions import mempool_check_time_locks
|
|
20
|
+
from chia.full_node.mempool_manager import (
|
|
21
|
+
MEMPOOL_MIN_FEE_INCREASE,
|
|
22
|
+
QUOTE_BYTES,
|
|
23
|
+
QUOTE_EXECUTION_COST,
|
|
24
|
+
MempoolManager,
|
|
25
|
+
TimelockConditions,
|
|
26
|
+
can_replace,
|
|
27
|
+
compute_assert_height,
|
|
28
|
+
optional_max,
|
|
29
|
+
optional_min,
|
|
30
|
+
)
|
|
31
|
+
from chia.protocols import wallet_protocol
|
|
32
|
+
from chia.protocols.full_node_protocol import RequestBlock, RespondBlock
|
|
33
|
+
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
|
34
|
+
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
35
|
+
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
36
|
+
from chia.types.blockchain_format.coin import Coin
|
|
37
|
+
from chia.types.blockchain_format.program import INFINITE_COST, Program
|
|
38
|
+
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
39
|
+
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
40
|
+
from chia.types.clvm_cost import CLVMCost
|
|
41
|
+
from chia.types.coin_record import CoinRecord
|
|
42
|
+
from chia.types.coin_spend import CoinSpend, make_spend
|
|
43
|
+
from chia.types.condition_opcodes import ConditionOpcode
|
|
44
|
+
from chia.types.eligible_coin_spends import (
|
|
45
|
+
DedupCoinSpend,
|
|
46
|
+
EligibilityAndAdditions,
|
|
47
|
+
EligibleCoinSpends,
|
|
48
|
+
UnspentLineageInfo,
|
|
49
|
+
run_for_cost,
|
|
50
|
+
)
|
|
51
|
+
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
52
|
+
from chia.types.mempool_item import BundleCoinSpend, MempoolItem
|
|
53
|
+
from chia.types.peer_info import PeerInfo
|
|
54
|
+
from chia.types.spend_bundle import SpendBundle
|
|
55
|
+
from chia.types.spend_bundle_conditions import SpendBundleConditions, SpendConditions
|
|
56
|
+
from chia.util.errors import Err, ValidationError
|
|
57
|
+
from chia.util.ints import uint8, uint32, uint64
|
|
58
|
+
from chia.wallet.conditions import AssertCoinAnnouncement
|
|
59
|
+
from chia.wallet.payment import Payment
|
|
60
|
+
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
|
|
61
|
+
from chia.wallet.wallet import Wallet
|
|
62
|
+
from chia.wallet.wallet_coin_record import WalletCoinRecord
|
|
63
|
+
from chia.wallet.wallet_node import WalletNode
|
|
64
|
+
|
|
65
|
+
IDENTITY_PUZZLE = SerializedProgram.to(1)
|
|
66
|
+
IDENTITY_PUZZLE_HASH = IDENTITY_PUZZLE.get_tree_hash()
|
|
67
|
+
|
|
68
|
+
TEST_TIMESTAMP = uint64(10040)
|
|
69
|
+
TEST_COIN_AMOUNT = uint64(1000000000)
|
|
70
|
+
TEST_COIN = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT)
|
|
71
|
+
TEST_COIN_ID = TEST_COIN.name()
|
|
72
|
+
TEST_COIN_RECORD = CoinRecord(TEST_COIN, uint32(0), uint32(0), False, TEST_TIMESTAMP)
|
|
73
|
+
TEST_COIN_AMOUNT2 = uint64(2000000000)
|
|
74
|
+
TEST_COIN2 = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT2)
|
|
75
|
+
TEST_COIN_ID2 = TEST_COIN2.name()
|
|
76
|
+
TEST_COIN_RECORD2 = CoinRecord(TEST_COIN2, uint32(0), uint32(0), False, TEST_TIMESTAMP)
|
|
77
|
+
TEST_COIN_AMOUNT3 = uint64(3000000000)
|
|
78
|
+
TEST_COIN3 = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT3)
|
|
79
|
+
TEST_COIN_ID3 = TEST_COIN3.name()
|
|
80
|
+
TEST_COIN_RECORD3 = CoinRecord(TEST_COIN3, uint32(0), uint32(0), False, TEST_TIMESTAMP)
|
|
81
|
+
TEST_HEIGHT = uint32(5)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclasses.dataclass(frozen=True)
|
|
85
|
+
class TestBlockRecord:
|
|
86
|
+
"""
|
|
87
|
+
This is a subset of BlockRecord that the mempool manager uses for peak.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
header_hash: bytes32
|
|
91
|
+
height: uint32
|
|
92
|
+
timestamp: Optional[uint64]
|
|
93
|
+
prev_transaction_block_height: uint32
|
|
94
|
+
prev_transaction_block_hash: Optional[bytes32]
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def is_transaction_block(self) -> bool:
|
|
98
|
+
return self.timestamp is not None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
async def zero_calls_get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
102
|
+
assert len(coin_ids) == 0
|
|
103
|
+
return []
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async def get_coin_records_for_test_coins(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
107
|
+
test_coin_records = {
|
|
108
|
+
TEST_COIN_ID: TEST_COIN_RECORD,
|
|
109
|
+
TEST_COIN_ID2: TEST_COIN_RECORD2,
|
|
110
|
+
TEST_COIN_ID3: TEST_COIN_RECORD3,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ret: list[CoinRecord] = []
|
|
114
|
+
for name in coin_ids:
|
|
115
|
+
r = test_coin_records.get(name)
|
|
116
|
+
if r is not None:
|
|
117
|
+
ret.append(r)
|
|
118
|
+
return ret
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def height_hash(height: int) -> bytes32:
|
|
122
|
+
return bytes32(height.to_bytes(32, byteorder="big"))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def create_test_block_record(*, height: uint32 = TEST_HEIGHT, timestamp: uint64 = TEST_TIMESTAMP) -> TestBlockRecord:
|
|
126
|
+
return TestBlockRecord(
|
|
127
|
+
header_hash=height_hash(height),
|
|
128
|
+
height=height,
|
|
129
|
+
timestamp=timestamp,
|
|
130
|
+
prev_transaction_block_height=uint32(height - 1),
|
|
131
|
+
prev_transaction_block_hash=height_hash(height - 1),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
async def instantiate_mempool_manager(
|
|
136
|
+
get_coin_records: Callable[[Collection[bytes32]], Awaitable[list[CoinRecord]]],
|
|
137
|
+
*,
|
|
138
|
+
block_height: uint32 = TEST_HEIGHT,
|
|
139
|
+
block_timestamp: uint64 = TEST_TIMESTAMP,
|
|
140
|
+
constants: ConsensusConstants = DEFAULT_CONSTANTS,
|
|
141
|
+
max_tx_clvm_cost: Optional[uint64] = None,
|
|
142
|
+
) -> MempoolManager:
|
|
143
|
+
mempool_manager = MempoolManager(get_coin_records, constants, max_tx_clvm_cost=max_tx_clvm_cost)
|
|
144
|
+
test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
|
|
145
|
+
await mempool_manager.new_peak(test_block_record, None)
|
|
146
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
147
|
+
return mempool_manager
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
async def setup_mempool_with_coins(
|
|
151
|
+
*,
|
|
152
|
+
coin_amounts: list[int],
|
|
153
|
+
max_block_clvm_cost: Optional[int] = None,
|
|
154
|
+
max_tx_clvm_cost: Optional[uint64] = None,
|
|
155
|
+
mempool_block_buffer: Optional[int] = None,
|
|
156
|
+
) -> tuple[MempoolManager, list[Coin]]:
|
|
157
|
+
coins = []
|
|
158
|
+
test_coin_records = {}
|
|
159
|
+
for amount in coin_amounts:
|
|
160
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount))
|
|
161
|
+
coins.append(coin)
|
|
162
|
+
test_coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
163
|
+
|
|
164
|
+
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
165
|
+
ret: list[CoinRecord] = []
|
|
166
|
+
for name in coin_ids:
|
|
167
|
+
r = test_coin_records.get(name)
|
|
168
|
+
if r is not None:
|
|
169
|
+
ret.append(r)
|
|
170
|
+
return ret
|
|
171
|
+
|
|
172
|
+
constants = DEFAULT_CONSTANTS
|
|
173
|
+
if max_block_clvm_cost is not None:
|
|
174
|
+
constants = constants.replace(MAX_BLOCK_COST_CLVM=uint64(max_block_clvm_cost + TEST_BLOCK_OVERHEAD))
|
|
175
|
+
if mempool_block_buffer is not None:
|
|
176
|
+
constants = constants.replace(MEMPOOL_BLOCK_BUFFER=uint8(mempool_block_buffer))
|
|
177
|
+
mempool_manager = await instantiate_mempool_manager(
|
|
178
|
+
get_coin_records, constants=constants, max_tx_clvm_cost=max_tx_clvm_cost
|
|
179
|
+
)
|
|
180
|
+
return (mempool_manager, coins)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def make_test_conds(
|
|
184
|
+
*,
|
|
185
|
+
birth_height: Optional[int] = None,
|
|
186
|
+
birth_seconds: Optional[int] = None,
|
|
187
|
+
height_relative: Optional[int] = None,
|
|
188
|
+
height_absolute: int = 0,
|
|
189
|
+
seconds_relative: Optional[int] = None,
|
|
190
|
+
seconds_absolute: int = 0,
|
|
191
|
+
before_height_relative: Optional[int] = None,
|
|
192
|
+
before_height_absolute: Optional[int] = None,
|
|
193
|
+
before_seconds_relative: Optional[int] = None,
|
|
194
|
+
before_seconds_absolute: Optional[int] = None,
|
|
195
|
+
cost: int = 0,
|
|
196
|
+
spend_ids: list[bytes32] = [TEST_COIN_ID],
|
|
197
|
+
) -> SpendBundleConditions:
|
|
198
|
+
return SpendBundleConditions(
|
|
199
|
+
[
|
|
200
|
+
SpendConditions(
|
|
201
|
+
spend_id,
|
|
202
|
+
IDENTITY_PUZZLE_HASH,
|
|
203
|
+
IDENTITY_PUZZLE_HASH,
|
|
204
|
+
TEST_COIN_AMOUNT,
|
|
205
|
+
None if height_relative is None else uint32(height_relative),
|
|
206
|
+
None if seconds_relative is None else uint64(seconds_relative),
|
|
207
|
+
None if before_height_relative is None else uint32(before_height_relative),
|
|
208
|
+
None if before_seconds_relative is None else uint64(before_seconds_relative),
|
|
209
|
+
None if birth_height is None else uint32(birth_height),
|
|
210
|
+
None if birth_seconds is None else uint64(birth_seconds),
|
|
211
|
+
[],
|
|
212
|
+
[],
|
|
213
|
+
[],
|
|
214
|
+
[],
|
|
215
|
+
[],
|
|
216
|
+
[],
|
|
217
|
+
[],
|
|
218
|
+
[],
|
|
219
|
+
0,
|
|
220
|
+
)
|
|
221
|
+
for spend_id in spend_ids
|
|
222
|
+
],
|
|
223
|
+
0,
|
|
224
|
+
uint32(height_absolute),
|
|
225
|
+
uint64(seconds_absolute),
|
|
226
|
+
None if before_height_absolute is None else uint32(before_height_absolute),
|
|
227
|
+
None if before_seconds_absolute is None else uint64(before_seconds_absolute),
|
|
228
|
+
[],
|
|
229
|
+
cost,
|
|
230
|
+
0,
|
|
231
|
+
0,
|
|
232
|
+
False,
|
|
233
|
+
0,
|
|
234
|
+
0,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class TestCheckTimeLocks:
|
|
239
|
+
COIN_CONFIRMED_HEIGHT: ClassVar[uint32] = uint32(10)
|
|
240
|
+
COIN_TIMESTAMP: ClassVar[uint64] = uint64(10000)
|
|
241
|
+
PREV_BLOCK_HEIGHT: ClassVar[uint32] = uint32(15)
|
|
242
|
+
PREV_BLOCK_TIMESTAMP: ClassVar[uint64] = uint64(10150)
|
|
243
|
+
|
|
244
|
+
COIN_RECORD: ClassVar[CoinRecord] = CoinRecord(
|
|
245
|
+
TEST_COIN,
|
|
246
|
+
confirmed_block_index=uint32(COIN_CONFIRMED_HEIGHT),
|
|
247
|
+
spent_block_index=uint32(0),
|
|
248
|
+
coinbase=False,
|
|
249
|
+
timestamp=COIN_TIMESTAMP,
|
|
250
|
+
)
|
|
251
|
+
REMOVALS: ClassVar[dict[bytes32, CoinRecord]] = {TEST_COIN.name(): COIN_RECORD}
|
|
252
|
+
|
|
253
|
+
@pytest.mark.parametrize(
|
|
254
|
+
"conds,expected",
|
|
255
|
+
[
|
|
256
|
+
(make_test_conds(height_relative=5), None),
|
|
257
|
+
(make_test_conds(height_relative=6), Err.ASSERT_HEIGHT_RELATIVE_FAILED),
|
|
258
|
+
(make_test_conds(height_absolute=PREV_BLOCK_HEIGHT), None),
|
|
259
|
+
(make_test_conds(height_absolute=uint32(PREV_BLOCK_HEIGHT + 1)), Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
|
|
260
|
+
(make_test_conds(seconds_relative=150), None),
|
|
261
|
+
(make_test_conds(seconds_relative=151), Err.ASSERT_SECONDS_RELATIVE_FAILED),
|
|
262
|
+
(make_test_conds(seconds_absolute=PREV_BLOCK_TIMESTAMP), None),
|
|
263
|
+
(make_test_conds(seconds_absolute=uint64(PREV_BLOCK_TIMESTAMP + 1)), Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
|
|
264
|
+
# the coin's confirmed height is 10
|
|
265
|
+
(make_test_conds(birth_height=9), Err.ASSERT_MY_BIRTH_HEIGHT_FAILED),
|
|
266
|
+
(make_test_conds(birth_height=10), None),
|
|
267
|
+
(make_test_conds(birth_height=11), Err.ASSERT_MY_BIRTH_HEIGHT_FAILED),
|
|
268
|
+
# coin timestamp is 10000
|
|
269
|
+
(make_test_conds(birth_seconds=uint64(COIN_TIMESTAMP - 1)), Err.ASSERT_MY_BIRTH_SECONDS_FAILED),
|
|
270
|
+
(make_test_conds(birth_seconds=COIN_TIMESTAMP), None),
|
|
271
|
+
(make_test_conds(birth_seconds=uint64(COIN_TIMESTAMP + 1)), Err.ASSERT_MY_BIRTH_SECONDS_FAILED),
|
|
272
|
+
# the coin is 5 blocks old in this test
|
|
273
|
+
(make_test_conds(before_height_relative=5), Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
|
|
274
|
+
(make_test_conds(before_height_relative=6), None),
|
|
275
|
+
# The block height is 15
|
|
276
|
+
(make_test_conds(before_height_absolute=PREV_BLOCK_HEIGHT), Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
|
|
277
|
+
(make_test_conds(before_height_absolute=uint64(PREV_BLOCK_HEIGHT + 1)), None),
|
|
278
|
+
# the coin is 150 seconds old in this test
|
|
279
|
+
(make_test_conds(before_seconds_relative=150), Err.ASSERT_BEFORE_SECONDS_RELATIVE_FAILED),
|
|
280
|
+
(make_test_conds(before_seconds_relative=151), None),
|
|
281
|
+
# The block timestamp is 10150
|
|
282
|
+
(make_test_conds(before_seconds_absolute=PREV_BLOCK_TIMESTAMP), Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
|
|
283
|
+
(make_test_conds(before_seconds_absolute=uint64(PREV_BLOCK_TIMESTAMP + 1)), None),
|
|
284
|
+
],
|
|
285
|
+
)
|
|
286
|
+
def test_conditions(
|
|
287
|
+
self,
|
|
288
|
+
conds: SpendBundleConditions,
|
|
289
|
+
expected: Optional[Err],
|
|
290
|
+
) -> None:
|
|
291
|
+
assert (
|
|
292
|
+
mempool_check_time_locks(
|
|
293
|
+
dict(self.REMOVALS),
|
|
294
|
+
conds,
|
|
295
|
+
self.PREV_BLOCK_HEIGHT,
|
|
296
|
+
self.PREV_BLOCK_TIMESTAMP,
|
|
297
|
+
)
|
|
298
|
+
== expected
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def expect(
|
|
303
|
+
*, height: int = 0, seconds: int = 0, before_height: Optional[int] = None, before_seconds: Optional[int] = None
|
|
304
|
+
) -> TimelockConditions:
|
|
305
|
+
ret = TimelockConditions(uint32(height), uint64(seconds))
|
|
306
|
+
if before_height is not None:
|
|
307
|
+
ret.assert_before_height = uint32(before_height)
|
|
308
|
+
if before_seconds is not None:
|
|
309
|
+
ret.assert_before_seconds = uint64(before_seconds)
|
|
310
|
+
return ret
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@pytest.mark.parametrize(
|
|
314
|
+
"conds,expected",
|
|
315
|
+
[
|
|
316
|
+
# ASSERT_HEIGHT_*
|
|
317
|
+
# coin birth height is 12
|
|
318
|
+
(make_test_conds(), expect()),
|
|
319
|
+
(make_test_conds(height_absolute=42), expect(height=42)),
|
|
320
|
+
# 1 is a relative height, but that only amounts to 13, so the absolute
|
|
321
|
+
# height is more restrictive
|
|
322
|
+
(make_test_conds(height_relative=1), expect(height=13)),
|
|
323
|
+
# 100 is a relative height, and since the coin was confirmed at height 12,
|
|
324
|
+
# that's 112
|
|
325
|
+
(make_test_conds(height_absolute=42, height_relative=100), expect(height=112)),
|
|
326
|
+
# Same thing but without the absolute height
|
|
327
|
+
(make_test_conds(height_relative=100), expect(height=112)),
|
|
328
|
+
(make_test_conds(height_relative=0), expect(height=12)),
|
|
329
|
+
# 42 is more restrictive than 13
|
|
330
|
+
(make_test_conds(height_absolute=42, height_relative=1), expect(height=42)),
|
|
331
|
+
# ASSERT_BEFORE_HEIGHT_*
|
|
332
|
+
(make_test_conds(before_height_absolute=100), expect(before_height=100)),
|
|
333
|
+
# coin is created at 12 + 1 relative height = 13
|
|
334
|
+
(make_test_conds(before_height_relative=1), expect(before_height=13)),
|
|
335
|
+
# coin is created at 12 + 0 relative height = 12
|
|
336
|
+
(make_test_conds(before_height_relative=0), expect(before_height=12)),
|
|
337
|
+
# 13 is more restrictive than 42
|
|
338
|
+
(make_test_conds(before_height_absolute=42, before_height_relative=1), expect(before_height=13)),
|
|
339
|
+
# 100 is a relative height, and since the coin was confirmed at height 12,
|
|
340
|
+
# that's 112
|
|
341
|
+
(make_test_conds(before_height_absolute=200, before_height_relative=100), expect(before_height=112)),
|
|
342
|
+
# Same thing but without the absolute height
|
|
343
|
+
(make_test_conds(before_height_relative=100), expect(before_height=112)),
|
|
344
|
+
# ASSERT_BEFORE_SECONDS_*
|
|
345
|
+
# coin timestamp is 10000
|
|
346
|
+
# single absolute assert before seconds
|
|
347
|
+
(make_test_conds(before_seconds_absolute=20000), expect(before_seconds=20000)),
|
|
348
|
+
# coin is created at 10000 + 100 relative seconds = 10100
|
|
349
|
+
(make_test_conds(before_seconds_relative=100), expect(before_seconds=10100)),
|
|
350
|
+
# coin is created at 10000 + 0 relative seconds = 10000
|
|
351
|
+
(make_test_conds(before_seconds_relative=0), expect(before_seconds=10000)),
|
|
352
|
+
# 10100 is more restrictive than 20000
|
|
353
|
+
(make_test_conds(before_seconds_absolute=20000, before_seconds_relative=100), expect(before_seconds=10100)),
|
|
354
|
+
# 20000 is a relative seconds, and since the coin was confirmed at seconds
|
|
355
|
+
# 10000 that's 300000
|
|
356
|
+
(make_test_conds(before_seconds_absolute=20000, before_seconds_relative=20000), expect(before_seconds=20000)),
|
|
357
|
+
# Same thing but without the absolute seconds
|
|
358
|
+
(make_test_conds(before_seconds_relative=20000), expect(before_seconds=30000)),
|
|
359
|
+
# ASSERT_SECONDS_*
|
|
360
|
+
# coin timestamp is 10000
|
|
361
|
+
# single absolute assert seconds
|
|
362
|
+
(make_test_conds(seconds_absolute=20000), expect(seconds=20000)),
|
|
363
|
+
# coin is created at 10000 + 100 relative seconds = 10100
|
|
364
|
+
(make_test_conds(seconds_relative=100), expect(seconds=10100)),
|
|
365
|
+
# coin is created at 10000 + 0 relative seconds = 10000
|
|
366
|
+
(make_test_conds(seconds_relative=0), expect(seconds=10000)),
|
|
367
|
+
# 20000 is more restrictive than 10100
|
|
368
|
+
(make_test_conds(seconds_absolute=20000, seconds_relative=100), expect(seconds=20000)),
|
|
369
|
+
# 20000 is a relative seconds, and since the coin was confirmed at seconds
|
|
370
|
+
# 10000 that's 300000
|
|
371
|
+
(make_test_conds(seconds_absolute=20000, seconds_relative=20000), expect(seconds=30000)),
|
|
372
|
+
# Same thing but without the absolute seconds
|
|
373
|
+
(make_test_conds(seconds_relative=20000), expect(seconds=30000)),
|
|
374
|
+
],
|
|
375
|
+
)
|
|
376
|
+
def test_compute_assert_height(conds: SpendBundleConditions, expected: TimelockConditions) -> None:
|
|
377
|
+
coin_id = TEST_COIN.name()
|
|
378
|
+
|
|
379
|
+
confirmed_height = uint32(12)
|
|
380
|
+
coin_records = {coin_id: CoinRecord(TEST_COIN, confirmed_height, uint32(0), False, uint64(10000))}
|
|
381
|
+
|
|
382
|
+
assert compute_assert_height(coin_records, conds) == expected
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def spend_bundle_from_conditions(
|
|
386
|
+
conditions: list[list[Any]], coin: Coin = TEST_COIN, aggsig: G2Element = G2Element()
|
|
387
|
+
) -> SpendBundle:
|
|
388
|
+
solution = SerializedProgram.to(conditions)
|
|
389
|
+
coin_spend = make_spend(coin, IDENTITY_PUZZLE, solution)
|
|
390
|
+
return SpendBundle([coin_spend], aggsig)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
async def add_spendbundle(
|
|
394
|
+
mempool_manager: MempoolManager, sb: SpendBundle, sb_name: bytes32
|
|
395
|
+
) -> tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]:
|
|
396
|
+
sbc = await mempool_manager.pre_validate_spendbundle(sb, sb_name)
|
|
397
|
+
ret = await mempool_manager.add_spend_bundle(sb, sbc, sb_name, TEST_HEIGHT)
|
|
398
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
399
|
+
return ret.cost, ret.status, ret.error
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
async def generate_and_add_spendbundle(
|
|
403
|
+
mempool_manager: MempoolManager,
|
|
404
|
+
conditions: list[list[Any]],
|
|
405
|
+
coin: Coin = TEST_COIN,
|
|
406
|
+
aggsig: G2Element = G2Element(),
|
|
407
|
+
) -> tuple[SpendBundle, bytes32, tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]]:
|
|
408
|
+
sb = spend_bundle_from_conditions(conditions, coin, aggsig)
|
|
409
|
+
sb_name = sb.name()
|
|
410
|
+
result = await add_spendbundle(mempool_manager, sb, sb_name)
|
|
411
|
+
return (sb, sb_name, result)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def make_bundle_spends_map_and_fee(
|
|
415
|
+
spend_bundle: SpendBundle, conds: SpendBundleConditions
|
|
416
|
+
) -> tuple[dict[bytes32, BundleCoinSpend], uint64]:
|
|
417
|
+
bundle_coin_spends: dict[bytes32, BundleCoinSpend] = {}
|
|
418
|
+
eligibility_and_additions: dict[bytes32, EligibilityAndAdditions] = {}
|
|
419
|
+
removals_amount = 0
|
|
420
|
+
additions_amount = 0
|
|
421
|
+
for spend in conds.spends:
|
|
422
|
+
coin_id = bytes32(spend.coin_id)
|
|
423
|
+
spend_additions = []
|
|
424
|
+
for puzzle_hash, amount, _ in spend.create_coin:
|
|
425
|
+
spend_additions.append(Coin(coin_id, puzzle_hash, uint64(amount)))
|
|
426
|
+
additions_amount += amount
|
|
427
|
+
eligibility_and_additions[coin_id] = EligibilityAndAdditions(
|
|
428
|
+
is_eligible_for_dedup=bool(spend.flags & ELIGIBLE_FOR_DEDUP),
|
|
429
|
+
spend_additions=spend_additions,
|
|
430
|
+
is_eligible_for_ff=bool(spend.flags & ELIGIBLE_FOR_FF),
|
|
431
|
+
)
|
|
432
|
+
for coin_spend in spend_bundle.coin_spends:
|
|
433
|
+
coin_id = coin_spend.coin.name()
|
|
434
|
+
removals_amount += coin_spend.coin.amount
|
|
435
|
+
eligibility_info = eligibility_and_additions.get(
|
|
436
|
+
coin_id, EligibilityAndAdditions(is_eligible_for_dedup=False, spend_additions=[], is_eligible_for_ff=False)
|
|
437
|
+
)
|
|
438
|
+
bundle_coin_spends[coin_id] = BundleCoinSpend(
|
|
439
|
+
coin_spend=coin_spend,
|
|
440
|
+
eligible_for_dedup=eligibility_info.is_eligible_for_dedup,
|
|
441
|
+
eligible_for_fast_forward=eligibility_info.is_eligible_for_ff,
|
|
442
|
+
additions=eligibility_info.spend_additions,
|
|
443
|
+
)
|
|
444
|
+
fee = uint64(removals_amount - additions_amount)
|
|
445
|
+
return bundle_coin_spends, fee
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem:
|
|
449
|
+
conds = get_conditions_from_spendbundle(spend_bundle, INFINITE_COST, DEFAULT_CONSTANTS, uint32(0))
|
|
450
|
+
bundle_coin_spends, fee = make_bundle_spends_map_and_fee(spend_bundle, conds)
|
|
451
|
+
return MempoolItem(
|
|
452
|
+
spend_bundle=spend_bundle,
|
|
453
|
+
fee=fee,
|
|
454
|
+
conds=conds,
|
|
455
|
+
spend_bundle_name=spend_bundle.name(),
|
|
456
|
+
height_added_to_mempool=TEST_HEIGHT,
|
|
457
|
+
bundle_coin_spends=bundle_coin_spends,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@pytest.mark.anyio
|
|
462
|
+
async def test_empty_spend_bundle() -> None:
|
|
463
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
464
|
+
sb = SpendBundle([], G2Element())
|
|
465
|
+
with pytest.raises(ValidationError, match="INVALID_SPEND_BUNDLE"):
|
|
466
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@pytest.mark.anyio
|
|
470
|
+
async def test_negative_addition_amount() -> None:
|
|
471
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
472
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, -1]]
|
|
473
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
474
|
+
with pytest.raises(ValidationError, match="COIN_AMOUNT_NEGATIVE"):
|
|
475
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@pytest.mark.anyio
|
|
479
|
+
async def test_valid_addition_amount() -> None:
|
|
480
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
481
|
+
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
|
|
482
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount]]
|
|
483
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, max_amount)
|
|
484
|
+
sb = spend_bundle_from_conditions(conditions, coin)
|
|
485
|
+
# ensure this does not throw
|
|
486
|
+
_ = await mempool_manager.pre_validate_spendbundle(sb)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
@pytest.mark.anyio
|
|
490
|
+
async def test_too_big_addition_amount() -> None:
|
|
491
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
492
|
+
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
|
|
493
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount + 1]]
|
|
494
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
495
|
+
with pytest.raises(ValidationError, match="COIN_AMOUNT_EXCEEDS_MAXIMUM"):
|
|
496
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@pytest.mark.anyio
|
|
500
|
+
async def test_duplicate_output() -> None:
|
|
501
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
502
|
+
conditions = [
|
|
503
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
504
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
505
|
+
]
|
|
506
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
507
|
+
with pytest.raises(ValidationError, match="DUPLICATE_OUTPUT"):
|
|
508
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@pytest.mark.anyio
|
|
512
|
+
async def test_block_cost_exceeds_max() -> None:
|
|
513
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
514
|
+
conditions = []
|
|
515
|
+
for i in range(2400):
|
|
516
|
+
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i])
|
|
517
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
518
|
+
with pytest.raises(ValidationError, match="BLOCK_COST_EXCEEDS_MAX"):
|
|
519
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@pytest.mark.anyio
|
|
523
|
+
async def test_double_spend_prevalidation() -> None:
|
|
524
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
525
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
526
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
527
|
+
sb_twice = SpendBundle.aggregate([sb, sb])
|
|
528
|
+
with pytest.raises(ValidationError, match="DOUBLE_SPEND"):
|
|
529
|
+
await mempool_manager.pre_validate_spendbundle(sb_twice)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
@pytest.mark.anyio
|
|
533
|
+
async def test_minting_coin() -> None:
|
|
534
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
535
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT]]
|
|
536
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
537
|
+
_ = await mempool_manager.pre_validate_spendbundle(sb)
|
|
538
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT + 1]]
|
|
539
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
540
|
+
with pytest.raises(ValidationError, match="MINTING_COIN"):
|
|
541
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
@pytest.mark.anyio
|
|
545
|
+
async def test_reserve_fee_condition() -> None:
|
|
546
|
+
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
|
|
547
|
+
conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT]]
|
|
548
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
549
|
+
_ = await mempool_manager.pre_validate_spendbundle(sb)
|
|
550
|
+
conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT + 1]]
|
|
551
|
+
sb = spend_bundle_from_conditions(conditions)
|
|
552
|
+
with pytest.raises(ValidationError, match="RESERVE_FEE_CONDITION_FAILED"):
|
|
553
|
+
await mempool_manager.pre_validate_spendbundle(sb)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@pytest.mark.anyio
|
|
557
|
+
async def test_unknown_unspent() -> None:
|
|
558
|
+
async def get_coin_records(_: Collection[bytes32]) -> list[CoinRecord]:
|
|
559
|
+
return []
|
|
560
|
+
|
|
561
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records)
|
|
562
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
563
|
+
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
|
|
564
|
+
assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
@pytest.mark.anyio
|
|
568
|
+
async def test_same_sb_twice_with_eligible_coin() -> None:
|
|
569
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
|
|
570
|
+
sb1_conditions = [
|
|
571
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
572
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
573
|
+
]
|
|
574
|
+
sb1 = spend_bundle_from_conditions(sb1_conditions)
|
|
575
|
+
sk = AugSchemeMPL.key_gen(b"5" * 32)
|
|
576
|
+
g1 = sk.get_g1()
|
|
577
|
+
sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
|
|
578
|
+
sb2_conditions = [
|
|
579
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3],
|
|
580
|
+
[ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH],
|
|
581
|
+
]
|
|
582
|
+
sb2 = spend_bundle_from_conditions(sb2_conditions, TEST_COIN2, sig)
|
|
583
|
+
sb = SpendBundle.aggregate([sb1, sb2])
|
|
584
|
+
sb_name = sb.name()
|
|
585
|
+
result = await add_spendbundle(mempool_manager, sb, sb_name)
|
|
586
|
+
expected_cost = uint64(10_236_088)
|
|
587
|
+
assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
|
|
588
|
+
assert mempool_manager.get_spendbundle(sb_name) == sb
|
|
589
|
+
result = await add_spendbundle(mempool_manager, sb, sb_name)
|
|
590
|
+
assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
|
|
591
|
+
assert mempool_manager.get_spendbundle(sb_name) == sb
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
@pytest.mark.anyio
|
|
595
|
+
async def test_sb_twice_with_eligible_coin_and_different_spends_order() -> None:
|
|
596
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
|
|
597
|
+
sb1_conditions = [
|
|
598
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
599
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
600
|
+
]
|
|
601
|
+
sb1 = spend_bundle_from_conditions(sb1_conditions)
|
|
602
|
+
sk = AugSchemeMPL.key_gen(b"6" * 32)
|
|
603
|
+
g1 = sk.get_g1()
|
|
604
|
+
sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
|
|
605
|
+
sb2_conditions: list[list[Any]] = [
|
|
606
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3],
|
|
607
|
+
[ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), IDENTITY_PUZZLE_HASH],
|
|
608
|
+
]
|
|
609
|
+
sb2 = spend_bundle_from_conditions(sb2_conditions, TEST_COIN2, sig)
|
|
610
|
+
sb3_conditions = [[ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), IDENTITY_PUZZLE_HASH]]
|
|
611
|
+
sb3 = spend_bundle_from_conditions(sb3_conditions, TEST_COIN3, sig)
|
|
612
|
+
sb = SpendBundle.aggregate([sb1, sb2, sb3])
|
|
613
|
+
sb_name = sb.name()
|
|
614
|
+
reordered_sb = SpendBundle.aggregate([sb3, sb1, sb2])
|
|
615
|
+
reordered_sb_name = reordered_sb.name()
|
|
616
|
+
assert mempool_manager.get_spendbundle(sb_name) is None
|
|
617
|
+
assert mempool_manager.get_spendbundle(reordered_sb_name) is None
|
|
618
|
+
result = await add_spendbundle(mempool_manager, sb, sb_name)
|
|
619
|
+
expected_cost = uint64(13_056_132)
|
|
620
|
+
assert result == (expected_cost, MempoolInclusionStatus.SUCCESS, None)
|
|
621
|
+
assert mempool_manager.get_spendbundle(sb_name) == sb
|
|
622
|
+
assert mempool_manager.get_spendbundle(reordered_sb_name) is None
|
|
623
|
+
# This reordered spend bundle should generate conflicting coin spends with
|
|
624
|
+
# the previously added spend bundle
|
|
625
|
+
result = await add_spendbundle(mempool_manager, reordered_sb, reordered_sb_name)
|
|
626
|
+
assert result == (expected_cost, MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
627
|
+
assert mempool_manager.get_spendbundle(sb_name) == sb
|
|
628
|
+
assert mempool_manager.get_spendbundle(reordered_sb_name) is None
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
co = ConditionOpcode
|
|
632
|
+
mis = MempoolInclusionStatus
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
@pytest.mark.anyio
|
|
636
|
+
@pytest.mark.parametrize(
|
|
637
|
+
"opcode,lock_value,expected_status,expected_error",
|
|
638
|
+
[
|
|
639
|
+
# the mempool rules don't allow relative height- or time conditions on
|
|
640
|
+
# ephemeral spends
|
|
641
|
+
# SECONDS RELATIVE
|
|
642
|
+
(co.ASSERT_SECONDS_RELATIVE, -2, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
643
|
+
(co.ASSERT_SECONDS_RELATIVE, -1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
644
|
+
(co.ASSERT_SECONDS_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
645
|
+
(co.ASSERT_SECONDS_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
646
|
+
(co.ASSERT_SECONDS_RELATIVE, 9, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
647
|
+
(co.ASSERT_SECONDS_RELATIVE, 10, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
648
|
+
# HEIGHT RELATIVE
|
|
649
|
+
(co.ASSERT_HEIGHT_RELATIVE, -2, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
650
|
+
(co.ASSERT_HEIGHT_RELATIVE, -1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
651
|
+
(co.ASSERT_HEIGHT_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
652
|
+
(co.ASSERT_HEIGHT_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
653
|
+
(co.ASSERT_HEIGHT_RELATIVE, 5, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
654
|
+
(co.ASSERT_HEIGHT_RELATIVE, 6, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
655
|
+
(co.ASSERT_HEIGHT_RELATIVE, 7, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
656
|
+
(co.ASSERT_HEIGHT_RELATIVE, 10, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
657
|
+
(co.ASSERT_HEIGHT_RELATIVE, 11, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
658
|
+
# BEFORE HEIGHT RELATIVE
|
|
659
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, -2, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
|
|
660
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, -1, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_RELATIVE_FAILED),
|
|
661
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 0, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
662
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 1, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
663
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 5, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
664
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 6, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
665
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 7, mis.FAILED, Err.EPHEMERAL_RELATIVE_CONDITION),
|
|
666
|
+
# HEIGHT ABSOLUTE
|
|
667
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 4, mis.SUCCESS, None),
|
|
668
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 5, mis.SUCCESS, None),
|
|
669
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 6, mis.PENDING, Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
|
|
670
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 7, mis.PENDING, Err.ASSERT_HEIGHT_ABSOLUTE_FAILED),
|
|
671
|
+
# BEFORE HEIGHT ABSOLUTE
|
|
672
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 4, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
|
|
673
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 5, mis.FAILED, Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED),
|
|
674
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 6, mis.SUCCESS, None),
|
|
675
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 7, mis.SUCCESS, None),
|
|
676
|
+
# SECONDS ABSOLUTE
|
|
677
|
+
# Current block timestamp is 10050
|
|
678
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10049, mis.SUCCESS, None),
|
|
679
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10050, mis.SUCCESS, None),
|
|
680
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10051, mis.FAILED, Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
|
|
681
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10052, mis.FAILED, Err.ASSERT_SECONDS_ABSOLUTE_FAILED),
|
|
682
|
+
# BEFORE SECONDS ABSOLUTE
|
|
683
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10049, mis.FAILED, Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
|
|
684
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10050, mis.FAILED, Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED),
|
|
685
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10051, mis.SUCCESS, None),
|
|
686
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10052, mis.SUCCESS, None),
|
|
687
|
+
],
|
|
688
|
+
)
|
|
689
|
+
async def test_ephemeral_timelock(
|
|
690
|
+
opcode: ConditionOpcode,
|
|
691
|
+
lock_value: int,
|
|
692
|
+
expected_status: MempoolInclusionStatus,
|
|
693
|
+
expected_error: Optional[Err],
|
|
694
|
+
) -> None:
|
|
695
|
+
mempool_manager = await instantiate_mempool_manager(
|
|
696
|
+
get_coin_records=get_coin_records_for_test_coins,
|
|
697
|
+
block_height=uint32(5),
|
|
698
|
+
block_timestamp=uint64(10050),
|
|
699
|
+
constants=DEFAULT_CONSTANTS,
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
703
|
+
created_coin = Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))
|
|
704
|
+
sb1 = spend_bundle_from_conditions(conditions)
|
|
705
|
+
sb2 = spend_bundle_from_conditions([[opcode, lock_value]], created_coin)
|
|
706
|
+
# sb spends TEST_COIN and creates created_coin which gets spent too
|
|
707
|
+
sb = SpendBundle.aggregate([sb1, sb2])
|
|
708
|
+
# We shouldn't have a record of this ephemeral coin
|
|
709
|
+
assert await get_coin_records_for_test_coins([created_coin.name()]) == []
|
|
710
|
+
try:
|
|
711
|
+
_, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
712
|
+
assert (status, error) == (expected_status, expected_error)
|
|
713
|
+
except ValidationError as e:
|
|
714
|
+
assert expected_status == mis.FAILED
|
|
715
|
+
assert expected_error == e.code
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def test_optional_min() -> None:
|
|
719
|
+
assert optional_min(uint32(100), None) == uint32(100)
|
|
720
|
+
assert optional_min(None, uint32(100)) == uint32(100)
|
|
721
|
+
assert optional_min(None, None) is None
|
|
722
|
+
assert optional_min(uint32(123), uint32(234)) == uint32(123)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def test_optional_max() -> None:
|
|
726
|
+
assert optional_max(uint32(100), None) == uint32(100)
|
|
727
|
+
assert optional_max(None, uint32(100)) == uint32(100)
|
|
728
|
+
assert optional_max(None, None) is None
|
|
729
|
+
assert optional_max(uint32(123), uint32(234)) == uint32(234)
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
def mk_item(
|
|
733
|
+
coins: list[Coin],
|
|
734
|
+
*,
|
|
735
|
+
cost: int = 1,
|
|
736
|
+
fee: int = 0,
|
|
737
|
+
assert_height: Optional[int] = None,
|
|
738
|
+
assert_before_height: Optional[int] = None,
|
|
739
|
+
assert_before_seconds: Optional[int] = None,
|
|
740
|
+
) -> MempoolItem:
|
|
741
|
+
# we don't actually care about the puzzle and solutions for the purpose of
|
|
742
|
+
# can_replace()
|
|
743
|
+
spend_ids = []
|
|
744
|
+
coin_spends = []
|
|
745
|
+
bundle_coin_spends = {}
|
|
746
|
+
for c in coins:
|
|
747
|
+
coin_id = c.name()
|
|
748
|
+
spend_ids.append(coin_id)
|
|
749
|
+
spend = make_spend(c, SerializedProgram.to(None), SerializedProgram.to(None))
|
|
750
|
+
coin_spends.append(spend)
|
|
751
|
+
bundle_coin_spends[coin_id] = BundleCoinSpend(
|
|
752
|
+
coin_spend=spend, eligible_for_dedup=False, eligible_for_fast_forward=False, additions=[]
|
|
753
|
+
)
|
|
754
|
+
spend_bundle = SpendBundle(coin_spends, G2Element())
|
|
755
|
+
conds = make_test_conds(cost=cost, spend_ids=spend_ids)
|
|
756
|
+
return MempoolItem(
|
|
757
|
+
spend_bundle=spend_bundle,
|
|
758
|
+
fee=uint64(fee),
|
|
759
|
+
conds=conds,
|
|
760
|
+
spend_bundle_name=spend_bundle.name(),
|
|
761
|
+
height_added_to_mempool=uint32(0),
|
|
762
|
+
assert_height=None if assert_height is None else uint32(assert_height),
|
|
763
|
+
assert_before_height=None if assert_before_height is None else uint32(assert_before_height),
|
|
764
|
+
assert_before_seconds=None if assert_before_seconds is None else uint64(assert_before_seconds),
|
|
765
|
+
bundle_coin_spends=bundle_coin_spends,
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def make_test_coins() -> list[Coin]:
|
|
770
|
+
ret: list[Coin] = []
|
|
771
|
+
for i in range(5):
|
|
772
|
+
ret.append(Coin(height_hash(i), height_hash(i + 100), uint64(i * 100)))
|
|
773
|
+
return ret
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
coins = make_test_coins()
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
@pytest.mark.parametrize(
|
|
780
|
+
"existing_items,new_item,expected",
|
|
781
|
+
[
|
|
782
|
+
# FEE RULE
|
|
783
|
+
# the new item must pay a higher fee, in absolute terms
|
|
784
|
+
# replacing exactly the same spend is fine, as long as we increment the fee
|
|
785
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1]), False),
|
|
786
|
+
# this is less than the minimum fee increase
|
|
787
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=9999999), False),
|
|
788
|
+
# this is the minimum fee increase
|
|
789
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000), True),
|
|
790
|
+
# FEE RATE RULE
|
|
791
|
+
# the new item must pay a higher fee per cost than the existing item(s)
|
|
792
|
+
# the existing fee rate is 2 and the new fee rate is 2
|
|
793
|
+
([mk_item(coins[0:1], cost=1000, fee=2000)], mk_item(coins[0:1], cost=10000000, fee=20000000), False),
|
|
794
|
+
# the new rate is >2
|
|
795
|
+
([mk_item(coins[0:1], cost=1000, fee=2000)], mk_item(coins[0:1], cost=10000000, fee=20000001), True),
|
|
796
|
+
# SUPERSET RULE
|
|
797
|
+
# we can't replace an item spending coin 0 and 1 with an
|
|
798
|
+
# item that just spends coin 0
|
|
799
|
+
([mk_item(coins[0:2])], mk_item(coins[0:1], fee=10000000), False),
|
|
800
|
+
# or just spends coin 1
|
|
801
|
+
([mk_item(coins[0:2])], mk_item(coins[1:2], fee=10000000), False),
|
|
802
|
+
# but if we spend the same coins
|
|
803
|
+
([mk_item(coins[0:2])], mk_item(coins[0:2], fee=10000000), True),
|
|
804
|
+
# or if we spend the same coins with additional coins
|
|
805
|
+
([mk_item(coins[0:2])], mk_item(coins[0:3], fee=10000000), True),
|
|
806
|
+
# FEE- AND FEE RATE RULES
|
|
807
|
+
# if we're replacing two items, each paying a fee of 100, we need to
|
|
808
|
+
# spend (at least) the same coins and pay at least 10000000 higher fee
|
|
809
|
+
(
|
|
810
|
+
[mk_item(coins[0:1], fee=100, cost=100), mk_item(coins[1:2], fee=100, cost=100)],
|
|
811
|
+
mk_item(coins[0:2], fee=10000200, cost=200),
|
|
812
|
+
True,
|
|
813
|
+
),
|
|
814
|
+
# if the fee rate is exactly the same, we won't allow the replacement
|
|
815
|
+
(
|
|
816
|
+
[mk_item(coins[0:1], fee=100, cost=100), mk_item(coins[1:2], fee=100, cost=100)],
|
|
817
|
+
mk_item(coins[0:2], fee=10000200, cost=10000200),
|
|
818
|
+
False,
|
|
819
|
+
),
|
|
820
|
+
# TIMELOCK RULE
|
|
821
|
+
# the new item must not have different time lock than the existing item(s)
|
|
822
|
+
# the assert height time lock condition was introduced in the new item
|
|
823
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_height=1000), False),
|
|
824
|
+
# the assert before height time lock condition was introduced in the new item
|
|
825
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_before_height=1000), False),
|
|
826
|
+
# the assert before seconds time lock condition was introduced in the new item
|
|
827
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000), False),
|
|
828
|
+
# if we don't alter any time locks, we are allowed to replace
|
|
829
|
+
([mk_item(coins[0:1])], mk_item(coins[0:1], fee=10000000), True),
|
|
830
|
+
# ASSERT_HEIGHT
|
|
831
|
+
# the assert height time lock condition was removed in the new item
|
|
832
|
+
([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000), False),
|
|
833
|
+
# different assert height constraint
|
|
834
|
+
([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=100), False),
|
|
835
|
+
([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=2000), False),
|
|
836
|
+
# the same assert height is OK
|
|
837
|
+
([mk_item(coins[0:1], assert_height=1000)], mk_item(coins[0:1], fee=10000000, assert_height=1000), True),
|
|
838
|
+
# The new spend just have to match the most restrictive condition
|
|
839
|
+
(
|
|
840
|
+
[mk_item(coins[0:1], assert_height=200), mk_item(coins[1:2], assert_height=400)],
|
|
841
|
+
mk_item(coins[0:2], fee=10000000, assert_height=400),
|
|
842
|
+
True,
|
|
843
|
+
),
|
|
844
|
+
# ASSERT_BEFORE_HEIGHT
|
|
845
|
+
# the assert before height time lock condition was removed in the new item
|
|
846
|
+
([mk_item(coins[0:1], assert_before_height=1000)], mk_item(coins[0:1], fee=10000000), False),
|
|
847
|
+
# different assert before height constraint
|
|
848
|
+
(
|
|
849
|
+
[mk_item(coins[0:1], assert_before_height=1000)],
|
|
850
|
+
mk_item(coins[0:1], fee=10000000, assert_before_height=100),
|
|
851
|
+
False,
|
|
852
|
+
),
|
|
853
|
+
(
|
|
854
|
+
[mk_item(coins[0:1], assert_before_height=1000)],
|
|
855
|
+
mk_item(coins[0:1], fee=10000000, assert_before_height=2000),
|
|
856
|
+
False,
|
|
857
|
+
),
|
|
858
|
+
# The new spend just have to match the most restrictive condition
|
|
859
|
+
(
|
|
860
|
+
[mk_item(coins[0:1], assert_before_height=200), mk_item(coins[1:2], assert_before_height=400)],
|
|
861
|
+
mk_item(coins[0:2], fee=10000000, assert_before_height=200),
|
|
862
|
+
True,
|
|
863
|
+
),
|
|
864
|
+
# ASSERT_BEFORE_SECONDS
|
|
865
|
+
# the assert before height time lock condition was removed in the new item
|
|
866
|
+
([mk_item(coins[0:1], assert_before_seconds=1000)], mk_item(coins[0:1], fee=10000000), False),
|
|
867
|
+
# different assert before seconds constraint
|
|
868
|
+
(
|
|
869
|
+
[mk_item(coins[0:1], assert_before_seconds=1000)],
|
|
870
|
+
mk_item(coins[0:1], fee=10000000, assert_before_seconds=100),
|
|
871
|
+
False,
|
|
872
|
+
),
|
|
873
|
+
(
|
|
874
|
+
[mk_item(coins[0:1], assert_before_seconds=1000)],
|
|
875
|
+
mk_item(coins[0:1], fee=10000000, assert_before_seconds=2000),
|
|
876
|
+
False,
|
|
877
|
+
),
|
|
878
|
+
# the assert before height time lock condition was introduced in the new item
|
|
879
|
+
(
|
|
880
|
+
[mk_item(coins[0:1], assert_before_seconds=1000)],
|
|
881
|
+
mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000),
|
|
882
|
+
True,
|
|
883
|
+
),
|
|
884
|
+
# The new spend just have to match the most restrictive condition
|
|
885
|
+
(
|
|
886
|
+
[mk_item(coins[0:1], assert_before_seconds=200), mk_item(coins[1:2], assert_before_seconds=400)],
|
|
887
|
+
mk_item(coins[0:2], fee=10000000, assert_before_seconds=200),
|
|
888
|
+
True,
|
|
889
|
+
),
|
|
890
|
+
# MIXED CONDITIONS
|
|
891
|
+
# we can't replace an assert_before_seconds with assert_before_height
|
|
892
|
+
(
|
|
893
|
+
[mk_item(coins[0:1], assert_before_seconds=1000)],
|
|
894
|
+
mk_item(coins[0:1], fee=10000000, assert_before_height=2000),
|
|
895
|
+
False,
|
|
896
|
+
),
|
|
897
|
+
# we added another condition
|
|
898
|
+
(
|
|
899
|
+
[mk_item(coins[0:1], assert_before_seconds=1000)],
|
|
900
|
+
mk_item(coins[0:1], fee=10000000, assert_before_seconds=1000, assert_height=200),
|
|
901
|
+
False,
|
|
902
|
+
),
|
|
903
|
+
# we removed assert before height
|
|
904
|
+
(
|
|
905
|
+
[mk_item(coins[0:1], assert_height=200, assert_before_height=1000)],
|
|
906
|
+
mk_item(coins[0:1], fee=10000000, assert_height=200),
|
|
907
|
+
False,
|
|
908
|
+
),
|
|
909
|
+
],
|
|
910
|
+
)
|
|
911
|
+
def test_can_replace(existing_items: list[MempoolItem], new_item: MempoolItem, expected: bool) -> None:
|
|
912
|
+
removals = {c.name() for c in new_item.spend_bundle.removals()}
|
|
913
|
+
assert can_replace(existing_items, removals, new_item) == expected
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
@pytest.mark.anyio
|
|
917
|
+
async def test_get_items_not_in_filter() -> None:
|
|
918
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
|
|
919
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
920
|
+
sb1, sb1_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions)
|
|
921
|
+
conditions2 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
|
|
922
|
+
sb2, sb2_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions2, TEST_COIN2)
|
|
923
|
+
conditions3 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
|
|
924
|
+
sb3, sb3_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions3, TEST_COIN3)
|
|
925
|
+
|
|
926
|
+
# Don't filter anything
|
|
927
|
+
empty_filter = PyBIP158([])
|
|
928
|
+
result = mempool_manager.get_items_not_in_filter(empty_filter)
|
|
929
|
+
assert result == [sb3, sb2, sb1]
|
|
930
|
+
|
|
931
|
+
# Filter everything
|
|
932
|
+
full_filter = PyBIP158([bytearray(sb1_name), bytearray(sb2_name), bytearray(sb3_name)])
|
|
933
|
+
result = mempool_manager.get_items_not_in_filter(full_filter)
|
|
934
|
+
assert result == []
|
|
935
|
+
|
|
936
|
+
# Negative limit
|
|
937
|
+
with pytest.raises(AssertionError):
|
|
938
|
+
mempool_manager.get_items_not_in_filter(empty_filter, limit=-1)
|
|
939
|
+
|
|
940
|
+
# Zero limit
|
|
941
|
+
with pytest.raises(AssertionError):
|
|
942
|
+
mempool_manager.get_items_not_in_filter(empty_filter, limit=0)
|
|
943
|
+
|
|
944
|
+
# Filter only one of the spend bundles
|
|
945
|
+
sb3_filter = PyBIP158([bytearray(sb3_name)])
|
|
946
|
+
|
|
947
|
+
# With a limit of one, sb2 has the highest FPC
|
|
948
|
+
result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=1)
|
|
949
|
+
assert result == [sb2]
|
|
950
|
+
|
|
951
|
+
# With a higher limit, all bundles aside from sb3 get included
|
|
952
|
+
result = mempool_manager.get_items_not_in_filter(sb3_filter, limit=5)
|
|
953
|
+
assert result == [sb2, sb1]
|
|
954
|
+
|
|
955
|
+
# Filter two of the spend bundles
|
|
956
|
+
sb2_and_3_filter = PyBIP158([bytearray(sb2_name), bytearray(sb3_name)])
|
|
957
|
+
result = mempool_manager.get_items_not_in_filter(sb2_and_3_filter)
|
|
958
|
+
assert result == [sb1]
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
@pytest.mark.anyio
|
|
962
|
+
async def test_total_mempool_fees() -> None:
|
|
963
|
+
coin_records: dict[bytes32, CoinRecord] = {}
|
|
964
|
+
|
|
965
|
+
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
966
|
+
ret: list[CoinRecord] = []
|
|
967
|
+
for name in coin_ids:
|
|
968
|
+
r = coin_records.get(name)
|
|
969
|
+
if r is not None:
|
|
970
|
+
ret.append(r)
|
|
971
|
+
return ret
|
|
972
|
+
|
|
973
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records)
|
|
974
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
975
|
+
|
|
976
|
+
# the limit of total fees in the mempool is 2^63
|
|
977
|
+
# the limit per mempool item is 2^50, that lets us add 8192 items with the
|
|
978
|
+
# maximum amount of fee before reaching the total mempool limit
|
|
979
|
+
amount = uint64(2**50)
|
|
980
|
+
total_fee = 0
|
|
981
|
+
for i in range(8192):
|
|
982
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
|
|
983
|
+
coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
984
|
+
amount = uint64(amount - 1)
|
|
985
|
+
# the fee is 1 less than the amount because we create a coin of 1 mojo
|
|
986
|
+
total_fee += amount
|
|
987
|
+
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
|
|
988
|
+
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
989
|
+
assert mempool_manager.mempool.total_mempool_fees() == total_fee
|
|
990
|
+
|
|
991
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, amount)
|
|
992
|
+
coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
|
|
993
|
+
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions, coin)
|
|
994
|
+
assert result[1] == MempoolInclusionStatus.FAILED
|
|
995
|
+
assert result[2] == Err.INVALID_BLOCK_FEE_AMOUNT
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
@pytest.mark.parametrize("reverse_tx_order", [True, False])
|
|
999
|
+
@pytest.mark.anyio
|
|
1000
|
+
async def test_create_bundle_from_mempool(reverse_tx_order: bool) -> None:
|
|
1001
|
+
async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
|
|
1002
|
+
assert False # pragma: no cover
|
|
1003
|
+
|
|
1004
|
+
async def make_coin_spends(coins: list[Coin], *, high_fees: bool = True) -> list[CoinSpend]:
|
|
1005
|
+
spends_list = []
|
|
1006
|
+
for i in range(0, len(coins)):
|
|
1007
|
+
coin_spend = make_spend(
|
|
1008
|
+
coins[i],
|
|
1009
|
+
IDENTITY_PUZZLE,
|
|
1010
|
+
Program.to(
|
|
1011
|
+
[[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i if high_fees else (coins[i].amount - 1)]]
|
|
1012
|
+
),
|
|
1013
|
+
)
|
|
1014
|
+
spends_list.append(coin_spend)
|
|
1015
|
+
return spends_list
|
|
1016
|
+
|
|
1017
|
+
async def send_spends_to_mempool(coin_spends: list[CoinSpend]) -> None:
|
|
1018
|
+
g2 = G2Element()
|
|
1019
|
+
for cs in coin_spends:
|
|
1020
|
+
sb = SpendBundle([cs], g2)
|
|
1021
|
+
result = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
1022
|
+
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
1023
|
+
|
|
1024
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(2000000000, 2000002200)))
|
|
1025
|
+
high_rate_spends = await make_coin_spends(coins[0:2200])
|
|
1026
|
+
low_rate_spends = await make_coin_spends(coins[2200:2400], high_fees=False)
|
|
1027
|
+
spends = low_rate_spends + high_rate_spends if reverse_tx_order else high_rate_spends + low_rate_spends
|
|
1028
|
+
await send_spends_to_mempool(spends)
|
|
1029
|
+
assert mempool_manager.peak is not None
|
|
1030
|
+
result = await mempool_manager.create_bundle_from_mempool(
|
|
1031
|
+
mempool_manager.peak.header_hash, get_unspent_lineage_info_for_puzzle_hash
|
|
1032
|
+
)
|
|
1033
|
+
assert result is not None
|
|
1034
|
+
# Make sure we filled the block with only high rate spends
|
|
1035
|
+
assert len([s for s in high_rate_spends if s in result[0].coin_spends]) == len(result[0].coin_spends)
|
|
1036
|
+
assert len([s for s in low_rate_spends if s in result[0].coin_spends]) == 0
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
@pytest.mark.parametrize("num_skipped_items", [PRIORITY_TX_THRESHOLD, MAX_SKIPPED_ITEMS])
|
|
1040
|
+
@pytest.mark.anyio
|
|
1041
|
+
async def test_create_bundle_from_mempool_on_max_cost(num_skipped_items: int, caplog: pytest.LogCaptureFixture) -> None:
|
|
1042
|
+
"""
|
|
1043
|
+
This test exercises the path where an item's inclusion would exceed the
|
|
1044
|
+
maximum cumulative cost, so it gets skipped as a result.
|
|
1045
|
+
|
|
1046
|
+
NOTE:
|
|
1047
|
+
1. After PRIORITY_TX_THRESHOLD, we skip items with eligible coins.
|
|
1048
|
+
2. After skipping MAX_SKIPPED_ITEMS, we stop processing further items.
|
|
1049
|
+
"""
|
|
1050
|
+
|
|
1051
|
+
async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
|
|
1052
|
+
assert False # pragma: no cover
|
|
1053
|
+
|
|
1054
|
+
MAX_BLOCK_CLVM_COST = 550_000_000
|
|
1055
|
+
|
|
1056
|
+
mempool_manager, coins = await setup_mempool_with_coins(
|
|
1057
|
+
coin_amounts=list(range(1_000_000_000, 1_000_000_030)),
|
|
1058
|
+
max_block_clvm_cost=MAX_BLOCK_CLVM_COST,
|
|
1059
|
+
max_tx_clvm_cost=uint64(MAX_BLOCK_CLVM_COST),
|
|
1060
|
+
mempool_block_buffer=20,
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
async def make_and_send_big_cost_sb(coin: Coin) -> None:
|
|
1064
|
+
"""
|
|
1065
|
+
Creates a spend bundle with a big enough cost that gets it close to the
|
|
1066
|
+
maximum block clvm cost limit.
|
|
1067
|
+
"""
|
|
1068
|
+
conditions = []
|
|
1069
|
+
sk = AugSchemeMPL.key_gen(b"7" * 32)
|
|
1070
|
+
g1 = sk.get_g1()
|
|
1071
|
+
sig = AugSchemeMPL.sign(sk, IDENTITY_PUZZLE_HASH, g1)
|
|
1072
|
+
aggsig = G2Element()
|
|
1073
|
+
# Let's get as close to `MAX_BLOCK_CLVM_COST` (550_000_000) as possible.
|
|
1074
|
+
# We start by accounting for execution cost
|
|
1075
|
+
spend_bundle_cost = 44
|
|
1076
|
+
# And then the created coin
|
|
1077
|
+
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - 10_000_000])
|
|
1078
|
+
TEST_CREATE_COIN_SPEND_BYTESIZE = 93
|
|
1079
|
+
TEST_CREATE_COIN_CONDITION_COST = (
|
|
1080
|
+
ConditionCost.CREATE_COIN.value + TEST_CREATE_COIN_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
|
|
1081
|
+
)
|
|
1082
|
+
spend_bundle_cost += TEST_CREATE_COIN_CONDITION_COST
|
|
1083
|
+
# We're using agg sig conditions to increase the spend bundle's cost
|
|
1084
|
+
# and reach our target cost.
|
|
1085
|
+
TEST_AGG_SIG_SPEND_BYTESIZE = 88
|
|
1086
|
+
TEST_AGGSIG_CONDITION_COST = (
|
|
1087
|
+
ConditionCost.AGG_SIG.value + TEST_AGG_SIG_SPEND_BYTESIZE * DEFAULT_CONSTANTS.COST_PER_BYTE
|
|
1088
|
+
)
|
|
1089
|
+
while spend_bundle_cost + TEST_AGGSIG_CONDITION_COST < MAX_BLOCK_CLVM_COST:
|
|
1090
|
+
conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, IDENTITY_PUZZLE_HASH])
|
|
1091
|
+
aggsig += sig
|
|
1092
|
+
spend_bundle_cost += TEST_AGGSIG_CONDITION_COST
|
|
1093
|
+
# We now have a spend bundle with a big enough cost that gets it close to the limit
|
|
1094
|
+
_, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coin, aggsig)
|
|
1095
|
+
cost, status, _ = res
|
|
1096
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1097
|
+
assert cost == spend_bundle_cost
|
|
1098
|
+
|
|
1099
|
+
# Create the spend bundles with a big enough cost that they get close to the limit
|
|
1100
|
+
for i in range(num_skipped_items):
|
|
1101
|
+
await make_and_send_big_cost_sb(coins[i])
|
|
1102
|
+
|
|
1103
|
+
# Create a spend bundle with a relatively smaller cost.
|
|
1104
|
+
# Combined with a big cost spend bundle, we'd exceed the maximum block clvm cost
|
|
1105
|
+
sb2_coin = coins[num_skipped_items]
|
|
1106
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, sb2_coin.amount - 200_000]]
|
|
1107
|
+
sb2, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, sb2_coin)
|
|
1108
|
+
assert res[1] == MempoolInclusionStatus.SUCCESS
|
|
1109
|
+
sb2_addition = Coin(sb2_coin.name(), IDENTITY_PUZZLE_HASH, uint64(sb2_coin.amount - 200_000))
|
|
1110
|
+
# Create 4 extra spend bundles with smaller FPC and smaller costs
|
|
1111
|
+
extra_sbs = []
|
|
1112
|
+
extra_additions = []
|
|
1113
|
+
sk = AugSchemeMPL.key_gen(b"8" * 32)
|
|
1114
|
+
g1 = sk.get_g1()
|
|
1115
|
+
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
|
1116
|
+
for i in range(num_skipped_items + 1, num_skipped_items + 5):
|
|
1117
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coins[i].amount - 30_000]]
|
|
1118
|
+
# Make the first of these without eligible coins
|
|
1119
|
+
if i == num_skipped_items + 1:
|
|
1120
|
+
conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, bytes(g1), b"foobar"])
|
|
1121
|
+
aggsig = sig
|
|
1122
|
+
else:
|
|
1123
|
+
aggsig = G2Element()
|
|
1124
|
+
sb, _, res = await generate_and_add_spendbundle(mempool_manager, conditions, coins[i], aggsig)
|
|
1125
|
+
extra_sbs.append(sb)
|
|
1126
|
+
coin = Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, uint64(coins[i].amount - 30_000))
|
|
1127
|
+
extra_additions.append(coin)
|
|
1128
|
+
assert res[1] == MempoolInclusionStatus.SUCCESS
|
|
1129
|
+
|
|
1130
|
+
assert mempool_manager.peak is not None
|
|
1131
|
+
caplog.set_level(logging.DEBUG)
|
|
1132
|
+
result = await mempool_manager.create_bundle_from_mempool(
|
|
1133
|
+
mempool_manager.peak.header_hash, get_unspent_lineage_info_for_puzzle_hash
|
|
1134
|
+
)
|
|
1135
|
+
assert result is not None
|
|
1136
|
+
agg, additions = result
|
|
1137
|
+
skipped_due_to_eligible_coins = sum(
|
|
1138
|
+
1
|
|
1139
|
+
for line in caplog.text.split("\n")
|
|
1140
|
+
if "DEBUG Exception while checking a mempool item for deduplication: Skipping transaction with eligible coin(s)"
|
|
1141
|
+
in line
|
|
1142
|
+
)
|
|
1143
|
+
if num_skipped_items == PRIORITY_TX_THRESHOLD:
|
|
1144
|
+
# We skipped enough big cost items to reach `PRIORITY_TX_THRESHOLD`,
|
|
1145
|
+
# so the first from the extra 4 (the one without eligible coins) went in,
|
|
1146
|
+
# and the other 3 were skipped (they have eligible coins)
|
|
1147
|
+
assert skipped_due_to_eligible_coins == 3
|
|
1148
|
+
assert agg == SpendBundle.aggregate([sb2, extra_sbs[0]])
|
|
1149
|
+
assert additions == [sb2_addition, extra_additions[0]]
|
|
1150
|
+
assert agg.removals() == [sb2_coin, coins[num_skipped_items + 1]]
|
|
1151
|
+
elif num_skipped_items == MAX_SKIPPED_ITEMS:
|
|
1152
|
+
# We skipped enough big cost items to trigger `MAX_SKIPPED_ITEMS` so
|
|
1153
|
+
# we didn't process any of the extra items
|
|
1154
|
+
assert skipped_due_to_eligible_coins == 0
|
|
1155
|
+
assert agg == SpendBundle.aggregate([sb2])
|
|
1156
|
+
assert additions == [sb2_addition]
|
|
1157
|
+
assert agg.removals() == [sb2_coin]
|
|
1158
|
+
else:
|
|
1159
|
+
raise ValueError("num_skipped_items must be PRIORITY_TX_THRESHOLD or MAX_SKIPPED_ITEMS") # pragma: no cover
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
@pytest.mark.parametrize(
|
|
1163
|
+
"opcode,arg,expect_eviction, expect_limit",
|
|
1164
|
+
[
|
|
1165
|
+
# current height: 10 current_time: 10000
|
|
1166
|
+
# we step the chain forward 1 block and 19 seconds
|
|
1167
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10001, True, None),
|
|
1168
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10019, True, None),
|
|
1169
|
+
(co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 10020, False, 10020),
|
|
1170
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 11, True, None),
|
|
1171
|
+
(co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 12, False, 12),
|
|
1172
|
+
# the coin was created at height: 5 timestamp: 9900
|
|
1173
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 6, True, None),
|
|
1174
|
+
(co.ASSERT_BEFORE_HEIGHT_RELATIVE, 7, False, 5 + 7),
|
|
1175
|
+
(co.ASSERT_BEFORE_SECONDS_RELATIVE, 119, True, None),
|
|
1176
|
+
(co.ASSERT_BEFORE_SECONDS_RELATIVE, 120, False, 9900 + 120),
|
|
1177
|
+
],
|
|
1178
|
+
)
|
|
1179
|
+
@pytest.mark.anyio
|
|
1180
|
+
async def test_assert_before_expiration(
|
|
1181
|
+
opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: Optional[int]
|
|
1182
|
+
) -> None:
|
|
1183
|
+
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
1184
|
+
all_coins = {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}
|
|
1185
|
+
ret: list[CoinRecord] = []
|
|
1186
|
+
for name in coin_ids:
|
|
1187
|
+
r = all_coins.get(name)
|
|
1188
|
+
if r is not None:
|
|
1189
|
+
ret.append(r)
|
|
1190
|
+
return ret
|
|
1191
|
+
|
|
1192
|
+
mempool_manager = await instantiate_mempool_manager(
|
|
1193
|
+
get_coin_records,
|
|
1194
|
+
block_height=uint32(10),
|
|
1195
|
+
block_timestamp=uint64(10000),
|
|
1196
|
+
constants=DEFAULT_CONSTANTS,
|
|
1197
|
+
)
|
|
1198
|
+
|
|
1199
|
+
bundle = spend_bundle_from_conditions(
|
|
1200
|
+
[
|
|
1201
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1202
|
+
[opcode, arg],
|
|
1203
|
+
],
|
|
1204
|
+
coin=TEST_COIN,
|
|
1205
|
+
)
|
|
1206
|
+
bundle_name = bundle.name()
|
|
1207
|
+
assert (await add_spendbundle(mempool_manager, bundle, bundle_name))[1] == mis.SUCCESS
|
|
1208
|
+
# make sure the spend was added correctly
|
|
1209
|
+
assert mempool_manager.get_spendbundle(bundle_name) == bundle
|
|
1210
|
+
|
|
1211
|
+
block_record = create_test_block_record(height=uint32(11), timestamp=uint64(10019))
|
|
1212
|
+
await mempool_manager.new_peak(block_record, None)
|
|
1213
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
1214
|
+
|
|
1215
|
+
still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
|
|
1216
|
+
assert still_in_pool != expect_eviction
|
|
1217
|
+
if still_in_pool:
|
|
1218
|
+
assert expect_limit is not None
|
|
1219
|
+
item = mempool_manager.get_mempool_item(bundle_name)
|
|
1220
|
+
assert item is not None
|
|
1221
|
+
if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
|
|
1222
|
+
assert item.assert_before_seconds == expect_limit
|
|
1223
|
+
elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
|
|
1224
|
+
assert item.assert_before_height == expect_limit
|
|
1225
|
+
else:
|
|
1226
|
+
assert False
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
def make_test_spendbundle(coin: Coin, *, fee: int = 0, eligible_spend: bool = False) -> SpendBundle:
|
|
1230
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, uint64(coin.amount - fee)]]
|
|
1231
|
+
sig = G2Element()
|
|
1232
|
+
if not eligible_spend:
|
|
1233
|
+
sk = AugSchemeMPL.key_gen(b"2" * 32)
|
|
1234
|
+
g1 = sk.get_g1()
|
|
1235
|
+
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
|
1236
|
+
conditions.append([ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"])
|
|
1237
|
+
return spend_bundle_from_conditions(conditions, coin, sig)
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
async def send_spendbundle(
|
|
1241
|
+
mempool_manager: MempoolManager,
|
|
1242
|
+
sb: SpendBundle,
|
|
1243
|
+
expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
|
|
1244
|
+
) -> None:
|
|
1245
|
+
result = await add_spendbundle(mempool_manager, sb, sb.name())
|
|
1246
|
+
assert (result[1], result[2]) == expected_result
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
async def make_and_send_spendbundle(
|
|
1250
|
+
mempool_manager: MempoolManager,
|
|
1251
|
+
coin: Coin,
|
|
1252
|
+
*,
|
|
1253
|
+
fee: int = 0,
|
|
1254
|
+
expected_result: tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
|
|
1255
|
+
) -> SpendBundle:
|
|
1256
|
+
sb = make_test_spendbundle(coin, fee=fee)
|
|
1257
|
+
await send_spendbundle(mempool_manager, sb, expected_result)
|
|
1258
|
+
return sb
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
def assert_sb_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
|
|
1262
|
+
assert sb == mempool_manager.get_spendbundle(sb.name())
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
def assert_sb_not_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
|
|
1266
|
+
assert mempool_manager.get_spendbundle(sb.name()) is None
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
@pytest.mark.anyio
|
|
1270
|
+
async def test_insufficient_fee_increase() -> None:
|
|
1271
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1272
|
+
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1273
|
+
sb1_2 = await make_and_send_spendbundle(
|
|
1274
|
+
mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1275
|
+
)
|
|
1276
|
+
# The old spendbundle must stay
|
|
1277
|
+
assert_sb_in_pool(mempool_manager, sb1_1)
|
|
1278
|
+
assert_sb_not_in_pool(mempool_manager, sb1_2)
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
@pytest.mark.anyio
|
|
1282
|
+
async def test_sufficient_fee_increase() -> None:
|
|
1283
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1284
|
+
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1285
|
+
sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1286
|
+
# sb1_1 gets replaced with sb1_2
|
|
1287
|
+
assert_sb_not_in_pool(mempool_manager, sb1_1)
|
|
1288
|
+
assert_sb_in_pool(mempool_manager, sb1_2)
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
@pytest.mark.anyio
|
|
1292
|
+
async def test_superset() -> None:
|
|
1293
|
+
# Aggregated spendbundle sb12 replaces sb1 since it spends a superset
|
|
1294
|
+
# of coins spent in sb1
|
|
1295
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1296
|
+
sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
|
|
1297
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1298
|
+
sb12 = SpendBundle.aggregate([sb2, sb1])
|
|
1299
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1300
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1301
|
+
assert_sb_not_in_pool(mempool_manager, sb1)
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
@pytest.mark.anyio
|
|
1305
|
+
async def test_superset_violation() -> None:
|
|
1306
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1307
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1308
|
+
sb2 = make_test_spendbundle(coins[1])
|
|
1309
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1310
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1311
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1312
|
+
# sb23 must not replace existing sb12 as the former does not spend all
|
|
1313
|
+
# coins that are spent in the latter (specifically, the first coin)
|
|
1314
|
+
sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1315
|
+
sb23 = SpendBundle.aggregate([sb2, sb3])
|
|
1316
|
+
await send_spendbundle(
|
|
1317
|
+
mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1318
|
+
)
|
|
1319
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1320
|
+
assert_sb_not_in_pool(mempool_manager, sb23)
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
@pytest.mark.anyio
|
|
1324
|
+
async def test_total_fpc_decrease() -> None:
|
|
1325
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1326
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1327
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1328
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1329
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1330
|
+
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1331
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1332
|
+
assert_sb_in_pool(mempool_manager, sb3)
|
|
1333
|
+
# sb1234 should not be in pool as it decreases total fees per cost
|
|
1334
|
+
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1335
|
+
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
|
|
1336
|
+
await send_spendbundle(
|
|
1337
|
+
mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
|
|
1338
|
+
)
|
|
1339
|
+
assert_sb_not_in_pool(mempool_manager, sb1234)
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
@pytest.mark.anyio
|
|
1343
|
+
async def test_sufficient_total_fpc_increase() -> None:
|
|
1344
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1345
|
+
sb1 = make_test_spendbundle(coins[0])
|
|
1346
|
+
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1347
|
+
sb12 = SpendBundle.aggregate([sb1, sb2])
|
|
1348
|
+
await send_spendbundle(mempool_manager, sb12)
|
|
1349
|
+
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
|
|
1350
|
+
assert_sb_in_pool(mempool_manager, sb12)
|
|
1351
|
+
assert_sb_in_pool(mempool_manager, sb3)
|
|
1352
|
+
# sb1234 has a higher fee per cost than its conflicts and should get
|
|
1353
|
+
# into the mempool
|
|
1354
|
+
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
|
|
1355
|
+
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
|
|
1356
|
+
await send_spendbundle(mempool_manager, sb1234)
|
|
1357
|
+
assert_sb_in_pool(mempool_manager, sb1234)
|
|
1358
|
+
assert_sb_not_in_pool(mempool_manager, sb12)
|
|
1359
|
+
assert_sb_not_in_pool(mempool_manager, sb3)
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
@pytest.mark.anyio
|
|
1363
|
+
async def test_replace_with_extra_eligible_coin() -> None:
|
|
1364
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1365
|
+
sb1234 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(4)])
|
|
1366
|
+
await send_spendbundle(mempool_manager, sb1234)
|
|
1367
|
+
assert_sb_in_pool(mempool_manager, sb1234)
|
|
1368
|
+
# Replace sb1234 with sb1234_2 which spends an eligible coin additionally
|
|
1369
|
+
eligible_sb = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE, eligible_spend=True)
|
|
1370
|
+
sb1234_2 = SpendBundle.aggregate([sb1234, eligible_sb])
|
|
1371
|
+
await send_spendbundle(mempool_manager, sb1234_2)
|
|
1372
|
+
assert_sb_not_in_pool(mempool_manager, sb1234)
|
|
1373
|
+
assert_sb_in_pool(mempool_manager, sb1234_2)
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
@pytest.mark.anyio
|
|
1377
|
+
async def test_replacing_one_with_an_eligible_coin() -> None:
|
|
1378
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
|
|
1379
|
+
sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
|
|
1380
|
+
eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
|
|
1381
|
+
sb123e = SpendBundle.aggregate([sb123, eligible_sb])
|
|
1382
|
+
await send_spendbundle(mempool_manager, sb123e)
|
|
1383
|
+
assert_sb_in_pool(mempool_manager, sb123e)
|
|
1384
|
+
# Replace sb123e with sb123e4
|
|
1385
|
+
sb4 = make_test_spendbundle(coins[4], fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
1386
|
+
sb123e4 = SpendBundle.aggregate([sb123e, sb4])
|
|
1387
|
+
await send_spendbundle(mempool_manager, sb123e4)
|
|
1388
|
+
assert_sb_not_in_pool(mempool_manager, sb123e)
|
|
1389
|
+
assert_sb_in_pool(mempool_manager, sb123e4)
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
@pytest.mark.parametrize("amount", [0, 1])
|
|
1393
|
+
def test_run_for_cost(amount: int) -> None:
|
|
1394
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, amount]]
|
|
1395
|
+
solution = SerializedProgram.to(conditions)
|
|
1396
|
+
cost = run_for_cost(IDENTITY_PUZZLE, solution, additions_count=1, max_cost=uint64(10000000))
|
|
1397
|
+
assert cost == uint64(1800044)
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
def test_run_for_cost_max_cost() -> None:
|
|
1401
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
|
|
1402
|
+
solution = SerializedProgram.to(conditions)
|
|
1403
|
+
with pytest.raises(ValueError, match="cost exceeded"):
|
|
1404
|
+
run_for_cost(IDENTITY_PUZZLE, solution, additions_count=1, max_cost=uint64(43))
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
def test_dedup_info_nothing_to_do() -> None:
|
|
1408
|
+
# No eligible coins, nothing to deduplicate, item gets considered normally
|
|
1409
|
+
|
|
1410
|
+
sk = AugSchemeMPL.key_gen(b"3" * 32)
|
|
1411
|
+
g1 = sk.get_g1()
|
|
1412
|
+
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
|
1413
|
+
|
|
1414
|
+
conditions = [
|
|
1415
|
+
[ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"],
|
|
1416
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1417
|
+
]
|
|
1418
|
+
sb = spend_bundle_from_conditions(conditions, TEST_COIN, sig)
|
|
1419
|
+
mempool_item = mempool_item_from_spendbundle(sb)
|
|
1420
|
+
eligible_coin_spends = EligibleCoinSpends()
|
|
1421
|
+
unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
|
|
1422
|
+
bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
|
|
1423
|
+
)
|
|
1424
|
+
assert unique_coin_spends == sb.coin_spends
|
|
1425
|
+
assert cost_saving == 0
|
|
1426
|
+
assert unique_additions == [Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1))]
|
|
1427
|
+
assert eligible_coin_spends == EligibleCoinSpends()
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
def test_dedup_info_eligible_1st_time() -> None:
|
|
1431
|
+
# Eligible coin encountered for the first time
|
|
1432
|
+
conditions = [
|
|
1433
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1434
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
1435
|
+
]
|
|
1436
|
+
sb = spend_bundle_from_conditions(conditions, TEST_COIN)
|
|
1437
|
+
mempool_item = mempool_item_from_spendbundle(sb)
|
|
1438
|
+
assert mempool_item.conds is not None
|
|
1439
|
+
eligible_coin_spends = EligibleCoinSpends()
|
|
1440
|
+
solution = SerializedProgram.to(conditions)
|
|
1441
|
+
unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
|
|
1442
|
+
bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
|
|
1443
|
+
)
|
|
1444
|
+
assert unique_coin_spends == sb.coin_spends
|
|
1445
|
+
assert cost_saving == 0
|
|
1446
|
+
assert set(unique_additions) == {
|
|
1447
|
+
Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(1)),
|
|
1448
|
+
Coin(TEST_COIN_ID, IDENTITY_PUZZLE_HASH, uint64(2)),
|
|
1449
|
+
}
|
|
1450
|
+
assert eligible_coin_spends == EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=solution, cost=None)})
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
def test_dedup_info_eligible_but_different_solution() -> None:
|
|
1454
|
+
# Eligible coin but different solution from the one we encountered
|
|
1455
|
+
initial_conditions = [
|
|
1456
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1457
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
1458
|
+
]
|
|
1459
|
+
initial_solution = SerializedProgram.to(initial_conditions)
|
|
1460
|
+
eligible_coin_spends = EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=None)})
|
|
1461
|
+
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
|
|
1462
|
+
sb = spend_bundle_from_conditions(conditions, TEST_COIN)
|
|
1463
|
+
mempool_item = mempool_item_from_spendbundle(sb)
|
|
1464
|
+
with pytest.raises(ValueError, match="Solution is different from what we're deduplicating on"):
|
|
1465
|
+
eligible_coin_spends.get_deduplication_info(
|
|
1466
|
+
bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
def test_dedup_info_eligible_2nd_time_and_another_1st_time() -> None:
|
|
1471
|
+
# Eligible coin encountered a second time, and another for the first time
|
|
1472
|
+
initial_conditions = [
|
|
1473
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1474
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
1475
|
+
]
|
|
1476
|
+
initial_solution = SerializedProgram.to(initial_conditions)
|
|
1477
|
+
eligible_coin_spends = EligibleCoinSpends({TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=None)})
|
|
1478
|
+
sb1 = spend_bundle_from_conditions(initial_conditions, TEST_COIN)
|
|
1479
|
+
second_conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
|
|
1480
|
+
second_solution = SerializedProgram.to(second_conditions)
|
|
1481
|
+
sb2 = spend_bundle_from_conditions(second_conditions, TEST_COIN2)
|
|
1482
|
+
sb = SpendBundle.aggregate([sb1, sb2])
|
|
1483
|
+
mempool_item = mempool_item_from_spendbundle(sb)
|
|
1484
|
+
assert mempool_item.conds is not None
|
|
1485
|
+
unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
|
|
1486
|
+
bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
|
|
1487
|
+
)
|
|
1488
|
+
# Only the eligible one that we encountered more than once gets deduplicated
|
|
1489
|
+
assert unique_coin_spends == sb2.coin_spends
|
|
1490
|
+
saved_cost = uint64(3600044)
|
|
1491
|
+
assert cost_saving == saved_cost
|
|
1492
|
+
assert unique_additions == [Coin(TEST_COIN_ID2, IDENTITY_PUZZLE_HASH, uint64(3))]
|
|
1493
|
+
# The coin we encountered a second time has its cost and additions properly updated
|
|
1494
|
+
# The coin we encountered for the first time gets cost None and an empty set of additions
|
|
1495
|
+
expected_eligible_spends = EligibleCoinSpends(
|
|
1496
|
+
{
|
|
1497
|
+
TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=saved_cost),
|
|
1498
|
+
TEST_COIN_ID2: DedupCoinSpend(solution=second_solution, cost=None),
|
|
1499
|
+
}
|
|
1500
|
+
)
|
|
1501
|
+
assert eligible_coin_spends == expected_eligible_spends
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
def test_dedup_info_eligible_3rd_time_another_2nd_time_and_one_non_eligible() -> None:
|
|
1505
|
+
# Eligible coin encountered a third time, another for the second time and one non eligible
|
|
1506
|
+
initial_conditions = [
|
|
1507
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
|
|
1508
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
|
|
1509
|
+
]
|
|
1510
|
+
initial_solution = SerializedProgram.to(initial_conditions)
|
|
1511
|
+
second_conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 3]]
|
|
1512
|
+
second_solution = SerializedProgram.to(second_conditions)
|
|
1513
|
+
saved_cost = uint64(3600044)
|
|
1514
|
+
eligible_coin_spends = EligibleCoinSpends(
|
|
1515
|
+
{
|
|
1516
|
+
TEST_COIN_ID: DedupCoinSpend(solution=initial_solution, cost=saved_cost),
|
|
1517
|
+
TEST_COIN_ID2: DedupCoinSpend(solution=second_solution, cost=None),
|
|
1518
|
+
}
|
|
1519
|
+
)
|
|
1520
|
+
sb1 = spend_bundle_from_conditions(initial_conditions, TEST_COIN)
|
|
1521
|
+
sb2 = spend_bundle_from_conditions(second_conditions, TEST_COIN2)
|
|
1522
|
+
sk = AugSchemeMPL.key_gen(b"4" * 32)
|
|
1523
|
+
g1 = sk.get_g1()
|
|
1524
|
+
sig = AugSchemeMPL.sign(sk, b"foobar", g1)
|
|
1525
|
+
sb3_conditions = [
|
|
1526
|
+
[ConditionOpcode.AGG_SIG_UNSAFE, g1, b"foobar"],
|
|
1527
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 4],
|
|
1528
|
+
]
|
|
1529
|
+
sb3 = spend_bundle_from_conditions(sb3_conditions, TEST_COIN3, sig)
|
|
1530
|
+
sb = SpendBundle.aggregate([sb1, sb2, sb3])
|
|
1531
|
+
mempool_item = mempool_item_from_spendbundle(sb)
|
|
1532
|
+
assert mempool_item.conds is not None
|
|
1533
|
+
unique_coin_spends, cost_saving, unique_additions = eligible_coin_spends.get_deduplication_info(
|
|
1534
|
+
bundle_coin_spends=mempool_item.bundle_coin_spends, max_cost=mempool_item.conds.cost
|
|
1535
|
+
)
|
|
1536
|
+
assert unique_coin_spends == sb3.coin_spends
|
|
1537
|
+
saved_cost2 = uint64(1800044)
|
|
1538
|
+
assert cost_saving == saved_cost + saved_cost2
|
|
1539
|
+
assert unique_additions == [Coin(TEST_COIN_ID3, IDENTITY_PUZZLE_HASH, uint64(4))]
|
|
1540
|
+
expected_eligible_spends = EligibleCoinSpends(
|
|
1541
|
+
{
|
|
1542
|
+
TEST_COIN_ID: DedupCoinSpend(initial_solution, saved_cost),
|
|
1543
|
+
TEST_COIN_ID2: DedupCoinSpend(second_solution, saved_cost2),
|
|
1544
|
+
}
|
|
1545
|
+
)
|
|
1546
|
+
assert eligible_coin_spends == expected_eligible_spends
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
@pytest.mark.anyio
|
|
1550
|
+
@pytest.mark.parametrize("new_height_step", [1, 2, -1])
|
|
1551
|
+
async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(new_height_step: int) -> None:
|
|
1552
|
+
"""
|
|
1553
|
+
This test makes sure all mempool items that spend a coin (in different ways)
|
|
1554
|
+
that shows up as spent in a block, get removed properly.
|
|
1555
|
+
NOTE: `new_height_step` parameter allows us to cover both the optimized and
|
|
1556
|
+
the reorg code paths
|
|
1557
|
+
"""
|
|
1558
|
+
new_height = uint32(TEST_HEIGHT + new_height_step)
|
|
1559
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(100))
|
|
1560
|
+
coin_id = coin.name()
|
|
1561
|
+
test_coin_records = {coin_id: CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))}
|
|
1562
|
+
|
|
1563
|
+
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
1564
|
+
ret: list[CoinRecord] = []
|
|
1565
|
+
for name in coin_ids:
|
|
1566
|
+
r = test_coin_records.get(name)
|
|
1567
|
+
if r is not None:
|
|
1568
|
+
ret.append(r)
|
|
1569
|
+
return ret
|
|
1570
|
+
|
|
1571
|
+
mempool_manager = await instantiate_mempool_manager(get_coin_records)
|
|
1572
|
+
# Create a bunch of mempool items that spend the coin in different ways
|
|
1573
|
+
for i in range(3):
|
|
1574
|
+
_, _, result = await generate_and_add_spendbundle(
|
|
1575
|
+
mempool_manager, [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i]], coin
|
|
1576
|
+
)
|
|
1577
|
+
assert result[1] == MempoolInclusionStatus.SUCCESS
|
|
1578
|
+
assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 3
|
|
1579
|
+
assert mempool_manager.mempool.size() == 3
|
|
1580
|
+
assert len(list(mempool_manager.mempool.items_by_feerate())) == 3
|
|
1581
|
+
# Setup a new peak where the incoming block has spent the coin
|
|
1582
|
+
# Mark this coin as spent
|
|
1583
|
+
test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
|
|
1584
|
+
block_record = create_test_block_record(height=new_height)
|
|
1585
|
+
await mempool_manager.new_peak(block_record, [coin_id])
|
|
1586
|
+
invariant_check_mempool(mempool_manager.mempool)
|
|
1587
|
+
# As the coin was a spend in all the mempool items we had, nothing should be left now
|
|
1588
|
+
assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 0
|
|
1589
|
+
assert mempool_manager.mempool.size() == 0
|
|
1590
|
+
assert len(list(mempool_manager.mempool.items_by_feerate())) == 0
|
|
1591
|
+
|
|
1592
|
+
|
|
1593
|
+
@pytest.mark.anyio
|
|
1594
|
+
async def test_bundle_coin_spends() -> None:
|
|
1595
|
+
# This tests the construction of bundle_coin_spends map for mempool items
|
|
1596
|
+
# We're creating sb123e with 4 coins, one of them being eligible
|
|
1597
|
+
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000005)))
|
|
1598
|
+
sb123 = SpendBundle.aggregate([make_test_spendbundle(coins[i]) for i in range(3)])
|
|
1599
|
+
eligible_sb = make_test_spendbundle(coins[3], eligible_spend=True)
|
|
1600
|
+
sb123e = SpendBundle.aggregate([sb123, eligible_sb])
|
|
1601
|
+
await send_spendbundle(mempool_manager, sb123e)
|
|
1602
|
+
mi123e = mempool_manager.get_mempool_item(sb123e.name())
|
|
1603
|
+
assert mi123e is not None
|
|
1604
|
+
for i in range(3):
|
|
1605
|
+
assert mi123e.bundle_coin_spends[coins[i].name()] == BundleCoinSpend(
|
|
1606
|
+
coin_spend=sb123.coin_spends[i],
|
|
1607
|
+
eligible_for_dedup=False,
|
|
1608
|
+
eligible_for_fast_forward=False,
|
|
1609
|
+
additions=[Coin(coins[i].name(), IDENTITY_PUZZLE_HASH, coins[i].amount)],
|
|
1610
|
+
)
|
|
1611
|
+
assert mi123e.bundle_coin_spends[coins[3].name()] == BundleCoinSpend(
|
|
1612
|
+
coin_spend=eligible_sb.coin_spends[0],
|
|
1613
|
+
eligible_for_dedup=True,
|
|
1614
|
+
eligible_for_fast_forward=False,
|
|
1615
|
+
additions=[Coin(coins[3].name(), IDENTITY_PUZZLE_HASH, coins[3].amount)],
|
|
1616
|
+
)
|
|
1617
|
+
|
|
1618
|
+
|
|
1619
|
+
@pytest.mark.anyio
|
|
1620
|
+
async def test_identical_spend_aggregation_e2e(
|
|
1621
|
+
simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str
|
|
1622
|
+
) -> None:
|
|
1623
|
+
def get_sb_names_by_coin_id(
|
|
1624
|
+
full_node_api: FullNodeSimulator,
|
|
1625
|
+
spent_coin_id: bytes32,
|
|
1626
|
+
) -> set[bytes32]:
|
|
1627
|
+
return {
|
|
1628
|
+
i.spend_bundle_name
|
|
1629
|
+
for i in full_node_api.full_node.mempool_manager.mempool.get_items_by_coin_id(spent_coin_id)
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
async def send_to_mempool(
|
|
1633
|
+
full_node: FullNodeSimulator, spend_bundle: SpendBundle, *, expecting_conflict: bool = False
|
|
1634
|
+
) -> None:
|
|
1635
|
+
res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle))
|
|
1636
|
+
assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
|
|
1637
|
+
res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
|
|
1638
|
+
if expecting_conflict:
|
|
1639
|
+
assert res_parsed.status == MempoolInclusionStatus.PENDING.value
|
|
1640
|
+
assert res_parsed.error == "MEMPOOL_CONFLICT"
|
|
1641
|
+
else:
|
|
1642
|
+
assert res_parsed.status == MempoolInclusionStatus.SUCCESS.value
|
|
1643
|
+
|
|
1644
|
+
async def farm_a_block(full_node_api: FullNodeSimulator, wallet_node: WalletNode, ph: bytes32) -> None:
|
|
1645
|
+
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1646
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
|
|
1647
|
+
|
|
1648
|
+
async def make_setup_and_coins(
|
|
1649
|
+
full_node_api: FullNodeSimulator, wallet_node: WalletNode
|
|
1650
|
+
) -> tuple[Wallet, list[WalletCoinRecord], bytes32]:
|
|
1651
|
+
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
1652
|
+
ph = await wallet.get_new_puzzlehash()
|
|
1653
|
+
phs = [await wallet.get_new_puzzlehash() for _ in range(3)]
|
|
1654
|
+
for _ in range(2):
|
|
1655
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1656
|
+
other_recipients = [Payment(puzzle_hash=p, amount=uint64(200), memos=[]) for p in phs[1:]]
|
|
1657
|
+
async with wallet.wallet_state_manager.new_action_scope(
|
|
1658
|
+
DEFAULT_TX_CONFIG, push=False, sign=True
|
|
1659
|
+
) as action_scope:
|
|
1660
|
+
await wallet.generate_signed_transaction(uint64(200), phs[0], action_scope, primaries=other_recipients)
|
|
1661
|
+
[tx] = action_scope.side_effects.transactions
|
|
1662
|
+
assert tx.spend_bundle is not None
|
|
1663
|
+
await send_to_mempool(full_node_api, tx.spend_bundle)
|
|
1664
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1665
|
+
coins = list(await wallet_node.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(1))
|
|
1666
|
+
# Two blocks farmed plus 3 transactions
|
|
1667
|
+
assert len(coins) == 7
|
|
1668
|
+
return (wallet, coins, ph)
|
|
1669
|
+
|
|
1670
|
+
[[full_node_api], [[wallet_node, wallet_server]], _] = simulator_and_wallet
|
|
1671
|
+
server = full_node_api.full_node.server
|
|
1672
|
+
await wallet_server.start_client(PeerInfo(self_hostname, server.get_port()), None)
|
|
1673
|
+
wallet, coins, ph = await make_setup_and_coins(full_node_api, wallet_node)
|
|
1674
|
+
|
|
1675
|
+
# Make sure spending AB then BC would generate a conflict for the latter
|
|
1676
|
+
async with wallet.wallet_state_manager.new_action_scope(
|
|
1677
|
+
DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
|
|
1678
|
+
) as action_scope:
|
|
1679
|
+
await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[0].coin})
|
|
1680
|
+
await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[1].coin})
|
|
1681
|
+
await wallet.generate_signed_transaction(uint64(30), ph, action_scope, coins={coins[2].coin})
|
|
1682
|
+
[tx_a, tx_b, tx_c] = action_scope.side_effects.transactions
|
|
1683
|
+
assert tx_a.spend_bundle is not None
|
|
1684
|
+
assert tx_b.spend_bundle is not None
|
|
1685
|
+
assert tx_c.spend_bundle is not None
|
|
1686
|
+
ab_bundle = SpendBundle.aggregate([tx_a.spend_bundle, tx_b.spend_bundle])
|
|
1687
|
+
await send_to_mempool(full_node_api, ab_bundle)
|
|
1688
|
+
# BC should conflict here (on B)
|
|
1689
|
+
bc_bundle = SpendBundle.aggregate([tx_b.spend_bundle, tx_c.spend_bundle])
|
|
1690
|
+
await send_to_mempool(full_node_api, bc_bundle, expecting_conflict=True)
|
|
1691
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1692
|
+
|
|
1693
|
+
# Make sure DE and EF would aggregate on E when E is eligible for deduplication
|
|
1694
|
+
|
|
1695
|
+
# Create a coin with the identity puzzle hash
|
|
1696
|
+
async with wallet.wallet_state_manager.new_action_scope(
|
|
1697
|
+
DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
|
|
1698
|
+
) as action_scope:
|
|
1699
|
+
await wallet.generate_signed_transaction(uint64(200), IDENTITY_PUZZLE_HASH, action_scope, coins={coins[3].coin})
|
|
1700
|
+
[tx] = action_scope.side_effects.transactions
|
|
1701
|
+
assert tx.spend_bundle is not None
|
|
1702
|
+
await send_to_mempool(full_node_api, tx.spend_bundle)
|
|
1703
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1704
|
+
# Grab the coin we created and make an eligible coin out of it
|
|
1705
|
+
coins_with_identity_ph = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
|
|
1706
|
+
False, IDENTITY_PUZZLE_HASH
|
|
1707
|
+
)
|
|
1708
|
+
sb = spend_bundle_from_conditions(
|
|
1709
|
+
[[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 110]], coins_with_identity_ph[0].coin
|
|
1710
|
+
)
|
|
1711
|
+
await send_to_mempool(full_node_api, sb)
|
|
1712
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1713
|
+
# Grab the eligible coin to spend as E in DE and EF transactions
|
|
1714
|
+
e_coin = (await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, IDENTITY_PUZZLE_HASH))[
|
|
1715
|
+
0
|
|
1716
|
+
].coin
|
|
1717
|
+
e_coin_id = e_coin.name()
|
|
1718
|
+
# Restrict spending E with an announcement to consume
|
|
1719
|
+
message = b"Identical spend aggregation test"
|
|
1720
|
+
e_announcement = AssertCoinAnnouncement(asserted_id=e_coin_id, asserted_msg=message)
|
|
1721
|
+
# Create transactions D and F that consume an announcement created by E
|
|
1722
|
+
async with wallet.wallet_state_manager.new_action_scope(
|
|
1723
|
+
DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
|
|
1724
|
+
) as action_scope:
|
|
1725
|
+
await wallet.generate_signed_transaction(
|
|
1726
|
+
uint64(100),
|
|
1727
|
+
ph,
|
|
1728
|
+
action_scope,
|
|
1729
|
+
fee=uint64(0),
|
|
1730
|
+
coins={coins[4].coin},
|
|
1731
|
+
extra_conditions=(e_announcement,),
|
|
1732
|
+
)
|
|
1733
|
+
await wallet.generate_signed_transaction(
|
|
1734
|
+
uint64(150),
|
|
1735
|
+
ph,
|
|
1736
|
+
action_scope,
|
|
1737
|
+
fee=uint64(0),
|
|
1738
|
+
coins={coins[5].coin},
|
|
1739
|
+
extra_conditions=(e_announcement,),
|
|
1740
|
+
)
|
|
1741
|
+
[tx_d, tx_f] = action_scope.side_effects.transactions
|
|
1742
|
+
assert tx_d.spend_bundle is not None
|
|
1743
|
+
assert tx_f.spend_bundle is not None
|
|
1744
|
+
# Create transaction E now that spends e_coin to create another eligible
|
|
1745
|
+
# coin as well as the announcement consumed by D and F
|
|
1746
|
+
conditions: list[list[Any]] = [
|
|
1747
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42],
|
|
1748
|
+
[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, message],
|
|
1749
|
+
]
|
|
1750
|
+
sb_e = spend_bundle_from_conditions(conditions, e_coin)
|
|
1751
|
+
# Send DE and EF combinations to the mempool
|
|
1752
|
+
sb_de = SpendBundle.aggregate([tx_d.spend_bundle, sb_e])
|
|
1753
|
+
sb_de_name = sb_de.name()
|
|
1754
|
+
await send_to_mempool(full_node_api, sb_de)
|
|
1755
|
+
sb_ef = SpendBundle.aggregate([sb_e, tx_f.spend_bundle])
|
|
1756
|
+
sb_ef_name = sb_ef.name()
|
|
1757
|
+
await send_to_mempool(full_node_api, sb_ef)
|
|
1758
|
+
# Send also a transaction EG that spends E differently from DE and EF,
|
|
1759
|
+
# so that it doesn't get deduplicated on E with them
|
|
1760
|
+
conditions = [
|
|
1761
|
+
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, e_coin.amount - 1],
|
|
1762
|
+
[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, message],
|
|
1763
|
+
]
|
|
1764
|
+
sb_e2 = spend_bundle_from_conditions(conditions, e_coin)
|
|
1765
|
+
g_coin = coins[6].coin
|
|
1766
|
+
g_coin_id = g_coin.name()
|
|
1767
|
+
async with wallet.wallet_state_manager.new_action_scope(
|
|
1768
|
+
DEFAULT_TX_CONFIG, push=False, merge_spends=False, sign=True
|
|
1769
|
+
) as action_scope:
|
|
1770
|
+
await wallet.generate_signed_transaction(
|
|
1771
|
+
uint64(13), ph, action_scope, coins={g_coin}, extra_conditions=(e_announcement,)
|
|
1772
|
+
)
|
|
1773
|
+
[tx_g] = action_scope.side_effects.transactions
|
|
1774
|
+
assert tx_g.spend_bundle is not None
|
|
1775
|
+
sb_e2g = SpendBundle.aggregate([sb_e2, tx_g.spend_bundle])
|
|
1776
|
+
sb_e2g_name = sb_e2g.name()
|
|
1777
|
+
await send_to_mempool(full_node_api, sb_e2g)
|
|
1778
|
+
|
|
1779
|
+
# Make sure our coin IDs to spend bundles mappings are correct
|
|
1780
|
+
assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == {sb_de_name}
|
|
1781
|
+
assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == {sb_de_name, sb_ef_name, sb_e2g_name}
|
|
1782
|
+
assert get_sb_names_by_coin_id(full_node_api, coins[5].coin.name()) == {sb_ef_name}
|
|
1783
|
+
assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == {sb_e2g_name}
|
|
1784
|
+
|
|
1785
|
+
await farm_a_block(full_node_api, wallet_node, ph)
|
|
1786
|
+
|
|
1787
|
+
# Make sure sb_de and sb_ef coins, including the deduplicated one, are removed
|
|
1788
|
+
# from the coin IDs to spend bundles mappings with the creation of a new block
|
|
1789
|
+
assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == set()
|
|
1790
|
+
assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == set()
|
|
1791
|
+
assert get_sb_names_by_coin_id(full_node_api, coins[5].coin.name()) == set()
|
|
1792
|
+
assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == set()
|
|
1793
|
+
|
|
1794
|
+
# Make sure coin G remains because E2G was removed as E got spent differently (by DE and EF)
|
|
1795
|
+
coins_set = await wallet_node.wallet_state_manager.coin_store.get_unspent_coins_for_wallet(1)
|
|
1796
|
+
assert g_coin in (c.coin for c in coins_set)
|
|
1797
|
+
# Only the newly created eligible coin is left now
|
|
1798
|
+
eligible_coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
|
|
1799
|
+
False, IDENTITY_PUZZLE_HASH
|
|
1800
|
+
)
|
|
1801
|
+
assert len(eligible_coins) == 1
|
|
1802
|
+
assert eligible_coins[0].coin.amount == 42
|
|
1803
|
+
|
|
1804
|
+
|
|
1805
|
+
# we have two coins in this test. They have different birth heights (and
|
|
1806
|
+
# timestamps)
|
|
1807
|
+
# coin1: amount=1, confirmed_height=10, timestamp=1000
|
|
1808
|
+
# coin2: amount=2, confirmed_height=20, timestamp=2000
|
|
1809
|
+
# the mempool is at height 21 and timestamp 2010
|
|
1810
|
+
@pytest.mark.anyio
|
|
1811
|
+
@pytest.mark.parametrize(
|
|
1812
|
+
"cond1,cond2,expected",
|
|
1813
|
+
[
|
|
1814
|
+
# ASSERT HEIGHT ABSOLUTE
|
|
1815
|
+
(
|
|
1816
|
+
[co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 30],
|
|
1817
|
+
[co.ASSERT_HEIGHT_ABSOLUTE, 30],
|
|
1818
|
+
Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
|
|
1819
|
+
),
|
|
1820
|
+
(
|
|
1821
|
+
[co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 31],
|
|
1822
|
+
[co.ASSERT_HEIGHT_ABSOLUTE, 30],
|
|
1823
|
+
None,
|
|
1824
|
+
),
|
|
1825
|
+
(
|
|
1826
|
+
[co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 21],
|
|
1827
|
+
[co.ASSERT_HEIGHT_ABSOLUTE, 20],
|
|
1828
|
+
Err.ASSERT_BEFORE_HEIGHT_ABSOLUTE_FAILED,
|
|
1829
|
+
),
|
|
1830
|
+
# ASSERT SECONDS ABSOLUTE
|
|
1831
|
+
(
|
|
1832
|
+
[co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3000],
|
|
1833
|
+
[co.ASSERT_SECONDS_ABSOLUTE, 3000],
|
|
1834
|
+
Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
|
|
1835
|
+
),
|
|
1836
|
+
(
|
|
1837
|
+
[co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3001],
|
|
1838
|
+
[co.ASSERT_SECONDS_ABSOLUTE, 3000],
|
|
1839
|
+
Err.ASSERT_SECONDS_ABSOLUTE_FAILED,
|
|
1840
|
+
),
|
|
1841
|
+
(
|
|
1842
|
+
[co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 2001],
|
|
1843
|
+
[co.ASSERT_SECONDS_ABSOLUTE, 2000],
|
|
1844
|
+
Err.ASSERT_BEFORE_SECONDS_ABSOLUTE_FAILED,
|
|
1845
|
+
),
|
|
1846
|
+
# ASSERT HEIGHT RELATIVE
|
|
1847
|
+
# coin1: height=10
|
|
1848
|
+
# coin2: height=20
|
|
1849
|
+
(
|
|
1850
|
+
[co.ASSERT_BEFORE_HEIGHT_RELATIVE, 15],
|
|
1851
|
+
[co.ASSERT_HEIGHT_RELATIVE, 5],
|
|
1852
|
+
Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
|
|
1853
|
+
),
|
|
1854
|
+
(
|
|
1855
|
+
[co.ASSERT_BEFORE_HEIGHT_RELATIVE, 26],
|
|
1856
|
+
[co.ASSERT_HEIGHT_RELATIVE, 15],
|
|
1857
|
+
None,
|
|
1858
|
+
),
|
|
1859
|
+
(
|
|
1860
|
+
[co.ASSERT_BEFORE_HEIGHT_RELATIVE, 16],
|
|
1861
|
+
[co.ASSERT_HEIGHT_RELATIVE, 5],
|
|
1862
|
+
None,
|
|
1863
|
+
),
|
|
1864
|
+
# ASSERT SECONDS RELATIVE
|
|
1865
|
+
# coin1: timestamp=1000
|
|
1866
|
+
# coin2: timestamp=2000
|
|
1867
|
+
(
|
|
1868
|
+
[co.ASSERT_BEFORE_SECONDS_RELATIVE, 1500],
|
|
1869
|
+
[co.ASSERT_SECONDS_RELATIVE, 500],
|
|
1870
|
+
Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
|
|
1871
|
+
),
|
|
1872
|
+
# we don't have a pending cache for seconds timelocks, so these fail
|
|
1873
|
+
# immediately
|
|
1874
|
+
(
|
|
1875
|
+
[co.ASSERT_BEFORE_SECONDS_RELATIVE, 2501],
|
|
1876
|
+
[co.ASSERT_SECONDS_RELATIVE, 1500],
|
|
1877
|
+
Err.ASSERT_SECONDS_RELATIVE_FAILED,
|
|
1878
|
+
),
|
|
1879
|
+
(
|
|
1880
|
+
[co.ASSERT_BEFORE_SECONDS_RELATIVE, 1501],
|
|
1881
|
+
[co.ASSERT_SECONDS_RELATIVE, 500],
|
|
1882
|
+
Err.ASSERT_SECONDS_RELATIVE_FAILED,
|
|
1883
|
+
),
|
|
1884
|
+
# ASSERT HEIGHT RELATIVE and ASSERT HEIGHT ABSOLUTE
|
|
1885
|
+
# coin1: height=10
|
|
1886
|
+
# coin2: height=20
|
|
1887
|
+
(
|
|
1888
|
+
[co.ASSERT_BEFORE_HEIGHT_RELATIVE, 20],
|
|
1889
|
+
[co.ASSERT_HEIGHT_ABSOLUTE, 30],
|
|
1890
|
+
Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
|
|
1891
|
+
),
|
|
1892
|
+
(
|
|
1893
|
+
[co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 30],
|
|
1894
|
+
[co.ASSERT_HEIGHT_RELATIVE, 10],
|
|
1895
|
+
Err.IMPOSSIBLE_HEIGHT_ABSOLUTE_CONSTRAINTS,
|
|
1896
|
+
),
|
|
1897
|
+
(
|
|
1898
|
+
[co.ASSERT_BEFORE_HEIGHT_RELATIVE, 21],
|
|
1899
|
+
[co.ASSERT_HEIGHT_ABSOLUTE, 30],
|
|
1900
|
+
None,
|
|
1901
|
+
),
|
|
1902
|
+
(
|
|
1903
|
+
[co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, 31],
|
|
1904
|
+
[co.ASSERT_HEIGHT_RELATIVE, 10],
|
|
1905
|
+
None,
|
|
1906
|
+
),
|
|
1907
|
+
# ASSERT SECONDS ABSOLUTE and ASSERT SECONDS RELATIVE
|
|
1908
|
+
(
|
|
1909
|
+
[co.ASSERT_BEFORE_SECONDS_RELATIVE, 2000],
|
|
1910
|
+
[co.ASSERT_SECONDS_ABSOLUTE, 3000],
|
|
1911
|
+
Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
|
|
1912
|
+
),
|
|
1913
|
+
(
|
|
1914
|
+
[co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3000],
|
|
1915
|
+
[co.ASSERT_SECONDS_RELATIVE, 1000],
|
|
1916
|
+
Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS,
|
|
1917
|
+
),
|
|
1918
|
+
# we don't have a pending cache for seconds timelocks, so these fail
|
|
1919
|
+
# immediately
|
|
1920
|
+
(
|
|
1921
|
+
[co.ASSERT_BEFORE_SECONDS_RELATIVE, 2001],
|
|
1922
|
+
[co.ASSERT_SECONDS_ABSOLUTE, 3000],
|
|
1923
|
+
Err.ASSERT_SECONDS_ABSOLUTE_FAILED,
|
|
1924
|
+
),
|
|
1925
|
+
(
|
|
1926
|
+
[co.ASSERT_BEFORE_SECONDS_ABSOLUTE, 3001],
|
|
1927
|
+
[co.ASSERT_SECONDS_RELATIVE, 1000],
|
|
1928
|
+
Err.ASSERT_SECONDS_RELATIVE_FAILED,
|
|
1929
|
+
),
|
|
1930
|
+
],
|
|
1931
|
+
)
|
|
1932
|
+
async def test_mempool_timelocks(cond1: list[object], cond2: list[object], expected: Optional[Err]) -> None:
|
|
1933
|
+
coins = []
|
|
1934
|
+
test_coin_records = {}
|
|
1935
|
+
|
|
1936
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(1))
|
|
1937
|
+
coins.append(coin)
|
|
1938
|
+
test_coin_records[coin.name()] = CoinRecord(coin, uint32(10), uint32(0), False, uint64(1000))
|
|
1939
|
+
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(2))
|
|
1940
|
+
coins.append(coin)
|
|
1941
|
+
test_coin_records[coin.name()] = CoinRecord(coin, uint32(20), uint32(0), False, uint64(2000))
|
|
1942
|
+
|
|
1943
|
+
async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
|
1944
|
+
ret: list[CoinRecord] = []
|
|
1945
|
+
for name in coin_ids:
|
|
1946
|
+
r = test_coin_records.get(name)
|
|
1947
|
+
if r is not None:
|
|
1948
|
+
ret.append(r)
|
|
1949
|
+
return ret
|
|
1950
|
+
|
|
1951
|
+
mempool_manager = await instantiate_mempool_manager(
|
|
1952
|
+
get_coin_records, block_height=uint32(21), block_timestamp=uint64(2010)
|
|
1953
|
+
)
|
|
1954
|
+
|
|
1955
|
+
coin_spends = [
|
|
1956
|
+
make_spend(coins[0], IDENTITY_PUZZLE, Program.to([cond1])),
|
|
1957
|
+
make_spend(coins[1], IDENTITY_PUZZLE, Program.to([cond2])),
|
|
1958
|
+
]
|
|
1959
|
+
|
|
1960
|
+
bundle = SpendBundle(coin_spends, G2Element())
|
|
1961
|
+
bundle_name = bundle.name()
|
|
1962
|
+
try:
|
|
1963
|
+
result = await add_spendbundle(mempool_manager, bundle, bundle_name)
|
|
1964
|
+
print(result)
|
|
1965
|
+
if expected is not None:
|
|
1966
|
+
assert result == (None, MempoolInclusionStatus.FAILED, expected)
|
|
1967
|
+
else:
|
|
1968
|
+
assert result[0] is not None
|
|
1969
|
+
assert result[1] != MempoolInclusionStatus.FAILED
|
|
1970
|
+
except ValidationError as e:
|
|
1971
|
+
assert e.code == expected
|
|
1972
|
+
|
|
1973
|
+
|
|
1974
|
+
TEST_FILL_RATE_ITEM_COST = 144_720_020
|
|
1975
|
+
TEST_COST_PER_BYTE = 12_000
|
|
1976
|
+
TEST_BLOCK_OVERHEAD = QUOTE_BYTES * TEST_COST_PER_BYTE + QUOTE_EXECUTION_COST
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
@pytest.mark.anyio
|
|
1980
|
+
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.HARD_FORK_2_0])
|
|
1981
|
+
@pytest.mark.parametrize(
|
|
1982
|
+
"max_block_clvm_cost, expected_block_items, expected_block_cost",
|
|
1983
|
+
[
|
|
1984
|
+
# Here we set the block cost limit to twice the test items' cost, so we
|
|
1985
|
+
# expect both test items to get included in the block.
|
|
1986
|
+
# NOTE: The expected block cost is smaller than the sum of items' costs
|
|
1987
|
+
# because of the spend bundle aggregation that creates the block
|
|
1988
|
+
# bundle, in addition to a small block compression effect that we
|
|
1989
|
+
# can't completely avoid.
|
|
1990
|
+
(TEST_FILL_RATE_ITEM_COST * 2, 2, TEST_FILL_RATE_ITEM_COST * 2 - 107_980),
|
|
1991
|
+
# Here we set the block cost limit to twice the test items' cost - 1,
|
|
1992
|
+
# so we expect only one of the two test items to get included in the block.
|
|
1993
|
+
# NOTE: The cost difference here is because get_conditions_from_spendbundle
|
|
1994
|
+
# does not include the block overhead.
|
|
1995
|
+
(TEST_FILL_RATE_ITEM_COST * 2 - 1, 1, TEST_FILL_RATE_ITEM_COST + TEST_BLOCK_OVERHEAD),
|
|
1996
|
+
],
|
|
1997
|
+
)
|
|
1998
|
+
async def test_fill_rate_block_validation(
|
|
1999
|
+
blockchain_constants: ConsensusConstants,
|
|
2000
|
+
max_block_clvm_cost: uint64,
|
|
2001
|
+
expected_block_items: int,
|
|
2002
|
+
expected_block_cost: uint64,
|
|
2003
|
+
) -> None:
|
|
2004
|
+
"""
|
|
2005
|
+
This test covers the case where we set the fill rate to 100% and ensure
|
|
2006
|
+
that we wouldn't generate a block that exceed the maximum block cost limit.
|
|
2007
|
+
In the first scenario, we set the block cost limit to match the test items'
|
|
2008
|
+
costs sum, expecting both test items to get included in the block.
|
|
2009
|
+
In the second scenario, we reduce the maximum block cost limit by one,
|
|
2010
|
+
expecting only one of the two test items to get included in the block.
|
|
2011
|
+
"""
|
|
2012
|
+
|
|
2013
|
+
async def send_to_mempool(full_node: FullNodeSimulator, spend_bundle: SpendBundle) -> None:
|
|
2014
|
+
res = await full_node.send_transaction(wallet_protocol.SendTransaction(spend_bundle))
|
|
2015
|
+
assert res is not None and ProtocolMessageTypes(res.type) == ProtocolMessageTypes.transaction_ack
|
|
2016
|
+
res_parsed = wallet_protocol.TransactionAck.from_bytes(res.data)
|
|
2017
|
+
assert res_parsed.status == MempoolInclusionStatus.SUCCESS.value
|
|
2018
|
+
|
|
2019
|
+
async def fill_mempool_with_test_sbs(
|
|
2020
|
+
full_node_api: FullNodeSimulator,
|
|
2021
|
+
) -> list[tuple[bytes32, SerializedProgram, bytes32]]:
|
|
2022
|
+
coins_and_puzzles = []
|
|
2023
|
+
# Create different puzzles and use different (parent) coins to reduce
|
|
2024
|
+
# the effects of block compression as much as possible.
|
|
2025
|
+
for i in (1, 2):
|
|
2026
|
+
puzzle = SerializedProgram.to((1, [[ConditionOpcode.REMARK, bytes([i] * 12_000)]]))
|
|
2027
|
+
ph = puzzle.get_tree_hash()
|
|
2028
|
+
for _ in range(2):
|
|
2029
|
+
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
2030
|
+
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, ph)
|
|
2031
|
+
coin = next(cr.coin for cr in coin_records if cr.coin.amount == 250_000_000_000)
|
|
2032
|
+
coins_and_puzzles.append((coin, puzzle))
|
|
2033
|
+
sbs_info = []
|
|
2034
|
+
for coin, puzzle in coins_and_puzzles:
|
|
2035
|
+
coin_spend = make_spend(coin, puzzle, SerializedProgram.to([]))
|
|
2036
|
+
sb = SpendBundle([coin_spend], G2Element())
|
|
2037
|
+
await send_to_mempool(full_node_api, sb)
|
|
2038
|
+
sbs_info.append((coin.name(), puzzle, sb.name()))
|
|
2039
|
+
return sbs_info
|
|
2040
|
+
|
|
2041
|
+
constants = blockchain_constants.replace(MAX_BLOCK_COST_CLVM=max_block_clvm_cost)
|
|
2042
|
+
async with setup_simulators_and_wallets(1, 0, constants) as setup:
|
|
2043
|
+
full_node_api = setup.simulators[0].peer_api
|
|
2044
|
+
assert full_node_api.full_node._mempool_manager is not None
|
|
2045
|
+
# We have to alter the following values here as they're not exposed elsewhere
|
|
2046
|
+
# and without them we won't be able to get the test bundle in.
|
|
2047
|
+
# This defaults to `MAX_BLOCK_COST_CLVM // 2`
|
|
2048
|
+
full_node_api.full_node._mempool_manager.max_tx_clvm_cost = max_block_clvm_cost
|
|
2049
|
+
# This defaults to `MAX_BLOCK_COST_CLVM - BLOCK_OVERHEAD`
|
|
2050
|
+
full_node_api.full_node._mempool_manager.mempool.mempool_info = dataclasses.replace(
|
|
2051
|
+
full_node_api.full_node._mempool_manager.mempool.mempool_info,
|
|
2052
|
+
max_block_clvm_cost=CLVMCost(max_block_clvm_cost),
|
|
2053
|
+
)
|
|
2054
|
+
sbs_info = await fill_mempool_with_test_sbs(full_node_api)
|
|
2055
|
+
# This check is here just to make sure our bundles have the expected cost
|
|
2056
|
+
for sb_info in sbs_info:
|
|
2057
|
+
_, _, sb_name = sb_info
|
|
2058
|
+
mi = full_node_api.full_node.mempool_manager.get_mempool_item(sb_name)
|
|
2059
|
+
assert mi is not None
|
|
2060
|
+
assert mi.cost == TEST_FILL_RATE_ITEM_COST
|
|
2061
|
+
# Farm the block to make sure we're passing block validation
|
|
2062
|
+
current_peak = full_node_api.full_node.blockchain.get_peak()
|
|
2063
|
+
assert current_peak is not None
|
|
2064
|
+
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(IDENTITY_PUZZLE_HASH))
|
|
2065
|
+
# Check that our resulting block is what we expect
|
|
2066
|
+
peak = full_node_api.full_node.blockchain.get_peak()
|
|
2067
|
+
assert peak is not None
|
|
2068
|
+
# Check for the peak change after farming the block
|
|
2069
|
+
assert peak.prev_hash == current_peak.header_hash
|
|
2070
|
+
# Check our coin(s)
|
|
2071
|
+
for i in range(expected_block_items):
|
|
2072
|
+
coin_name, puzzle, _ = sbs_info[i]
|
|
2073
|
+
rps_res = await full_node_api.request_puzzle_solution(
|
|
2074
|
+
wallet_protocol.RequestPuzzleSolution(coin_name, peak.height)
|
|
2075
|
+
)
|
|
2076
|
+
assert rps_res is not None
|
|
2077
|
+
rps_res_parsed = wallet_protocol.RespondPuzzleSolution.from_bytes(rps_res.data)
|
|
2078
|
+
assert rps_res_parsed.response.puzzle == puzzle
|
|
2079
|
+
# Check the block cost
|
|
2080
|
+
rb_res = await full_node_api.request_block(RequestBlock(peak.height, True))
|
|
2081
|
+
assert rb_res is not None
|
|
2082
|
+
rb_res_parsed = RespondBlock.from_bytes(rb_res.data)
|
|
2083
|
+
assert rb_res_parsed.block.transactions_info is not None
|
|
2084
|
+
assert rb_res_parsed.block.transactions_info.cost == expected_block_cost
|