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,3255 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import logging
|
|
5
|
+
import random
|
|
6
|
+
from typing import Callable, Optional
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from chia_rs import G1Element, G2Element, get_flags_for_height_and_constants
|
|
10
|
+
from clvm.casts import int_to_bytes
|
|
11
|
+
from clvm_tools import binutils
|
|
12
|
+
from clvm_tools.binutils import assemble
|
|
13
|
+
|
|
14
|
+
from chia._tests.blockchain.blockchain_test_utils import _validate_and_add_block
|
|
15
|
+
from chia._tests.connection_utils import add_dummy_connection, connect_and_get_peer
|
|
16
|
+
from chia._tests.core.mempool.test_mempool_manager import (
|
|
17
|
+
IDENTITY_PUZZLE_HASH,
|
|
18
|
+
TEST_COIN,
|
|
19
|
+
assert_sb_in_pool,
|
|
20
|
+
assert_sb_not_in_pool,
|
|
21
|
+
make_test_coins,
|
|
22
|
+
mempool_item_from_spendbundle,
|
|
23
|
+
mk_item,
|
|
24
|
+
spend_bundle_from_conditions,
|
|
25
|
+
)
|
|
26
|
+
from chia._tests.core.node_height import node_height_at_least
|
|
27
|
+
from chia._tests.util.get_name_puzzle_conditions import get_name_puzzle_conditions
|
|
28
|
+
from chia._tests.util.misc import BenchmarkRunner, invariant_check_mempool
|
|
29
|
+
from chia._tests.util.time_out_assert import time_out_assert
|
|
30
|
+
from chia.consensus.condition_costs import ConditionCost
|
|
31
|
+
from chia.consensus.cost_calculator import NPCResult
|
|
32
|
+
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
33
|
+
from chia.full_node.bitcoin_fee_estimator import create_bitcoin_fee_estimator
|
|
34
|
+
from chia.full_node.fee_estimation import EmptyMempoolInfo, MempoolInfo
|
|
35
|
+
from chia.full_node.full_node_api import FullNodeAPI
|
|
36
|
+
from chia.full_node.mempool import Mempool
|
|
37
|
+
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
|
|
38
|
+
from chia.full_node.mempool_manager import MEMPOOL_MIN_FEE_INCREASE
|
|
39
|
+
from chia.full_node.pending_tx_cache import ConflictTxCache, PendingTxCache
|
|
40
|
+
from chia.protocols import full_node_protocol, wallet_protocol
|
|
41
|
+
from chia.protocols.wallet_protocol import TransactionAck
|
|
42
|
+
from chia.server.api_protocol import ApiMetadata
|
|
43
|
+
from chia.server.outbound_message import Message
|
|
44
|
+
from chia.server.server import ChiaServer
|
|
45
|
+
from chia.server.ws_connection import WSChiaConnection
|
|
46
|
+
from chia.simulator.add_blocks_in_batches import add_blocks_in_batches
|
|
47
|
+
from chia.simulator.block_tools import BlockTools, test_constants
|
|
48
|
+
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
49
|
+
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
50
|
+
from chia.simulator.wallet_tools import WalletTool
|
|
51
|
+
from chia.types.blockchain_format.coin import Coin
|
|
52
|
+
from chia.types.blockchain_format.program import Program
|
|
53
|
+
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
54
|
+
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
55
|
+
from chia.types.clvm_cost import CLVMCost
|
|
56
|
+
from chia.types.coin_spend import CoinSpend, make_spend
|
|
57
|
+
from chia.types.condition_opcodes import ConditionOpcode
|
|
58
|
+
from chia.types.condition_with_args import ConditionWithArgs
|
|
59
|
+
from chia.types.eligible_coin_spends import UnspentLineageInfo, run_for_cost
|
|
60
|
+
from chia.types.fee_rate import FeeRate
|
|
61
|
+
from chia.types.full_block import FullBlock
|
|
62
|
+
from chia.types.generator_types import BlockGenerator
|
|
63
|
+
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
64
|
+
from chia.types.mempool_item import MempoolItem
|
|
65
|
+
from chia.types.spend_bundle import SpendBundle, estimate_fees
|
|
66
|
+
from chia.types.spend_bundle_conditions import SpendBundleConditions
|
|
67
|
+
from chia.util.errors import Err
|
|
68
|
+
from chia.util.hash import std_hash
|
|
69
|
+
from chia.util.ints import uint32, uint64
|
|
70
|
+
from chia.util.recursive_replace import recursive_replace
|
|
71
|
+
from chia.wallet.conditions import AssertCoinAnnouncement, AssertPuzzleAnnouncement
|
|
72
|
+
|
|
73
|
+
BURN_PUZZLE_HASH = bytes32(b"0" * 32)
|
|
74
|
+
BURN_PUZZLE_HASH_2 = bytes32(b"1" * 32)
|
|
75
|
+
|
|
76
|
+
log = logging.getLogger(__name__)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def new_mi(mi: MempoolInfo, max_mempool_cost: int, min_replace_fee_per_cost: int) -> MempoolInfo:
|
|
80
|
+
return dataclasses.replace(
|
|
81
|
+
mi,
|
|
82
|
+
minimum_fee_per_cost_to_replace=FeeRate(uint64(min_replace_fee_per_cost)),
|
|
83
|
+
max_size_in_cost=CLVMCost(uint64(max_mempool_cost)),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.fixture(scope="module")
|
|
88
|
+
def wallet_a(bt: BlockTools) -> WalletTool:
|
|
89
|
+
return bt.get_pool_wallet_tool()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def generate_test_spend_bundle(
|
|
93
|
+
wallet: WalletTool,
|
|
94
|
+
coin: Coin,
|
|
95
|
+
condition_dic: Optional[dict[ConditionOpcode, list[ConditionWithArgs]]] = None,
|
|
96
|
+
fee: uint64 = uint64(0),
|
|
97
|
+
amount: uint64 = uint64(1000),
|
|
98
|
+
new_puzzle_hash: bytes32 = BURN_PUZZLE_HASH,
|
|
99
|
+
) -> SpendBundle:
|
|
100
|
+
if condition_dic is None:
|
|
101
|
+
condition_dic = {}
|
|
102
|
+
transaction = wallet.generate_signed_transaction(amount, new_puzzle_hash, coin, condition_dic, fee)
|
|
103
|
+
assert transaction is not None
|
|
104
|
+
return transaction
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def make_item(
|
|
108
|
+
idx: int, cost: uint64 = uint64(80), assert_height: uint32 = uint32(100), fee: uint64 = uint64(0)
|
|
109
|
+
) -> MempoolItem:
|
|
110
|
+
spend_bundle_name = bytes32([idx] * 32)
|
|
111
|
+
return MempoolItem(
|
|
112
|
+
SpendBundle([], G2Element()),
|
|
113
|
+
fee,
|
|
114
|
+
SpendBundleConditions([], 0, 0, 0, None, None, [], cost, 0, 0, False, 0, 0),
|
|
115
|
+
spend_bundle_name,
|
|
116
|
+
uint32(0),
|
|
117
|
+
assert_height,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class TestConflictTxCache:
|
|
122
|
+
def test_recall(self) -> None:
|
|
123
|
+
c = ConflictTxCache(100)
|
|
124
|
+
item = make_item(1)
|
|
125
|
+
c.add(item)
|
|
126
|
+
assert c.get(item.name) == item
|
|
127
|
+
tx = c.drain()
|
|
128
|
+
assert tx == {item.spend_bundle_name: item}
|
|
129
|
+
|
|
130
|
+
def test_fifo_limit(self) -> None:
|
|
131
|
+
c = ConflictTxCache(200)
|
|
132
|
+
# each item has cost 80
|
|
133
|
+
items = [make_item(i) for i in range(1, 4)]
|
|
134
|
+
for i in items:
|
|
135
|
+
c.add(i)
|
|
136
|
+
# the max cost is 200, only two transactions will fit
|
|
137
|
+
# we evict items FIFO, so the to most recently added will be left
|
|
138
|
+
tx = c.drain()
|
|
139
|
+
assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
|
|
140
|
+
|
|
141
|
+
def test_item_limit(self) -> None:
|
|
142
|
+
c = ConflictTxCache(1000000, 2)
|
|
143
|
+
# each item has cost 80
|
|
144
|
+
items = [make_item(i) for i in range(1, 4)]
|
|
145
|
+
for i in items:
|
|
146
|
+
c.add(i)
|
|
147
|
+
# the max size is 2, only two transactions will fit
|
|
148
|
+
# we evict items FIFO, so the to most recently added will be left
|
|
149
|
+
tx = c.drain()
|
|
150
|
+
assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
|
|
151
|
+
|
|
152
|
+
def test_drain(self) -> None:
|
|
153
|
+
c = ConflictTxCache(100)
|
|
154
|
+
item = make_item(1)
|
|
155
|
+
c.add(item)
|
|
156
|
+
tx = c.drain()
|
|
157
|
+
assert tx == {item.spend_bundle_name: item}
|
|
158
|
+
|
|
159
|
+
# drain will clear the cache, so a second call will be empty
|
|
160
|
+
tx = c.drain()
|
|
161
|
+
assert tx == {}
|
|
162
|
+
|
|
163
|
+
def test_cost(self) -> None:
|
|
164
|
+
c = ConflictTxCache(200)
|
|
165
|
+
assert c.cost() == 0
|
|
166
|
+
item1 = make_item(1)
|
|
167
|
+
c.add(item1)
|
|
168
|
+
# each item has cost 80
|
|
169
|
+
assert c.cost() == 80
|
|
170
|
+
|
|
171
|
+
item2 = make_item(2)
|
|
172
|
+
c.add(item2)
|
|
173
|
+
assert c.cost() == 160
|
|
174
|
+
|
|
175
|
+
# the first item is evicted, so the cost stays the same
|
|
176
|
+
item3 = make_item(3)
|
|
177
|
+
c.add(item3)
|
|
178
|
+
assert c.cost() == 160
|
|
179
|
+
|
|
180
|
+
tx = c.drain()
|
|
181
|
+
assert tx == {item2.spend_bundle_name: item2, item3.spend_bundle_name: item3}
|
|
182
|
+
|
|
183
|
+
assert c.cost() == 0
|
|
184
|
+
item4 = make_item(4)
|
|
185
|
+
c.add(item4)
|
|
186
|
+
assert c.cost() == 80
|
|
187
|
+
|
|
188
|
+
tx = c.drain()
|
|
189
|
+
assert tx == {item4.spend_bundle_name: item4}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class TestPendingTxCache:
|
|
193
|
+
def test_recall(self) -> None:
|
|
194
|
+
c = PendingTxCache(100)
|
|
195
|
+
item = make_item(1)
|
|
196
|
+
c.add(item)
|
|
197
|
+
assert c.get(item.name) == item
|
|
198
|
+
tx = c.drain(uint32(101))
|
|
199
|
+
assert tx == {item.spend_bundle_name: item}
|
|
200
|
+
|
|
201
|
+
def test_fifo_limit(self) -> None:
|
|
202
|
+
c = PendingTxCache(200)
|
|
203
|
+
# each item has cost 80
|
|
204
|
+
items = [make_item(i) for i in range(1, 4)]
|
|
205
|
+
for i in items:
|
|
206
|
+
c.add(i)
|
|
207
|
+
# the max cost is 200, only two transactions will fit
|
|
208
|
+
# the eviction is FIFO because all items have the same assert_height
|
|
209
|
+
tx = c.drain(uint32(101))
|
|
210
|
+
assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
|
|
211
|
+
|
|
212
|
+
def test_add_eviction(self) -> None:
|
|
213
|
+
c = PendingTxCache(160)
|
|
214
|
+
item = make_item(1)
|
|
215
|
+
c.add(item)
|
|
216
|
+
|
|
217
|
+
for i in range(3):
|
|
218
|
+
item = make_item(i + 1, assert_height=uint32(50))
|
|
219
|
+
c.add(item)
|
|
220
|
+
|
|
221
|
+
txs = c.drain(uint32(161))
|
|
222
|
+
assert len(txs) == 2
|
|
223
|
+
for tx in txs.values():
|
|
224
|
+
assert tx.assert_height == 50
|
|
225
|
+
|
|
226
|
+
def test_item_limit(self) -> None:
|
|
227
|
+
c = PendingTxCache(1000000, 2)
|
|
228
|
+
# each item has cost 80
|
|
229
|
+
items = [make_item(i) for i in range(1, 4)]
|
|
230
|
+
for i in items:
|
|
231
|
+
c.add(i)
|
|
232
|
+
# the max size is 2, only two transactions will fit
|
|
233
|
+
# the eviction is FIFO because all items have the same assert_height
|
|
234
|
+
tx = c.drain(uint32(101))
|
|
235
|
+
assert tx == {items[-2].spend_bundle_name: items[-2], items[-1].spend_bundle_name: items[-1]}
|
|
236
|
+
|
|
237
|
+
def test_drain(self) -> None:
|
|
238
|
+
c = PendingTxCache(100)
|
|
239
|
+
item = make_item(1)
|
|
240
|
+
c.add(item)
|
|
241
|
+
tx = c.drain(uint32(101))
|
|
242
|
+
assert tx == {item.spend_bundle_name: item}
|
|
243
|
+
|
|
244
|
+
# drain will clear the cache, so a second call will be empty
|
|
245
|
+
tx = c.drain(uint32(101))
|
|
246
|
+
assert tx == {}
|
|
247
|
+
|
|
248
|
+
def test_cost(self) -> None:
|
|
249
|
+
c = PendingTxCache(200)
|
|
250
|
+
assert c.cost() == 0
|
|
251
|
+
item1 = make_item(1)
|
|
252
|
+
c.add(item1)
|
|
253
|
+
# each item has cost 80
|
|
254
|
+
assert c.cost() == 80
|
|
255
|
+
|
|
256
|
+
item2 = make_item(2)
|
|
257
|
+
c.add(item2)
|
|
258
|
+
assert c.cost() == 160
|
|
259
|
+
|
|
260
|
+
# the first item is evicted, so the cost stays the same
|
|
261
|
+
item3 = make_item(3)
|
|
262
|
+
c.add(item3)
|
|
263
|
+
assert c.cost() == 160
|
|
264
|
+
|
|
265
|
+
tx = c.drain(uint32(101))
|
|
266
|
+
assert tx == {item2.spend_bundle_name: item2, item3.spend_bundle_name: item3}
|
|
267
|
+
|
|
268
|
+
assert c.cost() == 0
|
|
269
|
+
item4 = make_item(4)
|
|
270
|
+
c.add(item4)
|
|
271
|
+
assert c.cost() == 80
|
|
272
|
+
|
|
273
|
+
tx = c.drain(uint32(101))
|
|
274
|
+
assert tx == {item4.spend_bundle_name: item4}
|
|
275
|
+
|
|
276
|
+
def test_drain_height(self) -> None:
|
|
277
|
+
c = PendingTxCache(20000, 1000)
|
|
278
|
+
|
|
279
|
+
# each item has cost 80
|
|
280
|
+
# heights are 100-109
|
|
281
|
+
items = [make_item(i, assert_height=uint32(100 + i)) for i in range(10)]
|
|
282
|
+
for i in items:
|
|
283
|
+
c.add(i)
|
|
284
|
+
|
|
285
|
+
tx = c.drain(uint32(101))
|
|
286
|
+
assert tx == {items[0].spend_bundle_name: items[0]}
|
|
287
|
+
|
|
288
|
+
tx = c.drain(uint32(105))
|
|
289
|
+
assert tx == {
|
|
290
|
+
items[1].spend_bundle_name: items[1],
|
|
291
|
+
items[2].spend_bundle_name: items[2],
|
|
292
|
+
items[3].spend_bundle_name: items[3],
|
|
293
|
+
items[4].spend_bundle_name: items[4],
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
tx = c.drain(uint32(105))
|
|
297
|
+
assert tx == {}
|
|
298
|
+
|
|
299
|
+
tx = c.drain(uint32(110))
|
|
300
|
+
assert tx == {
|
|
301
|
+
items[5].spend_bundle_name: items[5],
|
|
302
|
+
items[6].spend_bundle_name: items[6],
|
|
303
|
+
items[7].spend_bundle_name: items[7],
|
|
304
|
+
items[8].spend_bundle_name: items[8],
|
|
305
|
+
items[9].spend_bundle_name: items[9],
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class TestMempool:
|
|
310
|
+
@pytest.mark.anyio
|
|
311
|
+
async def test_basic_mempool(
|
|
312
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
313
|
+
) -> None:
|
|
314
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
315
|
+
|
|
316
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
317
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
318
|
+
|
|
319
|
+
max_block_cost_clvm = uint64(40000000)
|
|
320
|
+
max_mempool_cost = max_block_cost_clvm * 5
|
|
321
|
+
mempool_info = new_mi(EmptyMempoolInfo, max_mempool_cost, uint64(5))
|
|
322
|
+
fee_estimator = create_bitcoin_fee_estimator(max_block_cost_clvm)
|
|
323
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
324
|
+
assert mempool.get_min_fee_rate(104000) == 0
|
|
325
|
+
|
|
326
|
+
assert mempool.get_min_fee_rate(max_mempool_cost + 1) is None
|
|
327
|
+
|
|
328
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
329
|
+
spend_bundle = generate_test_spend_bundle(wallet_a, coin)
|
|
330
|
+
assert spend_bundle is not None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
metadata = ApiMetadata()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# this (method'ish) function is not designed per normal uses so allowing the ignore
|
|
337
|
+
# for the different return type. normal is Optional[Message]
|
|
338
|
+
@metadata.request(peer_required=True, bytes_required=True) # type: ignore[type-var]
|
|
339
|
+
async def respond_transaction(
|
|
340
|
+
self: FullNodeAPI,
|
|
341
|
+
tx: full_node_protocol.RespondTransaction,
|
|
342
|
+
peer: WSChiaConnection,
|
|
343
|
+
tx_bytes: bytes = b"",
|
|
344
|
+
test: bool = False,
|
|
345
|
+
) -> tuple[MempoolInclusionStatus, Optional[Err]]:
|
|
346
|
+
"""
|
|
347
|
+
Receives a full transaction from peer.
|
|
348
|
+
If tx is added to mempool, send tx_id to others. (new_transaction)
|
|
349
|
+
"""
|
|
350
|
+
assert tx_bytes != b""
|
|
351
|
+
spend_name = std_hash(tx_bytes)
|
|
352
|
+
if spend_name in self.full_node.full_node_store.pending_tx_request:
|
|
353
|
+
self.full_node.full_node_store.pending_tx_request.pop(spend_name)
|
|
354
|
+
if spend_name in self.full_node.full_node_store.peers_with_tx:
|
|
355
|
+
self.full_node.full_node_store.peers_with_tx.pop(spend_name)
|
|
356
|
+
ret = await self.full_node.add_transaction(tx.transaction, spend_name, peer, test)
|
|
357
|
+
invariant_check_mempool(self.full_node.mempool_manager.mempool)
|
|
358
|
+
return ret
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
async def next_block(full_node_1: FullNodeSimulator, wallet_a: WalletTool, bt: BlockTools) -> Coin:
|
|
362
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
363
|
+
# we have to farm a new block here, to ensure every test has a unique coin to test spending.
|
|
364
|
+
# all this could be simplified if the tests did not share a simulation
|
|
365
|
+
start_height = blocks[-1].height
|
|
366
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
367
|
+
blocks = bt.get_consecutive_blocks(
|
|
368
|
+
1,
|
|
369
|
+
block_list_input=blocks,
|
|
370
|
+
guarantee_transaction_block=True,
|
|
371
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
372
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
373
|
+
genesis_timestamp=uint64(10_000),
|
|
374
|
+
time_per_block=10,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
378
|
+
|
|
379
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 1)
|
|
380
|
+
return blocks[-1].get_included_reward_coins()[0]
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
co = ConditionOpcode
|
|
384
|
+
mis = MempoolInclusionStatus
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
async def send_sb(node: FullNodeAPI, sb: SpendBundle) -> Optional[Message]:
|
|
388
|
+
tx = wallet_protocol.SendTransaction(sb)
|
|
389
|
+
return await node.send_transaction(tx, test=True)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
async def gen_and_send_sb(node: FullNodeAPI, wallet: WalletTool, coin: Coin, fee: uint64 = uint64(0)) -> SpendBundle:
|
|
393
|
+
sb = generate_test_spend_bundle(wallet=wallet, coin=coin, fee=fee)
|
|
394
|
+
assert sb is not None
|
|
395
|
+
await send_sb(node, sb)
|
|
396
|
+
return sb
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class TestMempoolManager:
|
|
400
|
+
@pytest.mark.anyio
|
|
401
|
+
async def test_basic_mempool_manager(
|
|
402
|
+
self,
|
|
403
|
+
two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
|
|
404
|
+
wallet_a: WalletTool,
|
|
405
|
+
self_hostname: str,
|
|
406
|
+
) -> None:
|
|
407
|
+
full_node_1, _full_node_2, server_1, server_2, bt = two_nodes_one_block
|
|
408
|
+
|
|
409
|
+
peer = await connect_and_get_peer(server_1, server_2, self_hostname)
|
|
410
|
+
|
|
411
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
412
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
413
|
+
spend_bundle = generate_test_spend_bundle(wallet_a, coin)
|
|
414
|
+
assert spend_bundle is not None
|
|
415
|
+
tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle)
|
|
416
|
+
await full_node_1.respond_transaction(tx, peer, test=True)
|
|
417
|
+
|
|
418
|
+
await time_out_assert(
|
|
419
|
+
10,
|
|
420
|
+
full_node_1.full_node.mempool_manager.get_spendbundle,
|
|
421
|
+
spend_bundle,
|
|
422
|
+
spend_bundle.name(),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
@pytest.mark.anyio
|
|
426
|
+
@pytest.mark.parametrize(
|
|
427
|
+
"opcode,lock_value,expected",
|
|
428
|
+
[
|
|
429
|
+
# the mempool rules don't allow relative height- or time conditions on
|
|
430
|
+
# ephemeral spends
|
|
431
|
+
(co.ASSERT_MY_BIRTH_HEIGHT, -1, mis.FAILED),
|
|
432
|
+
(co.ASSERT_MY_BIRTH_HEIGHT, 0x100000000, mis.FAILED),
|
|
433
|
+
(co.ASSERT_MY_BIRTH_HEIGHT, 5, mis.FAILED),
|
|
434
|
+
(co.ASSERT_MY_BIRTH_HEIGHT, 6, mis.FAILED),
|
|
435
|
+
(co.ASSERT_MY_BIRTH_SECONDS, -1, mis.FAILED),
|
|
436
|
+
(co.ASSERT_MY_BIRTH_SECONDS, 0x10000000000000000, mis.FAILED),
|
|
437
|
+
(co.ASSERT_MY_BIRTH_SECONDS, 10049, mis.FAILED),
|
|
438
|
+
(co.ASSERT_MY_BIRTH_SECONDS, 10050, mis.FAILED),
|
|
439
|
+
(co.ASSERT_MY_BIRTH_SECONDS, 10051, mis.FAILED),
|
|
440
|
+
(co.ASSERT_SECONDS_RELATIVE, -2, mis.FAILED),
|
|
441
|
+
(co.ASSERT_SECONDS_RELATIVE, -1, mis.FAILED),
|
|
442
|
+
(co.ASSERT_SECONDS_RELATIVE, 0, mis.FAILED),
|
|
443
|
+
(co.ASSERT_SECONDS_RELATIVE, 1, mis.FAILED),
|
|
444
|
+
(co.ASSERT_HEIGHT_RELATIVE, -2, mis.FAILED),
|
|
445
|
+
(co.ASSERT_HEIGHT_RELATIVE, -1, mis.FAILED),
|
|
446
|
+
(co.ASSERT_HEIGHT_RELATIVE, 0, mis.FAILED),
|
|
447
|
+
(co.ASSERT_HEIGHT_RELATIVE, 1, mis.FAILED),
|
|
448
|
+
# the absolute height and seconds tests require fresh full nodes to
|
|
449
|
+
# run the test on. The fixture (one_node_one_block) creates a block,
|
|
450
|
+
# then condition_tester2 creates another 3 blocks
|
|
451
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 4, mis.SUCCESS),
|
|
452
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 5, mis.SUCCESS),
|
|
453
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 6, mis.PENDING),
|
|
454
|
+
(co.ASSERT_HEIGHT_ABSOLUTE, 7, mis.PENDING),
|
|
455
|
+
# genesis timestamp is 10000 and each block is 10 seconds
|
|
456
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10049, mis.SUCCESS),
|
|
457
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10050, mis.SUCCESS),
|
|
458
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10051, mis.FAILED),
|
|
459
|
+
(co.ASSERT_SECONDS_ABSOLUTE, 10052, mis.FAILED),
|
|
460
|
+
],
|
|
461
|
+
)
|
|
462
|
+
async def test_ephemeral_timelock(
|
|
463
|
+
self,
|
|
464
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
465
|
+
wallet_a: WalletTool,
|
|
466
|
+
opcode: ConditionOpcode,
|
|
467
|
+
lock_value: int,
|
|
468
|
+
expected: MempoolInclusionStatus,
|
|
469
|
+
) -> None:
|
|
470
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
471
|
+
conditions = {opcode: [ConditionWithArgs(opcode, [int_to_bytes(lock_value)])]}
|
|
472
|
+
tx1 = wallet_a.generate_signed_transaction(uint64(1000000), wallet_a.get_new_puzzlehash(), coin_2)
|
|
473
|
+
|
|
474
|
+
ephemeral_coin: Coin = tx1.additions()[0]
|
|
475
|
+
tx2 = wallet_a.generate_signed_transaction(
|
|
476
|
+
uint64(1000000), wallet_a.get_new_puzzlehash(), ephemeral_coin, conditions.copy(), uint64(0)
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
bundle = SpendBundle.aggregate([tx1, tx2])
|
|
480
|
+
return bundle
|
|
481
|
+
|
|
482
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
483
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
484
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
485
|
+
_blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
486
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
487
|
+
|
|
488
|
+
print(f"opcode={opcode} timelock_value={lock_value} expected={expected} status={status}")
|
|
489
|
+
print(f"status: {status}")
|
|
490
|
+
print(f"error: {err}")
|
|
491
|
+
|
|
492
|
+
assert status == expected
|
|
493
|
+
if expected == MempoolInclusionStatus.SUCCESS:
|
|
494
|
+
assert mempool_bundle == bundle
|
|
495
|
+
assert err is None
|
|
496
|
+
else:
|
|
497
|
+
assert mempool_bundle is None
|
|
498
|
+
assert err is not None
|
|
499
|
+
|
|
500
|
+
# this test makes sure that one spend successfully asserts the announce from
|
|
501
|
+
# another spend, even though the assert condition is duplicated 100 times
|
|
502
|
+
@pytest.mark.anyio
|
|
503
|
+
async def test_coin_announcement_duplicate_consumed(
|
|
504
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
505
|
+
) -> None:
|
|
506
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
507
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
508
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
509
|
+
dic = {cvp.opcode: [cvp] * 100}
|
|
510
|
+
|
|
511
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
512
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
513
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
514
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
515
|
+
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
516
|
+
return bundle
|
|
517
|
+
|
|
518
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
519
|
+
_blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
520
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
521
|
+
|
|
522
|
+
assert err is None
|
|
523
|
+
assert mempool_bundle == bundle
|
|
524
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
525
|
+
|
|
526
|
+
# this test makes sure that one spend successfully asserts the announce from
|
|
527
|
+
# another spend, even though the create announcement is duplicated 100 times
|
|
528
|
+
@pytest.mark.anyio
|
|
529
|
+
async def test_coin_duplicate_announcement_consumed(
|
|
530
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
531
|
+
) -> None:
|
|
532
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
533
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
534
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
535
|
+
dic = {cvp.opcode: [cvp]}
|
|
536
|
+
|
|
537
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
538
|
+
dic2 = {cvp.opcode: [cvp2] * 100}
|
|
539
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
540
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
541
|
+
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
542
|
+
return bundle
|
|
543
|
+
|
|
544
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
545
|
+
_blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
546
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
547
|
+
|
|
548
|
+
assert err is None
|
|
549
|
+
assert mempool_bundle == bundle
|
|
550
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
551
|
+
|
|
552
|
+
@pytest.mark.anyio
|
|
553
|
+
async def test_double_spend(
|
|
554
|
+
self,
|
|
555
|
+
two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
|
|
556
|
+
wallet_a: WalletTool,
|
|
557
|
+
self_hostname: str,
|
|
558
|
+
) -> None:
|
|
559
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
560
|
+
full_node_1, _full_node_2, server_1, server_2, bt = two_nodes_one_block
|
|
561
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
562
|
+
start_height = blocks[-1].height
|
|
563
|
+
blocks = bt.get_consecutive_blocks(
|
|
564
|
+
3,
|
|
565
|
+
block_list_input=blocks,
|
|
566
|
+
guarantee_transaction_block=True,
|
|
567
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
568
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
569
|
+
)
|
|
570
|
+
peer = await connect_and_get_peer(server_1, server_2, self_hostname)
|
|
571
|
+
|
|
572
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
573
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
574
|
+
|
|
575
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, blocks[-1].get_included_reward_coins()[0])
|
|
576
|
+
|
|
577
|
+
assert spend_bundle1 is not None
|
|
578
|
+
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
|
579
|
+
status, err = await respond_transaction(full_node_1, tx1, peer, test=True)
|
|
580
|
+
assert err is None
|
|
581
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
582
|
+
|
|
583
|
+
spend_bundle2 = generate_test_spend_bundle(
|
|
584
|
+
wallet_a,
|
|
585
|
+
blocks[-1].get_included_reward_coins()[0],
|
|
586
|
+
new_puzzle_hash=BURN_PUZZLE_HASH_2,
|
|
587
|
+
)
|
|
588
|
+
assert spend_bundle2 is not None
|
|
589
|
+
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle2)
|
|
590
|
+
status, err = await respond_transaction(full_node_1, tx2, peer, test=True)
|
|
591
|
+
|
|
592
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
593
|
+
sb2 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle2.name())
|
|
594
|
+
|
|
595
|
+
assert err == Err.MEMPOOL_CONFLICT
|
|
596
|
+
assert sb1 == spend_bundle1
|
|
597
|
+
assert sb2 is None
|
|
598
|
+
assert status == MempoolInclusionStatus.PENDING
|
|
599
|
+
|
|
600
|
+
@pytest.mark.anyio
|
|
601
|
+
async def test_double_spend_with_higher_fee(
|
|
602
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
603
|
+
) -> None:
|
|
604
|
+
full_node_1, _, bt = one_node_one_block
|
|
605
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
606
|
+
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
|
607
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
608
|
+
blocks = bt.get_consecutive_blocks(
|
|
609
|
+
3,
|
|
610
|
+
block_list_input=blocks,
|
|
611
|
+
guarantee_transaction_block=True,
|
|
612
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
613
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
617
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
618
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
619
|
+
|
|
620
|
+
coins = iter(blocks[-1].get_included_reward_coins())
|
|
621
|
+
coin1, coin2 = next(coins), next(coins)
|
|
622
|
+
coins = iter(blocks[-2].get_included_reward_coins())
|
|
623
|
+
coin3, coin4 = next(coins), next(coins)
|
|
624
|
+
|
|
625
|
+
sb1_1 = await gen_and_send_sb(full_node_1, wallet_a, coin1)
|
|
626
|
+
sb1_2 = await gen_and_send_sb(full_node_1, wallet_a, coin1, fee=uint64(1))
|
|
627
|
+
|
|
628
|
+
# Fee increase is insufficient, the old spendbundle must stay
|
|
629
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1_1)
|
|
630
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_2)
|
|
631
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
632
|
+
|
|
633
|
+
sb1_3 = await gen_and_send_sb(full_node_1, wallet_a, coin1, fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
634
|
+
|
|
635
|
+
# Fee increase is sufficiently high, sb1_1 gets replaced with sb1_3
|
|
636
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_1)
|
|
637
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1_3)
|
|
638
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
639
|
+
|
|
640
|
+
sb2 = generate_test_spend_bundle(wallet_a, coin2, fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
641
|
+
sb12 = SpendBundle.aggregate([sb2, sb1_3])
|
|
642
|
+
await send_sb(full_node_1, sb12)
|
|
643
|
+
|
|
644
|
+
# Aggregated spendbundle sb12 replaces sb1_3 since it spends a superset
|
|
645
|
+
# of coins spent in sb1_3
|
|
646
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb12)
|
|
647
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1_3)
|
|
648
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
649
|
+
|
|
650
|
+
sb3 = generate_test_spend_bundle(wallet_a, coin3, fee=uint64(MEMPOOL_MIN_FEE_INCREASE * 2))
|
|
651
|
+
sb23 = SpendBundle.aggregate([sb2, sb3])
|
|
652
|
+
await send_sb(full_node_1, sb23)
|
|
653
|
+
|
|
654
|
+
# sb23 must not replace existing sb12 as the former does not spend all
|
|
655
|
+
# coins that are spent in the latter (specifically, coin1)
|
|
656
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb12)
|
|
657
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb23)
|
|
658
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
659
|
+
|
|
660
|
+
await send_sb(full_node_1, sb3)
|
|
661
|
+
# Adding non-conflicting sb3 should succeed
|
|
662
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb3)
|
|
663
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
664
|
+
|
|
665
|
+
sb4_1 = generate_test_spend_bundle(wallet_a, coin4, fee=MEMPOOL_MIN_FEE_INCREASE)
|
|
666
|
+
sb1234_1 = SpendBundle.aggregate([sb12, sb3, sb4_1])
|
|
667
|
+
await send_sb(full_node_1, sb1234_1)
|
|
668
|
+
# sb1234_1 should not be in pool as it decreases total fees per cost
|
|
669
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb1234_1)
|
|
670
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
671
|
+
|
|
672
|
+
sb4_2 = generate_test_spend_bundle(wallet_a, coin4, fee=uint64(MEMPOOL_MIN_FEE_INCREASE * 2))
|
|
673
|
+
sb1234_2 = SpendBundle.aggregate([sb12, sb3, sb4_2])
|
|
674
|
+
await send_sb(full_node_1, sb1234_2)
|
|
675
|
+
# sb1234_2 has a higher fee per cost than its conflicts and should get
|
|
676
|
+
# into mempool
|
|
677
|
+
assert_sb_in_pool(full_node_1.full_node.mempool_manager, sb1234_2)
|
|
678
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb12)
|
|
679
|
+
assert_sb_not_in_pool(full_node_1.full_node.mempool_manager, sb3)
|
|
680
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
681
|
+
|
|
682
|
+
@pytest.mark.anyio
|
|
683
|
+
async def test_invalid_signature(
|
|
684
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
685
|
+
) -> None:
|
|
686
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
687
|
+
|
|
688
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
689
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
690
|
+
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
|
691
|
+
blocks = bt.get_consecutive_blocks(
|
|
692
|
+
3,
|
|
693
|
+
block_list_input=blocks,
|
|
694
|
+
guarantee_transaction_block=True,
|
|
695
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
696
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
700
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
701
|
+
|
|
702
|
+
coins = iter(blocks[-1].get_included_reward_coins())
|
|
703
|
+
coin1 = next(coins)
|
|
704
|
+
|
|
705
|
+
sb: SpendBundle = generate_test_spend_bundle(wallet_a, coin1)
|
|
706
|
+
assert sb.aggregated_signature != G2Element.generator()
|
|
707
|
+
sb = sb.replace(aggregated_signature=G2Element.generator())
|
|
708
|
+
res: Optional[Message] = await send_sb(full_node_1, sb)
|
|
709
|
+
assert res is not None
|
|
710
|
+
ack: TransactionAck = TransactionAck.from_bytes(res.data)
|
|
711
|
+
assert ack.status == MempoolInclusionStatus.FAILED.value
|
|
712
|
+
assert ack.error == Err.BAD_AGGREGATE_SIGNATURE.name
|
|
713
|
+
invariant_check_mempool(full_node_1.full_node.mempool_manager.mempool)
|
|
714
|
+
|
|
715
|
+
async def condition_tester(
|
|
716
|
+
self,
|
|
717
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
718
|
+
wallet_a: WalletTool,
|
|
719
|
+
dic: dict[ConditionOpcode, list[ConditionWithArgs]],
|
|
720
|
+
fee: int = 0,
|
|
721
|
+
num_blocks: int = 3,
|
|
722
|
+
coin: Optional[Coin] = None,
|
|
723
|
+
) -> tuple[list[FullBlock], SpendBundle, WSChiaConnection, MempoolInclusionStatus, Optional[Err]]:
|
|
724
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
725
|
+
full_node_1, server_1, bt = one_node_one_block
|
|
726
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
727
|
+
start_height = blocks[-1].height
|
|
728
|
+
blocks = bt.get_consecutive_blocks(
|
|
729
|
+
num_blocks,
|
|
730
|
+
block_list_input=blocks,
|
|
731
|
+
guarantee_transaction_block=True,
|
|
732
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
733
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
734
|
+
)
|
|
735
|
+
_, dummy_node_id = await add_dummy_connection(server_1, bt.config["self_hostname"], 100)
|
|
736
|
+
for node_id, wsc in server_1.all_connections.items():
|
|
737
|
+
if node_id == dummy_node_id:
|
|
738
|
+
dummy_peer = wsc
|
|
739
|
+
break
|
|
740
|
+
else:
|
|
741
|
+
raise Exception("dummy peer not found")
|
|
742
|
+
|
|
743
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
744
|
+
|
|
745
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + num_blocks)
|
|
746
|
+
|
|
747
|
+
spend_bundle1 = generate_test_spend_bundle(
|
|
748
|
+
wallet_a, coin or blocks[-num_blocks + 2].get_included_reward_coins()[0], dic, uint64(fee)
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
assert spend_bundle1 is not None
|
|
752
|
+
|
|
753
|
+
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
|
754
|
+
|
|
755
|
+
status, err = await respond_transaction(full_node_1, tx1, dummy_peer, test=True)
|
|
756
|
+
return blocks, spend_bundle1, dummy_peer, status, err
|
|
757
|
+
|
|
758
|
+
@pytest.mark.anyio
|
|
759
|
+
async def condition_tester2(
|
|
760
|
+
self,
|
|
761
|
+
node_server_bt: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
762
|
+
wallet_a: WalletTool,
|
|
763
|
+
test_fun: Callable[[Coin, Coin], SpendBundle],
|
|
764
|
+
) -> tuple[list[FullBlock], SpendBundle, MempoolInclusionStatus, Optional[Err]]:
|
|
765
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
766
|
+
full_node_1, server_1, bt = node_server_bt
|
|
767
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
768
|
+
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
|
769
|
+
blocks = bt.get_consecutive_blocks(
|
|
770
|
+
3,
|
|
771
|
+
block_list_input=blocks,
|
|
772
|
+
guarantee_transaction_block=True,
|
|
773
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
774
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
775
|
+
time_per_block=10,
|
|
776
|
+
)
|
|
777
|
+
_, dummy_node_id = await add_dummy_connection(server_1, bt.config["self_hostname"], 100)
|
|
778
|
+
for node_id, wsc in server_1.all_connections.items():
|
|
779
|
+
if node_id == dummy_node_id:
|
|
780
|
+
dummy_peer = wsc
|
|
781
|
+
break
|
|
782
|
+
else:
|
|
783
|
+
raise Exception("dummy peer not found")
|
|
784
|
+
|
|
785
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
786
|
+
|
|
787
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
788
|
+
|
|
789
|
+
coin_1 = blocks[-2].get_included_reward_coins()[0]
|
|
790
|
+
coin_2 = blocks[-1].get_included_reward_coins()[0]
|
|
791
|
+
|
|
792
|
+
bundle = test_fun(coin_1, coin_2)
|
|
793
|
+
|
|
794
|
+
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(bundle)
|
|
795
|
+
status, err = await respond_transaction(full_node_1, tx1, dummy_peer, test=True)
|
|
796
|
+
|
|
797
|
+
return blocks, bundle, status, err
|
|
798
|
+
|
|
799
|
+
@pytest.mark.anyio
|
|
800
|
+
async def test_invalid_block_index(
|
|
801
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
802
|
+
) -> None:
|
|
803
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
804
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
805
|
+
start_height = blocks[-1].height
|
|
806
|
+
cvp = ConditionWithArgs(
|
|
807
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
808
|
+
[int_to_bytes(start_height + 5)],
|
|
809
|
+
)
|
|
810
|
+
dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
|
|
811
|
+
blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
812
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
813
|
+
assert sb1 is None
|
|
814
|
+
# the transaction may become valid later
|
|
815
|
+
assert err == Err.ASSERT_HEIGHT_ABSOLUTE_FAILED
|
|
816
|
+
assert status == MempoolInclusionStatus.PENDING
|
|
817
|
+
|
|
818
|
+
@pytest.mark.anyio
|
|
819
|
+
async def test_block_index_missing_arg(
|
|
820
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
821
|
+
) -> None:
|
|
822
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
823
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [])
|
|
824
|
+
dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
|
|
825
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
826
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
827
|
+
assert sb1 is None
|
|
828
|
+
# the transaction may become valid later
|
|
829
|
+
assert err == Err.INVALID_CONDITION
|
|
830
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
831
|
+
|
|
832
|
+
@pytest.mark.anyio
|
|
833
|
+
async def test_correct_block_index(
|
|
834
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
835
|
+
) -> None:
|
|
836
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
837
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(1)])
|
|
838
|
+
dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
|
|
839
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
840
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
841
|
+
assert err is None
|
|
842
|
+
assert sb1 == spend_bundle1
|
|
843
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
844
|
+
|
|
845
|
+
@pytest.mark.anyio
|
|
846
|
+
async def test_block_index_garbage(
|
|
847
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
848
|
+
) -> None:
|
|
849
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
850
|
+
# garbage at the end of the argument list is ignored in consensus mode,
|
|
851
|
+
# but not in mempool-mode
|
|
852
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(1), b"garbage"])
|
|
853
|
+
dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
|
|
854
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
855
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
856
|
+
assert err is Err.INVALID_CONDITION
|
|
857
|
+
assert sb1 is None
|
|
858
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
859
|
+
|
|
860
|
+
@pytest.mark.anyio
|
|
861
|
+
async def test_negative_block_index(
|
|
862
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
863
|
+
) -> None:
|
|
864
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
865
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(-1)])
|
|
866
|
+
dic = {ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE: [cvp]}
|
|
867
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
868
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
869
|
+
assert err is None
|
|
870
|
+
assert sb1 == spend_bundle1
|
|
871
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
872
|
+
|
|
873
|
+
@pytest.mark.anyio
|
|
874
|
+
async def test_invalid_block_age(
|
|
875
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
876
|
+
) -> None:
|
|
877
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
878
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(5)])
|
|
879
|
+
dic = {cvp.opcode: [cvp]}
|
|
880
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
881
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
882
|
+
assert err == Err.ASSERT_HEIGHT_RELATIVE_FAILED
|
|
883
|
+
assert sb1 is None
|
|
884
|
+
# the transaction may become valid later
|
|
885
|
+
assert status == MempoolInclusionStatus.PENDING
|
|
886
|
+
|
|
887
|
+
@pytest.mark.anyio
|
|
888
|
+
async def test_block_age_missing_arg(
|
|
889
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
890
|
+
) -> None:
|
|
891
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
892
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [])
|
|
893
|
+
dic = {cvp.opcode: [cvp]}
|
|
894
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
895
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
896
|
+
assert err == Err.INVALID_CONDITION
|
|
897
|
+
assert sb1 is None
|
|
898
|
+
# the transaction may become valid later
|
|
899
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
900
|
+
|
|
901
|
+
@pytest.mark.anyio
|
|
902
|
+
async def test_correct_block_age(
|
|
903
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
904
|
+
) -> None:
|
|
905
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
906
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(1)])
|
|
907
|
+
dic = {cvp.opcode: [cvp]}
|
|
908
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
909
|
+
one_node_one_block, wallet_a, dic, num_blocks=4
|
|
910
|
+
)
|
|
911
|
+
|
|
912
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
913
|
+
assert err is None
|
|
914
|
+
assert sb1 == spend_bundle1
|
|
915
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
916
|
+
|
|
917
|
+
@pytest.mark.anyio
|
|
918
|
+
async def test_block_age_garbage(
|
|
919
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
920
|
+
) -> None:
|
|
921
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
922
|
+
# garbage at the end of the argument list is ignored in consensus mode,
|
|
923
|
+
# but not in mempool mode
|
|
924
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(1), b"garbage"])
|
|
925
|
+
dic = {cvp.opcode: [cvp]}
|
|
926
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
927
|
+
one_node_one_block, wallet_a, dic, num_blocks=4
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
931
|
+
assert err is Err.INVALID_CONDITION
|
|
932
|
+
assert sb1 is None
|
|
933
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
934
|
+
|
|
935
|
+
@pytest.mark.anyio
|
|
936
|
+
async def test_negative_block_age(
|
|
937
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
938
|
+
) -> None:
|
|
939
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
940
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_RELATIVE, [int_to_bytes(-1)])
|
|
941
|
+
dic = {cvp.opcode: [cvp]}
|
|
942
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
943
|
+
one_node_one_block, wallet_a, dic, num_blocks=4
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
947
|
+
assert err is None
|
|
948
|
+
assert sb1 == spend_bundle1
|
|
949
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
950
|
+
|
|
951
|
+
@pytest.mark.anyio
|
|
952
|
+
async def test_correct_my_id(
|
|
953
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
954
|
+
) -> None:
|
|
955
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
956
|
+
|
|
957
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
958
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
959
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
960
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin.name()])
|
|
961
|
+
dic = {cvp.opcode: [cvp]}
|
|
962
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
963
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
967
|
+
assert err is None
|
|
968
|
+
assert sb1 == spend_bundle1
|
|
969
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
970
|
+
|
|
971
|
+
@pytest.mark.anyio
|
|
972
|
+
async def test_my_id_garbage(
|
|
973
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
974
|
+
) -> None:
|
|
975
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
976
|
+
|
|
977
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
978
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
979
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
980
|
+
# garbage at the end of the argument list is ignored in consensus mode,
|
|
981
|
+
# but not in mempool mode
|
|
982
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin.name(), b"garbage"])
|
|
983
|
+
dic = {cvp.opcode: [cvp]}
|
|
984
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
985
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
989
|
+
assert err is Err.INVALID_CONDITION
|
|
990
|
+
assert sb1 is None
|
|
991
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
992
|
+
|
|
993
|
+
@pytest.mark.anyio
|
|
994
|
+
async def test_invalid_my_id(
|
|
995
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
996
|
+
) -> None:
|
|
997
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
998
|
+
|
|
999
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1000
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1001
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1002
|
+
coin_2 = await next_block(full_node_1, wallet_a, bt)
|
|
1003
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [coin_2.name()])
|
|
1004
|
+
dic = {cvp.opcode: [cvp]}
|
|
1005
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1006
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1010
|
+
assert err == Err.ASSERT_MY_COIN_ID_FAILED
|
|
1011
|
+
assert sb1 is None
|
|
1012
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1013
|
+
|
|
1014
|
+
@pytest.mark.anyio
|
|
1015
|
+
async def test_my_id_missing_arg(
|
|
1016
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1017
|
+
) -> None:
|
|
1018
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1019
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_COIN_ID, [])
|
|
1020
|
+
dic = {cvp.opcode: [cvp]}
|
|
1021
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1022
|
+
|
|
1023
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1024
|
+
assert err == Err.INVALID_CONDITION
|
|
1025
|
+
assert sb1 is None
|
|
1026
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1027
|
+
|
|
1028
|
+
@pytest.mark.anyio
|
|
1029
|
+
async def test_assert_time_exceeds(
|
|
1030
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1031
|
+
) -> None:
|
|
1032
|
+
full_node_1, _, _ = one_node_one_block
|
|
1033
|
+
blockchain_peak = full_node_1.full_node.blockchain.get_peak()
|
|
1034
|
+
assert blockchain_peak is not None
|
|
1035
|
+
assert blockchain_peak.timestamp is not None
|
|
1036
|
+
# 5 seconds should be before the next block
|
|
1037
|
+
time_now = blockchain_peak.timestamp + 5
|
|
1038
|
+
|
|
1039
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
|
|
1040
|
+
dic = {cvp.opcode: [cvp]}
|
|
1041
|
+
_, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1042
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1043
|
+
assert err is None
|
|
1044
|
+
assert sb1 == spend_bundle1
|
|
1045
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1046
|
+
|
|
1047
|
+
@pytest.mark.anyio
|
|
1048
|
+
async def test_assert_time_fail(
|
|
1049
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1050
|
+
) -> None:
|
|
1051
|
+
full_node_1, _, _ = one_node_one_block
|
|
1052
|
+
blockchain_peak = full_node_1.full_node.blockchain.get_peak()
|
|
1053
|
+
assert blockchain_peak is not None
|
|
1054
|
+
assert blockchain_peak.timestamp is not None
|
|
1055
|
+
time_now = blockchain_peak.timestamp + 1000
|
|
1056
|
+
|
|
1057
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
|
|
1058
|
+
dic = {cvp.opcode: [cvp]}
|
|
1059
|
+
_, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1060
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1061
|
+
assert err == Err.ASSERT_SECONDS_ABSOLUTE_FAILED
|
|
1062
|
+
assert sb1 is None
|
|
1063
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1064
|
+
|
|
1065
|
+
@pytest.mark.anyio
|
|
1066
|
+
async def test_assert_height_pending(
|
|
1067
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1068
|
+
) -> None:
|
|
1069
|
+
full_node_1, _, _ = one_node_one_block
|
|
1070
|
+
blockchain_peak = full_node_1.full_node.blockchain.get_peak()
|
|
1071
|
+
assert blockchain_peak is not None
|
|
1072
|
+
current_height = blockchain_peak.height
|
|
1073
|
+
|
|
1074
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, [int_to_bytes(current_height + 4)])
|
|
1075
|
+
dic = {cvp.opcode: [cvp]}
|
|
1076
|
+
_, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1077
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1078
|
+
assert err == Err.ASSERT_HEIGHT_ABSOLUTE_FAILED
|
|
1079
|
+
assert sb1 is None
|
|
1080
|
+
assert status == MempoolInclusionStatus.PENDING
|
|
1081
|
+
|
|
1082
|
+
@pytest.mark.anyio
|
|
1083
|
+
async def test_assert_time_negative(
|
|
1084
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1085
|
+
) -> None:
|
|
1086
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1087
|
+
time_now = -1
|
|
1088
|
+
|
|
1089
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now)])
|
|
1090
|
+
dic = {cvp.opcode: [cvp]}
|
|
1091
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1092
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1093
|
+
assert err is None
|
|
1094
|
+
assert sb1 == spend_bundle1
|
|
1095
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1096
|
+
|
|
1097
|
+
@pytest.mark.anyio
|
|
1098
|
+
async def test_assert_time_missing_arg(
|
|
1099
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1100
|
+
) -> None:
|
|
1101
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1102
|
+
|
|
1103
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [])
|
|
1104
|
+
dic = {cvp.opcode: [cvp]}
|
|
1105
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1106
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1107
|
+
assert err == Err.INVALID_CONDITION
|
|
1108
|
+
assert sb1 is None
|
|
1109
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1110
|
+
|
|
1111
|
+
@pytest.mark.anyio
|
|
1112
|
+
async def test_assert_time_garbage(
|
|
1113
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1114
|
+
) -> None:
|
|
1115
|
+
full_node_1, _, _ = one_node_one_block
|
|
1116
|
+
blockchain_peak = full_node_1.full_node.blockchain.get_peak()
|
|
1117
|
+
assert blockchain_peak is not None
|
|
1118
|
+
assert blockchain_peak.timestamp is not None
|
|
1119
|
+
time_now = blockchain_peak.timestamp + 5
|
|
1120
|
+
|
|
1121
|
+
# garbage at the end of the argument list is ignored in consensus mode,
|
|
1122
|
+
# but not in mempool mode
|
|
1123
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [int_to_bytes(time_now), b"garbage"])
|
|
1124
|
+
dic = {cvp.opcode: [cvp]}
|
|
1125
|
+
_, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1126
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1127
|
+
assert err is Err.INVALID_CONDITION
|
|
1128
|
+
assert sb1 is None
|
|
1129
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1130
|
+
|
|
1131
|
+
@pytest.mark.anyio
|
|
1132
|
+
async def test_assert_time_relative_exceeds(
|
|
1133
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1134
|
+
) -> None:
|
|
1135
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1136
|
+
time_relative = 3
|
|
1137
|
+
|
|
1138
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative)])
|
|
1139
|
+
dic = {cvp.opcode: [cvp]}
|
|
1140
|
+
_blocks, spend_bundle1, peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1141
|
+
|
|
1142
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1143
|
+
assert err == Err.ASSERT_SECONDS_RELATIVE_FAILED
|
|
1144
|
+
assert sb1 is None
|
|
1145
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1146
|
+
|
|
1147
|
+
for i in range(0, 4):
|
|
1148
|
+
await full_node_1.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
1149
|
+
|
|
1150
|
+
tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
|
1151
|
+
|
|
1152
|
+
status, err = await respond_transaction(full_node_1, tx2, peer, test=True)
|
|
1153
|
+
|
|
1154
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1155
|
+
assert err is None
|
|
1156
|
+
assert sb1 == spend_bundle1
|
|
1157
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1158
|
+
|
|
1159
|
+
@pytest.mark.anyio
|
|
1160
|
+
async def test_assert_time_relative_garbage(
|
|
1161
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1162
|
+
) -> None:
|
|
1163
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1164
|
+
time_relative = 0
|
|
1165
|
+
|
|
1166
|
+
# garbage at the end of the arguments is ignored in consensus mode, but
|
|
1167
|
+
# not in mempool mode
|
|
1168
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative), b"garbage"])
|
|
1169
|
+
dic = {cvp.opcode: [cvp]}
|
|
1170
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1171
|
+
|
|
1172
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1173
|
+
assert err is Err.INVALID_CONDITION
|
|
1174
|
+
assert sb1 is None
|
|
1175
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1176
|
+
|
|
1177
|
+
@pytest.mark.anyio
|
|
1178
|
+
async def test_assert_time_relative_missing_arg(
|
|
1179
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1180
|
+
) -> None:
|
|
1181
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1182
|
+
|
|
1183
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [])
|
|
1184
|
+
dic = {cvp.opcode: [cvp]}
|
|
1185
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1186
|
+
|
|
1187
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1188
|
+
assert err == Err.INVALID_CONDITION
|
|
1189
|
+
assert sb1 is None
|
|
1190
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1191
|
+
|
|
1192
|
+
@pytest.mark.anyio
|
|
1193
|
+
async def test_assert_time_relative_negative(
|
|
1194
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1195
|
+
) -> None:
|
|
1196
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1197
|
+
time_relative = -3
|
|
1198
|
+
|
|
1199
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [int_to_bytes(time_relative)])
|
|
1200
|
+
dic = {cvp.opcode: [cvp]}
|
|
1201
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1202
|
+
|
|
1203
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1204
|
+
assert err is None
|
|
1205
|
+
assert sb1 == spend_bundle1
|
|
1206
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1207
|
+
|
|
1208
|
+
# ensure one spend can assert a coin announcement from another spend
|
|
1209
|
+
@pytest.mark.anyio
|
|
1210
|
+
async def test_correct_coin_announcement_consumed(
|
|
1211
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1212
|
+
) -> None:
|
|
1213
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1214
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
1215
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
1216
|
+
dic = {cvp.opcode: [cvp]}
|
|
1217
|
+
|
|
1218
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
1219
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1220
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1221
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1222
|
+
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1223
|
+
return bundle
|
|
1224
|
+
|
|
1225
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1226
|
+
_blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1227
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1228
|
+
|
|
1229
|
+
assert err is None
|
|
1230
|
+
assert mempool_bundle == bundle
|
|
1231
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1232
|
+
|
|
1233
|
+
# ensure one spend can assert a coin announcement from another spend, even
|
|
1234
|
+
# though the conditions have garbage at the end
|
|
1235
|
+
@pytest.mark.anyio
|
|
1236
|
+
@pytest.mark.parametrize(
|
|
1237
|
+
"assert_garbage,announce_garbage,expected,expected_included",
|
|
1238
|
+
[
|
|
1239
|
+
(True, False, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
|
|
1240
|
+
(False, True, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
|
|
1241
|
+
(False, False, None, MempoolInclusionStatus.SUCCESS),
|
|
1242
|
+
],
|
|
1243
|
+
)
|
|
1244
|
+
async def test_coin_announcement_garbage(
|
|
1245
|
+
self,
|
|
1246
|
+
assert_garbage: bool,
|
|
1247
|
+
announce_garbage: bool,
|
|
1248
|
+
expected: Optional[Err],
|
|
1249
|
+
expected_included: MempoolInclusionStatus,
|
|
1250
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1251
|
+
wallet_a: WalletTool,
|
|
1252
|
+
) -> None:
|
|
1253
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1254
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
1255
|
+
# garbage at the end is ignored in consensus mode, but not in
|
|
1256
|
+
# mempool mode
|
|
1257
|
+
cvp = ConditionWithArgs(
|
|
1258
|
+
ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
|
|
1259
|
+
[bytes(announce.msg_calc)] + ([b"garbage"] if announce_garbage else []),
|
|
1260
|
+
)
|
|
1261
|
+
dic = {cvp.opcode: [cvp]}
|
|
1262
|
+
|
|
1263
|
+
# garbage at the end is ignored in consensus mode, but not in
|
|
1264
|
+
# mempool mode
|
|
1265
|
+
cvp2 = ConditionWithArgs(
|
|
1266
|
+
ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"] + ([b"garbage"] if assert_garbage else [])
|
|
1267
|
+
)
|
|
1268
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1269
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1270
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1271
|
+
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1272
|
+
return bundle
|
|
1273
|
+
|
|
1274
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1275
|
+
_blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1276
|
+
|
|
1277
|
+
assert err is expected
|
|
1278
|
+
assert status == expected_included
|
|
1279
|
+
if status == MempoolInclusionStatus.SUCCESS:
|
|
1280
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1281
|
+
assert mempool_bundle == bundle
|
|
1282
|
+
|
|
1283
|
+
@pytest.mark.anyio
|
|
1284
|
+
async def test_coin_announcement_missing_arg(
|
|
1285
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1286
|
+
) -> None:
|
|
1287
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1288
|
+
# missing arg here
|
|
1289
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [])
|
|
1290
|
+
dic = {cvp.opcode: [cvp]}
|
|
1291
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
1292
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1293
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1294
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1295
|
+
|
|
1296
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1297
|
+
|
|
1298
|
+
full_node_1, _, _ = one_node_one_block
|
|
1299
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1300
|
+
|
|
1301
|
+
assert err == Err.INVALID_CONDITION
|
|
1302
|
+
assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
|
|
1303
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1304
|
+
|
|
1305
|
+
@pytest.mark.anyio
|
|
1306
|
+
async def test_coin_announcement_missing_arg2(
|
|
1307
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1308
|
+
) -> None:
|
|
1309
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1310
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
1311
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
1312
|
+
dic = {cvp.opcode: [cvp]}
|
|
1313
|
+
# missing arg here
|
|
1314
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [])
|
|
1315
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1316
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1317
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1318
|
+
|
|
1319
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1320
|
+
|
|
1321
|
+
full_node_1, _, _ = one_node_one_block
|
|
1322
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1323
|
+
|
|
1324
|
+
assert err == Err.INVALID_CONDITION
|
|
1325
|
+
assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
|
|
1326
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1327
|
+
|
|
1328
|
+
@pytest.mark.anyio
|
|
1329
|
+
async def test_coin_announcement_too_big(
|
|
1330
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1331
|
+
) -> None:
|
|
1332
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1333
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=bytes([1] * 10000))
|
|
1334
|
+
|
|
1335
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
1336
|
+
|
|
1337
|
+
dic = {cvp.opcode: [cvp]}
|
|
1338
|
+
|
|
1339
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
1340
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1341
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1342
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1343
|
+
|
|
1344
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1345
|
+
|
|
1346
|
+
full_node_1, _, bt = one_node_one_block
|
|
1347
|
+
blocks, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1348
|
+
|
|
1349
|
+
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1350
|
+
assert full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name()) is None
|
|
1351
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1352
|
+
|
|
1353
|
+
blocks = bt.get_consecutive_blocks(
|
|
1354
|
+
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=bundle
|
|
1355
|
+
)
|
|
1356
|
+
try:
|
|
1357
|
+
await _validate_and_add_block(full_node_1.full_node.blockchain, blocks[-1])
|
|
1358
|
+
assert False
|
|
1359
|
+
except AssertionError as e:
|
|
1360
|
+
assert e.args[0] == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1361
|
+
|
|
1362
|
+
# ensure an assert coin announcement is rejected if it doesn't match the
|
|
1363
|
+
# create announcement
|
|
1364
|
+
@pytest.mark.anyio
|
|
1365
|
+
async def test_invalid_coin_announcement_rejected(
|
|
1366
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1367
|
+
) -> None:
|
|
1368
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1369
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_2.name(), asserted_msg=b"test")
|
|
1370
|
+
|
|
1371
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
1372
|
+
|
|
1373
|
+
dic = {cvp.opcode: [cvp]}
|
|
1374
|
+
# mismatching message
|
|
1375
|
+
cvp2 = ConditionWithArgs(
|
|
1376
|
+
ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
|
|
1377
|
+
[b"wrong test"],
|
|
1378
|
+
)
|
|
1379
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1380
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1381
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1382
|
+
|
|
1383
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1384
|
+
|
|
1385
|
+
full_node_1, _, _ = one_node_one_block
|
|
1386
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1387
|
+
|
|
1388
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1389
|
+
|
|
1390
|
+
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1391
|
+
assert mempool_bundle is None
|
|
1392
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1393
|
+
|
|
1394
|
+
@pytest.mark.anyio
|
|
1395
|
+
async def test_invalid_coin_announcement_rejected_two(
|
|
1396
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1397
|
+
) -> None:
|
|
1398
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1399
|
+
announce = AssertCoinAnnouncement(asserted_id=coin_1.name(), asserted_msg=b"test")
|
|
1400
|
+
|
|
1401
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.msg_calc])
|
|
1402
|
+
|
|
1403
|
+
dic = {cvp.opcode: [cvp]}
|
|
1404
|
+
|
|
1405
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
|
|
1406
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1407
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1408
|
+
# coin 2 is making the announcement, right message wrong coin
|
|
1409
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1410
|
+
|
|
1411
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1412
|
+
|
|
1413
|
+
full_node_1, _, _ = one_node_one_block
|
|
1414
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1415
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1416
|
+
|
|
1417
|
+
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1418
|
+
assert mempool_bundle is None
|
|
1419
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1420
|
+
|
|
1421
|
+
@pytest.mark.anyio
|
|
1422
|
+
async def test_correct_puzzle_announcement(
|
|
1423
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1424
|
+
) -> None:
|
|
1425
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1426
|
+
announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
|
|
1427
|
+
|
|
1428
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
|
|
1429
|
+
|
|
1430
|
+
dic = {cvp.opcode: [cvp]}
|
|
1431
|
+
|
|
1432
|
+
cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, [bytes(0x80)])
|
|
1433
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1434
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1435
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1436
|
+
|
|
1437
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1438
|
+
|
|
1439
|
+
full_node_1, _, _ = one_node_one_block
|
|
1440
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1441
|
+
|
|
1442
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1443
|
+
|
|
1444
|
+
assert err is None
|
|
1445
|
+
assert mempool_bundle == bundle
|
|
1446
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1447
|
+
|
|
1448
|
+
@pytest.mark.anyio
|
|
1449
|
+
@pytest.mark.parametrize(
|
|
1450
|
+
"assert_garbage,announce_garbage,expected,expected_included",
|
|
1451
|
+
[
|
|
1452
|
+
(True, False, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
|
|
1453
|
+
(False, True, Err.INVALID_CONDITION, MempoolInclusionStatus.FAILED),
|
|
1454
|
+
(False, False, None, MempoolInclusionStatus.SUCCESS),
|
|
1455
|
+
],
|
|
1456
|
+
)
|
|
1457
|
+
async def test_puzzle_announcement_garbage(
|
|
1458
|
+
self,
|
|
1459
|
+
assert_garbage: bool,
|
|
1460
|
+
announce_garbage: bool,
|
|
1461
|
+
expected: Optional[Err],
|
|
1462
|
+
expected_included: MempoolInclusionStatus,
|
|
1463
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1464
|
+
wallet_a: WalletTool,
|
|
1465
|
+
) -> None:
|
|
1466
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1467
|
+
announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
|
|
1468
|
+
|
|
1469
|
+
# garbage at the end is ignored in consensus mode, but not in
|
|
1470
|
+
# mempool mode
|
|
1471
|
+
cvp = ConditionWithArgs(
|
|
1472
|
+
ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT,
|
|
1473
|
+
[bytes(announce.msg_calc)] + ([b"garbage"] if assert_garbage else []),
|
|
1474
|
+
)
|
|
1475
|
+
dic = {cvp.opcode: [cvp]}
|
|
1476
|
+
# garbage at the end is ignored in consensus mode, but not in
|
|
1477
|
+
# mempool mode
|
|
1478
|
+
cvp2 = ConditionWithArgs(
|
|
1479
|
+
ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, [bytes(0x80)] + ([b"garbage"] if announce_garbage else [])
|
|
1480
|
+
)
|
|
1481
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1482
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1483
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1484
|
+
|
|
1485
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1486
|
+
|
|
1487
|
+
full_node_1, _, _ = one_node_one_block
|
|
1488
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1489
|
+
|
|
1490
|
+
assert err is expected
|
|
1491
|
+
assert status == expected_included
|
|
1492
|
+
if status == MempoolInclusionStatus.SUCCESS:
|
|
1493
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1494
|
+
assert mempool_bundle == bundle
|
|
1495
|
+
|
|
1496
|
+
@pytest.mark.anyio
|
|
1497
|
+
async def test_puzzle_announcement_missing_arg(
|
|
1498
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1499
|
+
) -> None:
|
|
1500
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1501
|
+
# missing arg here
|
|
1502
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [])
|
|
1503
|
+
dic = {cvp.opcode: [cvp]}
|
|
1504
|
+
cvp2 = ConditionWithArgs(
|
|
1505
|
+
ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
|
|
1506
|
+
[b"test"],
|
|
1507
|
+
)
|
|
1508
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1509
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1510
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1511
|
+
|
|
1512
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1513
|
+
|
|
1514
|
+
full_node_1, _, _ = one_node_one_block
|
|
1515
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1516
|
+
|
|
1517
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1518
|
+
|
|
1519
|
+
assert err == Err.INVALID_CONDITION
|
|
1520
|
+
assert mempool_bundle is None
|
|
1521
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1522
|
+
|
|
1523
|
+
@pytest.mark.anyio
|
|
1524
|
+
async def test_puzzle_announcement_missing_arg2(
|
|
1525
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1526
|
+
) -> None:
|
|
1527
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1528
|
+
announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=b"test")
|
|
1529
|
+
|
|
1530
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
|
|
1531
|
+
dic = {cvp.opcode: [cvp]}
|
|
1532
|
+
# missing arg here
|
|
1533
|
+
cvp2 = ConditionWithArgs(
|
|
1534
|
+
ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
|
|
1535
|
+
[],
|
|
1536
|
+
)
|
|
1537
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1538
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1539
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1540
|
+
|
|
1541
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1542
|
+
|
|
1543
|
+
full_node_1, _, _ = one_node_one_block
|
|
1544
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1545
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1546
|
+
|
|
1547
|
+
assert err == Err.INVALID_CONDITION
|
|
1548
|
+
assert mempool_bundle is None
|
|
1549
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1550
|
+
|
|
1551
|
+
@pytest.mark.anyio
|
|
1552
|
+
async def test_invalid_puzzle_announcement_rejected(
|
|
1553
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1554
|
+
) -> None:
|
|
1555
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1556
|
+
announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes("test", "utf-8"))
|
|
1557
|
+
|
|
1558
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
|
|
1559
|
+
|
|
1560
|
+
dic = {cvp.opcode: [cvp]}
|
|
1561
|
+
|
|
1562
|
+
cvp2 = ConditionWithArgs(
|
|
1563
|
+
ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
|
|
1564
|
+
[b"wrong test"],
|
|
1565
|
+
)
|
|
1566
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1567
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1568
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1569
|
+
|
|
1570
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1571
|
+
|
|
1572
|
+
full_node_1, _, _ = one_node_one_block
|
|
1573
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1574
|
+
|
|
1575
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1576
|
+
|
|
1577
|
+
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1578
|
+
assert mempool_bundle is None
|
|
1579
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1580
|
+
|
|
1581
|
+
@pytest.mark.anyio
|
|
1582
|
+
async def test_invalid_puzzle_announcement_rejected_two(
|
|
1583
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1584
|
+
) -> None:
|
|
1585
|
+
def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
|
|
1586
|
+
announce = AssertPuzzleAnnouncement(asserted_ph=coin_2.puzzle_hash, asserted_msg=bytes(0x80))
|
|
1587
|
+
|
|
1588
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.msg_calc])
|
|
1589
|
+
|
|
1590
|
+
dic = {cvp.opcode: [cvp]}
|
|
1591
|
+
# Wrong type of Create_announcement
|
|
1592
|
+
cvp2 = ConditionWithArgs(
|
|
1593
|
+
ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
|
|
1594
|
+
[b"test"],
|
|
1595
|
+
)
|
|
1596
|
+
dic2 = {cvp.opcode: [cvp2]}
|
|
1597
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic)
|
|
1598
|
+
spend_bundle2 = generate_test_spend_bundle(wallet_a, coin_2, dic2)
|
|
1599
|
+
|
|
1600
|
+
return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1601
|
+
|
|
1602
|
+
full_node_1, _, _ = one_node_one_block
|
|
1603
|
+
_, bundle, status, err = await self.condition_tester2(one_node_one_block, wallet_a, test_fun)
|
|
1604
|
+
|
|
1605
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
|
1606
|
+
|
|
1607
|
+
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
|
1608
|
+
assert mempool_bundle is None
|
|
1609
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1610
|
+
|
|
1611
|
+
@pytest.mark.anyio
|
|
1612
|
+
async def test_assert_fee_condition(
|
|
1613
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1614
|
+
) -> None:
|
|
1615
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1616
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
|
|
1617
|
+
dic = {cvp.opcode: [cvp]}
|
|
1618
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1619
|
+
one_node_one_block, wallet_a, dic, fee=10
|
|
1620
|
+
)
|
|
1621
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1622
|
+
|
|
1623
|
+
assert err is None
|
|
1624
|
+
assert mempool_bundle is not None
|
|
1625
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1626
|
+
|
|
1627
|
+
@pytest.mark.anyio
|
|
1628
|
+
async def test_assert_fee_condition_garbage(
|
|
1629
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1630
|
+
) -> None:
|
|
1631
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1632
|
+
# garbage at the end of the arguments is ignored in consensus mode, but
|
|
1633
|
+
# not in mempool mode
|
|
1634
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10), b"garbage"])
|
|
1635
|
+
dic = {cvp.opcode: [cvp]}
|
|
1636
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1637
|
+
one_node_one_block, wallet_a, dic, fee=10
|
|
1638
|
+
)
|
|
1639
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1640
|
+
|
|
1641
|
+
assert err is Err.INVALID_CONDITION
|
|
1642
|
+
assert mempool_bundle is None
|
|
1643
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1644
|
+
|
|
1645
|
+
@pytest.mark.anyio
|
|
1646
|
+
async def test_assert_fee_condition_missing_arg(
|
|
1647
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1648
|
+
) -> None:
|
|
1649
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1650
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [])
|
|
1651
|
+
dic = {cvp.opcode: [cvp]}
|
|
1652
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1653
|
+
one_node_one_block, wallet_a, dic, fee=10
|
|
1654
|
+
)
|
|
1655
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1656
|
+
|
|
1657
|
+
assert err == Err.INVALID_CONDITION
|
|
1658
|
+
assert mempool_bundle is None
|
|
1659
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1660
|
+
|
|
1661
|
+
@pytest.mark.anyio
|
|
1662
|
+
async def test_assert_fee_condition_negative_fee(
|
|
1663
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1664
|
+
) -> None:
|
|
1665
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1666
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(-1)])
|
|
1667
|
+
dic = {cvp.opcode: [cvp]}
|
|
1668
|
+
blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1669
|
+
one_node_one_block, wallet_a, dic, fee=10
|
|
1670
|
+
)
|
|
1671
|
+
assert err == Err.RESERVE_FEE_CONDITION_FAILED
|
|
1672
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1673
|
+
blocks = bt.get_consecutive_blocks(
|
|
1674
|
+
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=spend_bundle1
|
|
1675
|
+
)
|
|
1676
|
+
assert full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) is None
|
|
1677
|
+
await _validate_and_add_block(
|
|
1678
|
+
full_node_1.full_node.blockchain, blocks[-1], expected_error=Err.RESERVE_FEE_CONDITION_FAILED
|
|
1679
|
+
)
|
|
1680
|
+
|
|
1681
|
+
@pytest.mark.anyio
|
|
1682
|
+
async def test_assert_fee_condition_fee_too_large(
|
|
1683
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1684
|
+
) -> None:
|
|
1685
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1686
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(2**64)])
|
|
1687
|
+
dic = {cvp.opcode: [cvp]}
|
|
1688
|
+
blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1689
|
+
one_node_one_block, wallet_a, dic, fee=10
|
|
1690
|
+
)
|
|
1691
|
+
assert err == Err.RESERVE_FEE_CONDITION_FAILED
|
|
1692
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1693
|
+
blocks = bt.get_consecutive_blocks(
|
|
1694
|
+
1, block_list_input=blocks, guarantee_transaction_block=True, transaction_data=spend_bundle1
|
|
1695
|
+
)
|
|
1696
|
+
assert full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name()) is None
|
|
1697
|
+
await _validate_and_add_block(
|
|
1698
|
+
full_node_1.full_node.blockchain, blocks[-1], expected_error=Err.RESERVE_FEE_CONDITION_FAILED
|
|
1699
|
+
)
|
|
1700
|
+
|
|
1701
|
+
@pytest.mark.anyio
|
|
1702
|
+
async def test_assert_fee_condition_wrong_fee(
|
|
1703
|
+
self, one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools], wallet_a: WalletTool
|
|
1704
|
+
) -> None:
|
|
1705
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1706
|
+
|
|
1707
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
|
|
1708
|
+
dic = {cvp.opcode: [cvp]}
|
|
1709
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1710
|
+
one_node_one_block, wallet_a, dic, fee=9
|
|
1711
|
+
)
|
|
1712
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1713
|
+
|
|
1714
|
+
assert err == Err.RESERVE_FEE_CONDITION_FAILED
|
|
1715
|
+
assert mempool_bundle is None
|
|
1716
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1717
|
+
|
|
1718
|
+
@pytest.mark.anyio
|
|
1719
|
+
async def test_stealing_fee(
|
|
1720
|
+
self,
|
|
1721
|
+
two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
|
|
1722
|
+
wallet_a: WalletTool,
|
|
1723
|
+
) -> None:
|
|
1724
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
1725
|
+
full_node_1, _, server_1, server_2, bt = two_nodes_one_block
|
|
1726
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
1727
|
+
start_height = blocks[-1].height
|
|
1728
|
+
blocks = bt.get_consecutive_blocks(
|
|
1729
|
+
5,
|
|
1730
|
+
block_list_input=blocks,
|
|
1731
|
+
guarantee_transaction_block=True,
|
|
1732
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
1733
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
1734
|
+
)
|
|
1735
|
+
|
|
1736
|
+
peer = await connect_and_get_peer(server_1, server_2, bt.config["self_hostname"])
|
|
1737
|
+
|
|
1738
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
1739
|
+
|
|
1740
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 5)
|
|
1741
|
+
|
|
1742
|
+
receiver_puzzlehash = BURN_PUZZLE_HASH
|
|
1743
|
+
|
|
1744
|
+
cvp = ConditionWithArgs(ConditionOpcode.RESERVE_FEE, [int_to_bytes(10)])
|
|
1745
|
+
dic = {cvp.opcode: [cvp]}
|
|
1746
|
+
|
|
1747
|
+
fee = 9
|
|
1748
|
+
|
|
1749
|
+
coin_1 = blocks[-2].get_included_reward_coins()[0]
|
|
1750
|
+
coin_2 = None
|
|
1751
|
+
for coin in blocks[-1].get_included_reward_coins():
|
|
1752
|
+
if coin.amount == coin_1.amount:
|
|
1753
|
+
coin_2 = coin
|
|
1754
|
+
assert coin_2 is not None
|
|
1755
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin_1, dic, uint64(fee))
|
|
1756
|
+
|
|
1757
|
+
steal_fee_spendbundle = wallet_a.generate_signed_transaction(
|
|
1758
|
+
uint64(coin_1.amount + fee - 4), receiver_puzzlehash, coin_2
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1761
|
+
assert spend_bundle1 is not None
|
|
1762
|
+
assert steal_fee_spendbundle is not None
|
|
1763
|
+
|
|
1764
|
+
combined = SpendBundle.aggregate([spend_bundle1, steal_fee_spendbundle])
|
|
1765
|
+
|
|
1766
|
+
assert estimate_fees(combined) == 4
|
|
1767
|
+
|
|
1768
|
+
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
|
1769
|
+
|
|
1770
|
+
status, err = await respond_transaction(full_node_1, tx1, peer, test=True)
|
|
1771
|
+
|
|
1772
|
+
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1773
|
+
|
|
1774
|
+
assert err == Err.RESERVE_FEE_CONDITION_FAILED
|
|
1775
|
+
assert mempool_bundle is None
|
|
1776
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1777
|
+
|
|
1778
|
+
@pytest.mark.anyio
|
|
1779
|
+
async def test_double_spend_same_bundle(
|
|
1780
|
+
self,
|
|
1781
|
+
two_nodes_one_block: tuple[FullNodeSimulator, FullNodeSimulator, ChiaServer, ChiaServer, BlockTools],
|
|
1782
|
+
wallet_a: WalletTool,
|
|
1783
|
+
) -> None:
|
|
1784
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
1785
|
+
full_node_1, _, server_1, server_2, bt = two_nodes_one_block
|
|
1786
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
1787
|
+
start_height = blocks[-1].height
|
|
1788
|
+
blocks = bt.get_consecutive_blocks(
|
|
1789
|
+
3,
|
|
1790
|
+
block_list_input=blocks,
|
|
1791
|
+
guarantee_transaction_block=True,
|
|
1792
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
1793
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
1794
|
+
)
|
|
1795
|
+
|
|
1796
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
1797
|
+
|
|
1798
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
1799
|
+
# coin = blocks[-1].get_included_reward_coins()[0]
|
|
1800
|
+
# spend_bundle1 = generate_test_spend_bundle(wallet_a, coin)
|
|
1801
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1802
|
+
spend_bundle1 = generate_test_spend_bundle(wallet_a, coin)
|
|
1803
|
+
|
|
1804
|
+
assert spend_bundle1 is not None
|
|
1805
|
+
|
|
1806
|
+
spend_bundle2 = generate_test_spend_bundle(
|
|
1807
|
+
wallet_a,
|
|
1808
|
+
coin,
|
|
1809
|
+
new_puzzle_hash=BURN_PUZZLE_HASH_2,
|
|
1810
|
+
)
|
|
1811
|
+
|
|
1812
|
+
assert spend_bundle2 is not None
|
|
1813
|
+
|
|
1814
|
+
spend_bundle_combined = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
|
1815
|
+
|
|
1816
|
+
tx = full_node_protocol.RespondTransaction(spend_bundle_combined)
|
|
1817
|
+
|
|
1818
|
+
peer = await connect_and_get_peer(server_1, server_2, bt.config["self_hostname"])
|
|
1819
|
+
status, err = await respond_transaction(full_node_1, tx, peer, test=True)
|
|
1820
|
+
|
|
1821
|
+
sb = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle_combined.name())
|
|
1822
|
+
assert err == Err.DOUBLE_SPEND
|
|
1823
|
+
assert sb is None
|
|
1824
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1825
|
+
|
|
1826
|
+
@pytest.mark.anyio
|
|
1827
|
+
async def test_agg_sig_condition(
|
|
1828
|
+
self,
|
|
1829
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1830
|
+
wallet_a: WalletTool,
|
|
1831
|
+
) -> None:
|
|
1832
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
1833
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1834
|
+
blocks = await full_node_1.get_all_full_blocks()
|
|
1835
|
+
start_height = blocks[-1].height
|
|
1836
|
+
blocks = bt.get_consecutive_blocks(
|
|
1837
|
+
3,
|
|
1838
|
+
block_list_input=blocks,
|
|
1839
|
+
guarantee_transaction_block=True,
|
|
1840
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
1841
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
1842
|
+
)
|
|
1843
|
+
|
|
1844
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
1845
|
+
|
|
1846
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
|
1847
|
+
|
|
1848
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1849
|
+
# coin = blocks[-1].get_included_reward_coins()[0]
|
|
1850
|
+
spend_bundle_0 = generate_test_spend_bundle(wallet_a, coin)
|
|
1851
|
+
unsigned: list[CoinSpend] = spend_bundle_0.coin_spends
|
|
1852
|
+
|
|
1853
|
+
assert len(unsigned) == 1
|
|
1854
|
+
# coin_spend: CoinSpend = unsigned[0]
|
|
1855
|
+
|
|
1856
|
+
# TODO(straya): fix this test
|
|
1857
|
+
# puzzle, solution = list(coin_spend.solution.as_iter())
|
|
1858
|
+
# conditions_dict = conditions_dict_for_solution(coin_spend.puzzle_reveal, coin_spend.solution, INFINITE_COST)
|
|
1859
|
+
|
|
1860
|
+
# pkm_pairs = pkm_pairs_for_conditions_dict(conditions_dict, coin_spend.coin.name())
|
|
1861
|
+
# assert len(pkm_pairs) == 1
|
|
1862
|
+
#
|
|
1863
|
+
# assert pkm_pairs[0][1] == solution.rest().first().get_tree_hash() + coin_spend.coin.name()
|
|
1864
|
+
#
|
|
1865
|
+
# spend_bundle = wallet_a.sign_transaction(unsigned)
|
|
1866
|
+
# assert spend_bundle is not None
|
|
1867
|
+
#
|
|
1868
|
+
# tx: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle)
|
|
1869
|
+
# await full_node_1.add_transaction(tx, peer, test=True)
|
|
1870
|
+
#
|
|
1871
|
+
# sb = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle.name())
|
|
1872
|
+
# assert sb is spend_bundle
|
|
1873
|
+
|
|
1874
|
+
@pytest.mark.anyio
|
|
1875
|
+
async def test_correct_my_parent(
|
|
1876
|
+
self,
|
|
1877
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1878
|
+
wallet_a: WalletTool,
|
|
1879
|
+
) -> None:
|
|
1880
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1881
|
+
|
|
1882
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1883
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1884
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1885
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin.parent_coin_info])
|
|
1886
|
+
dic = {cvp.opcode: [cvp]}
|
|
1887
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1888
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
1889
|
+
)
|
|
1890
|
+
|
|
1891
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1892
|
+
|
|
1893
|
+
assert err is None
|
|
1894
|
+
assert sb1 == spend_bundle1
|
|
1895
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1896
|
+
|
|
1897
|
+
@pytest.mark.anyio
|
|
1898
|
+
async def test_my_parent_garbage(
|
|
1899
|
+
self,
|
|
1900
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1901
|
+
wallet_a: WalletTool,
|
|
1902
|
+
) -> None:
|
|
1903
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1904
|
+
|
|
1905
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1906
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1907
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1908
|
+
# garbage at the end of the arguments list is allowed in consensus mode,
|
|
1909
|
+
# but not in mempool mode
|
|
1910
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin.parent_coin_info, b"garbage"])
|
|
1911
|
+
dic = {cvp.opcode: [cvp]}
|
|
1912
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1913
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
1914
|
+
)
|
|
1915
|
+
|
|
1916
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1917
|
+
|
|
1918
|
+
assert err is Err.INVALID_CONDITION
|
|
1919
|
+
assert sb1 is None
|
|
1920
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1921
|
+
|
|
1922
|
+
@pytest.mark.anyio
|
|
1923
|
+
async def test_my_parent_missing_arg(
|
|
1924
|
+
self,
|
|
1925
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1926
|
+
wallet_a: WalletTool,
|
|
1927
|
+
) -> None:
|
|
1928
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
1929
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [])
|
|
1930
|
+
dic = {cvp.opcode: [cvp]}
|
|
1931
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
1932
|
+
|
|
1933
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1934
|
+
|
|
1935
|
+
assert err == Err.INVALID_CONDITION
|
|
1936
|
+
assert sb1 is None
|
|
1937
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1938
|
+
|
|
1939
|
+
@pytest.mark.anyio
|
|
1940
|
+
async def test_invalid_my_parent(
|
|
1941
|
+
self,
|
|
1942
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1943
|
+
wallet_a: WalletTool,
|
|
1944
|
+
) -> None:
|
|
1945
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1946
|
+
|
|
1947
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1948
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1949
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1950
|
+
coin_2 = await next_block(full_node_1, wallet_a, bt)
|
|
1951
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PARENT_ID, [coin_2.parent_coin_info])
|
|
1952
|
+
dic = {cvp.opcode: [cvp]}
|
|
1953
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1954
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1957
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1958
|
+
|
|
1959
|
+
assert err == Err.ASSERT_MY_PARENT_ID_FAILED
|
|
1960
|
+
assert sb1 is None
|
|
1961
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
1962
|
+
|
|
1963
|
+
@pytest.mark.anyio
|
|
1964
|
+
async def test_correct_my_puzhash(
|
|
1965
|
+
self,
|
|
1966
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1967
|
+
wallet_a: WalletTool,
|
|
1968
|
+
) -> None:
|
|
1969
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1970
|
+
|
|
1971
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1972
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1973
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1974
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [coin.puzzle_hash])
|
|
1975
|
+
dic = {cvp.opcode: [cvp]}
|
|
1976
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
1977
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
1978
|
+
)
|
|
1979
|
+
|
|
1980
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
1981
|
+
|
|
1982
|
+
assert err is None
|
|
1983
|
+
assert sb1 == spend_bundle1
|
|
1984
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
1985
|
+
|
|
1986
|
+
@pytest.mark.anyio
|
|
1987
|
+
async def test_my_puzhash_garbage(
|
|
1988
|
+
self,
|
|
1989
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
1990
|
+
wallet_a: WalletTool,
|
|
1991
|
+
) -> None:
|
|
1992
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
1993
|
+
|
|
1994
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1995
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
1996
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
1997
|
+
# garbage at the end of the arguments list is allowed but stripped
|
|
1998
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [coin.puzzle_hash, b"garbage"])
|
|
1999
|
+
dic = {cvp.opcode: [cvp]}
|
|
2000
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
2001
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
2002
|
+
)
|
|
2003
|
+
|
|
2004
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2005
|
+
|
|
2006
|
+
assert err is Err.INVALID_CONDITION
|
|
2007
|
+
assert sb1 is None
|
|
2008
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2009
|
+
|
|
2010
|
+
@pytest.mark.anyio
|
|
2011
|
+
async def test_my_puzhash_missing_arg(
|
|
2012
|
+
self,
|
|
2013
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2014
|
+
wallet_a: WalletTool,
|
|
2015
|
+
) -> None:
|
|
2016
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
2017
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [])
|
|
2018
|
+
dic = {cvp.opcode: [cvp]}
|
|
2019
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
2020
|
+
|
|
2021
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2022
|
+
|
|
2023
|
+
assert err == Err.INVALID_CONDITION
|
|
2024
|
+
assert sb1 is None
|
|
2025
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2026
|
+
|
|
2027
|
+
@pytest.mark.anyio
|
|
2028
|
+
async def test_invalid_my_puzhash(
|
|
2029
|
+
self,
|
|
2030
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2031
|
+
wallet_a: WalletTool,
|
|
2032
|
+
) -> None:
|
|
2033
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
2034
|
+
|
|
2035
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2036
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2037
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
2038
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_PUZZLEHASH, [Program.to([]).get_tree_hash()])
|
|
2039
|
+
dic = {cvp.opcode: [cvp]}
|
|
2040
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
2041
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
2042
|
+
)
|
|
2043
|
+
|
|
2044
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2045
|
+
|
|
2046
|
+
assert err == Err.ASSERT_MY_PUZZLEHASH_FAILED
|
|
2047
|
+
assert sb1 is None
|
|
2048
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2049
|
+
|
|
2050
|
+
@pytest.mark.anyio
|
|
2051
|
+
async def test_correct_my_amount(
|
|
2052
|
+
self,
|
|
2053
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2054
|
+
wallet_a: WalletTool,
|
|
2055
|
+
) -> None:
|
|
2056
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
2057
|
+
|
|
2058
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2059
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2060
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
2061
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(coin.amount)])
|
|
2062
|
+
dic = {cvp.opcode: [cvp]}
|
|
2063
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
2064
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
2065
|
+
)
|
|
2066
|
+
|
|
2067
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2068
|
+
|
|
2069
|
+
assert err is None
|
|
2070
|
+
assert sb1 == spend_bundle1
|
|
2071
|
+
assert status == MempoolInclusionStatus.SUCCESS
|
|
2072
|
+
|
|
2073
|
+
@pytest.mark.anyio
|
|
2074
|
+
async def test_my_amount_garbage(
|
|
2075
|
+
self,
|
|
2076
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2077
|
+
wallet_a: WalletTool,
|
|
2078
|
+
) -> None:
|
|
2079
|
+
full_node_1, _server_1, bt = one_node_one_block
|
|
2080
|
+
|
|
2081
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2082
|
+
_ = await next_block(full_node_1, wallet_a, bt)
|
|
2083
|
+
coin = await next_block(full_node_1, wallet_a, bt)
|
|
2084
|
+
# garbage at the end of the arguments list is allowed in consensus mode,
|
|
2085
|
+
# but not in mempool mode
|
|
2086
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(coin.amount), b"garbage"])
|
|
2087
|
+
dic = {cvp.opcode: [cvp]}
|
|
2088
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(
|
|
2089
|
+
one_node_one_block, wallet_a, dic, coin=coin
|
|
2090
|
+
)
|
|
2091
|
+
|
|
2092
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2093
|
+
|
|
2094
|
+
assert err is Err.INVALID_CONDITION
|
|
2095
|
+
assert sb1 is None
|
|
2096
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2097
|
+
|
|
2098
|
+
@pytest.mark.anyio
|
|
2099
|
+
async def test_my_amount_missing_arg(
|
|
2100
|
+
self,
|
|
2101
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2102
|
+
wallet_a: WalletTool,
|
|
2103
|
+
) -> None:
|
|
2104
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
2105
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [])
|
|
2106
|
+
dic = {cvp.opcode: [cvp]}
|
|
2107
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
2108
|
+
|
|
2109
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2110
|
+
|
|
2111
|
+
assert err == Err.INVALID_CONDITION
|
|
2112
|
+
assert sb1 is None
|
|
2113
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2114
|
+
|
|
2115
|
+
@pytest.mark.anyio
|
|
2116
|
+
async def test_invalid_my_amount(
|
|
2117
|
+
self,
|
|
2118
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2119
|
+
wallet_a: WalletTool,
|
|
2120
|
+
) -> None:
|
|
2121
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
2122
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(1000)])
|
|
2123
|
+
dic = {cvp.opcode: [cvp]}
|
|
2124
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
2125
|
+
|
|
2126
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2127
|
+
|
|
2128
|
+
assert err == Err.ASSERT_MY_AMOUNT_FAILED
|
|
2129
|
+
assert sb1 is None
|
|
2130
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2131
|
+
|
|
2132
|
+
@pytest.mark.anyio
|
|
2133
|
+
async def test_negative_my_amount(
|
|
2134
|
+
self,
|
|
2135
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2136
|
+
wallet_a: WalletTool,
|
|
2137
|
+
) -> None:
|
|
2138
|
+
full_node_1, _server_1, _bt = one_node_one_block
|
|
2139
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(-1)])
|
|
2140
|
+
dic = {cvp.opcode: [cvp]}
|
|
2141
|
+
_blocks, spend_bundle1, _peer, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
2142
|
+
|
|
2143
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2144
|
+
|
|
2145
|
+
assert err == Err.ASSERT_MY_AMOUNT_FAILED
|
|
2146
|
+
assert sb1 is None
|
|
2147
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2148
|
+
|
|
2149
|
+
@pytest.mark.anyio
|
|
2150
|
+
async def test_my_amount_too_large(
|
|
2151
|
+
self,
|
|
2152
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2153
|
+
wallet_a: WalletTool,
|
|
2154
|
+
) -> None:
|
|
2155
|
+
full_node_1, _, _ = one_node_one_block
|
|
2156
|
+
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_MY_AMOUNT, [int_to_bytes(2**64)])
|
|
2157
|
+
dic = {cvp.opcode: [cvp]}
|
|
2158
|
+
_, spend_bundle1, _, status, err = await self.condition_tester(one_node_one_block, wallet_a, dic)
|
|
2159
|
+
|
|
2160
|
+
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
|
2161
|
+
|
|
2162
|
+
assert err == Err.ASSERT_MY_AMOUNT_FAILED
|
|
2163
|
+
assert sb1 is None
|
|
2164
|
+
assert status == MempoolInclusionStatus.FAILED
|
|
2165
|
+
|
|
2166
|
+
|
|
2167
|
+
# the following tests generate generator programs and run them through get_name_puzzle_conditions()
|
|
2168
|
+
|
|
2169
|
+
COST_PER_BYTE = 12000
|
|
2170
|
+
MAX_BLOCK_COST_CLVM = 11000000000
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
def generator_condition_tester(
|
|
2174
|
+
conditions: str,
|
|
2175
|
+
*,
|
|
2176
|
+
mempool_mode: bool = False,
|
|
2177
|
+
quote: bool = True,
|
|
2178
|
+
max_cost: int = MAX_BLOCK_COST_CLVM,
|
|
2179
|
+
height: uint32,
|
|
2180
|
+
coin_amount: int = 123,
|
|
2181
|
+
) -> NPCResult:
|
|
2182
|
+
prg = f"(q ((0x0101010101010101010101010101010101010101010101010101010101010101 {'(q ' if quote else ''} {conditions} {')' if quote else ''} {coin_amount} (() (q . ())))))" # noqa
|
|
2183
|
+
print(f"program: {prg}")
|
|
2184
|
+
program = SerializedProgram.from_bytes(binutils.assemble(prg).as_bin())
|
|
2185
|
+
generator = BlockGenerator(program, [])
|
|
2186
|
+
print(f"len: {len(bytes(program))}")
|
|
2187
|
+
npc_result: NPCResult = get_name_puzzle_conditions(
|
|
2188
|
+
generator, max_cost, mempool_mode=mempool_mode, height=height, constants=test_constants
|
|
2189
|
+
)
|
|
2190
|
+
return npc_result
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
class TestGeneratorConditions:
|
|
2194
|
+
def test_invalid_condition_args_terminator(self, softfork_height: uint32) -> None:
|
|
2195
|
+
# note how the condition argument list isn't correctly terminated with a
|
|
2196
|
+
# NIL atom. This is allowed, and all arguments beyond the ones we look
|
|
2197
|
+
# at are ignored, including the termination of the list
|
|
2198
|
+
npc_result = generator_condition_tester("(80 50 . 1)", height=softfork_height)
|
|
2199
|
+
assert npc_result.error is None
|
|
2200
|
+
assert npc_result.conds is not None
|
|
2201
|
+
assert len(npc_result.conds.spends) == 1
|
|
2202
|
+
assert npc_result.conds.spends[0].seconds_relative == 50
|
|
2203
|
+
|
|
2204
|
+
@pytest.mark.parametrize(
|
|
2205
|
+
"mempool,operand",
|
|
2206
|
+
[
|
|
2207
|
+
(True, -1),
|
|
2208
|
+
(False, -1),
|
|
2209
|
+
(True, 1),
|
|
2210
|
+
(False, 1),
|
|
2211
|
+
],
|
|
2212
|
+
)
|
|
2213
|
+
def test_div(self, mempool: bool, operand: int, softfork_height: uint32) -> None:
|
|
2214
|
+
# op_div is disallowed on negative numbers in the mempool, and after the
|
|
2215
|
+
# softfork
|
|
2216
|
+
npc_result = generator_condition_tester(
|
|
2217
|
+
f"(c (c (q . 80) (c (/ (q . 50) (q . {operand})) ())) ())",
|
|
2218
|
+
quote=False,
|
|
2219
|
+
mempool_mode=mempool,
|
|
2220
|
+
height=softfork_height,
|
|
2221
|
+
)
|
|
2222
|
+
|
|
2223
|
+
# after the 2.0 hard fork, division with negative numbers is allowed
|
|
2224
|
+
assert npc_result.error is None
|
|
2225
|
+
|
|
2226
|
+
def test_invalid_condition_list_terminator(self, softfork_height: uint32) -> None:
|
|
2227
|
+
# note how the list of conditions isn't correctly terminated with a
|
|
2228
|
+
# NIL atom. This is a failure
|
|
2229
|
+
npc_result = generator_condition_tester("(80 50) . 3", height=softfork_height)
|
|
2230
|
+
assert npc_result.error in {Err.INVALID_CONDITION.value, Err.GENERATOR_RUNTIME_ERROR.value}
|
|
2231
|
+
|
|
2232
|
+
@pytest.mark.parametrize(
|
|
2233
|
+
"opcode",
|
|
2234
|
+
[
|
|
2235
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2236
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2237
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2238
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2239
|
+
],
|
|
2240
|
+
)
|
|
2241
|
+
def test_duplicate_height_time_conditions(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
|
|
2242
|
+
# even though the generator outputs multiple conditions, we only
|
|
2243
|
+
# need to return the highest one (i.e. most strict)
|
|
2244
|
+
npc_result = generator_condition_tester(
|
|
2245
|
+
" ".join([f"({opcode.value[0]} {i})" for i in range(50, 101)]), height=softfork_height
|
|
2246
|
+
)
|
|
2247
|
+
print(npc_result)
|
|
2248
|
+
assert npc_result.error is None
|
|
2249
|
+
assert npc_result.conds is not None
|
|
2250
|
+
assert len(npc_result.conds.spends) == 1
|
|
2251
|
+
|
|
2252
|
+
assert len(npc_result.conds.spends) == 1
|
|
2253
|
+
if opcode == ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE:
|
|
2254
|
+
assert npc_result.conds.height_absolute == 100
|
|
2255
|
+
elif opcode == ConditionOpcode.ASSERT_HEIGHT_RELATIVE:
|
|
2256
|
+
assert npc_result.conds.spends[0].height_relative == 100
|
|
2257
|
+
elif opcode == ConditionOpcode.ASSERT_SECONDS_ABSOLUTE:
|
|
2258
|
+
assert npc_result.conds.seconds_absolute == 100
|
|
2259
|
+
elif opcode == ConditionOpcode.ASSERT_SECONDS_RELATIVE:
|
|
2260
|
+
assert npc_result.conds.spends[0].seconds_relative == 100
|
|
2261
|
+
|
|
2262
|
+
@pytest.mark.parametrize(
|
|
2263
|
+
"opcode",
|
|
2264
|
+
[
|
|
2265
|
+
ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
|
|
2266
|
+
ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
|
|
2267
|
+
],
|
|
2268
|
+
)
|
|
2269
|
+
def test_just_announcement(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
|
|
2270
|
+
message = "a" * 1024
|
|
2271
|
+
# announcements are validated on the Rust side and never returned
|
|
2272
|
+
# back. They are either satisified or cause an immediate failure
|
|
2273
|
+
npc_result = generator_condition_tester(f'({opcode.value[0]} "{message}") ' * 50, height=softfork_height)
|
|
2274
|
+
assert npc_result.error is None
|
|
2275
|
+
assert npc_result.conds is not None
|
|
2276
|
+
assert len(npc_result.conds.spends) == 1
|
|
2277
|
+
# create-announcements and assert-announcements are dropped once
|
|
2278
|
+
# validated
|
|
2279
|
+
|
|
2280
|
+
@pytest.mark.parametrize(
|
|
2281
|
+
"opcode",
|
|
2282
|
+
[
|
|
2283
|
+
ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
|
|
2284
|
+
ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT,
|
|
2285
|
+
],
|
|
2286
|
+
)
|
|
2287
|
+
def test_assert_announcement_fail(self, opcode: ConditionOpcode, softfork_height: uint32) -> None:
|
|
2288
|
+
message = "a" * 1024
|
|
2289
|
+
# announcements are validated on the Rust side and never returned
|
|
2290
|
+
# back. They ar either satisified or cause an immediate failure
|
|
2291
|
+
# in this test we just assert announcements, we never make them, so
|
|
2292
|
+
# these should fail
|
|
2293
|
+
npc_result = generator_condition_tester(f'({opcode.value[0]} "{message}") ', height=softfork_height)
|
|
2294
|
+
print(npc_result)
|
|
2295
|
+
assert npc_result.error == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED.value
|
|
2296
|
+
|
|
2297
|
+
def test_multiple_reserve_fee(self, softfork_height: uint32) -> None:
|
|
2298
|
+
# RESERVE_FEE
|
|
2299
|
+
cond = 52
|
|
2300
|
+
# even though the generator outputs 3 conditions, we only need to return one copy
|
|
2301
|
+
# with all the fees accumulated
|
|
2302
|
+
npc_result = generator_condition_tester(f"({cond} 10) " * 3, height=softfork_height)
|
|
2303
|
+
assert npc_result.error is None
|
|
2304
|
+
assert npc_result.conds is not None
|
|
2305
|
+
assert npc_result.conds.reserve_fee == 30
|
|
2306
|
+
assert len(npc_result.conds.spends) == 1
|
|
2307
|
+
|
|
2308
|
+
def test_duplicate_outputs(self, softfork_height: uint32) -> None:
|
|
2309
|
+
# CREATE_COIN
|
|
2310
|
+
# creating multiple coins with the same properties (same parent, same
|
|
2311
|
+
# target puzzle hash and same amount) is not allowed. That's a consensus
|
|
2312
|
+
# failure.
|
|
2313
|
+
puzzle_hash = "abababababababababababababababab"
|
|
2314
|
+
npc_result = generator_condition_tester(f'(51 "{puzzle_hash}" 10) ' * 2, height=softfork_height)
|
|
2315
|
+
assert npc_result.error == Err.DUPLICATE_OUTPUT.value
|
|
2316
|
+
|
|
2317
|
+
def test_create_coin_cost(self, softfork_height: uint32) -> None:
|
|
2318
|
+
# CREATE_COIN
|
|
2319
|
+
puzzle_hash = "abababababababababababababababab"
|
|
2320
|
+
|
|
2321
|
+
if softfork_height >= test_constants.HARD_FORK_HEIGHT:
|
|
2322
|
+
generator_base_cost = 40
|
|
2323
|
+
else:
|
|
2324
|
+
generator_base_cost = 20470
|
|
2325
|
+
|
|
2326
|
+
# this max cost is exactly enough for the create coin condition
|
|
2327
|
+
npc_result = generator_condition_tester(
|
|
2328
|
+
f'(51 "{puzzle_hash}" 10) ',
|
|
2329
|
+
max_cost=generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value,
|
|
2330
|
+
height=softfork_height,
|
|
2331
|
+
)
|
|
2332
|
+
assert npc_result.error is None
|
|
2333
|
+
assert npc_result.conds is not None
|
|
2334
|
+
assert npc_result.conds.cost == generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value
|
|
2335
|
+
assert len(npc_result.conds.spends) == 1
|
|
2336
|
+
assert len(npc_result.conds.spends[0].create_coin) == 1
|
|
2337
|
+
|
|
2338
|
+
# if we subtract one from max cost, this should fail
|
|
2339
|
+
npc_result = generator_condition_tester(
|
|
2340
|
+
f'(51 "{puzzle_hash}" 10) ',
|
|
2341
|
+
max_cost=generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value - 1,
|
|
2342
|
+
height=softfork_height,
|
|
2343
|
+
)
|
|
2344
|
+
assert npc_result.error in {Err.BLOCK_COST_EXCEEDS_MAX.value, Err.INVALID_BLOCK_COST.value}
|
|
2345
|
+
|
|
2346
|
+
@pytest.mark.parametrize(
|
|
2347
|
+
"condition",
|
|
2348
|
+
[
|
|
2349
|
+
ConditionOpcode.AGG_SIG_PARENT,
|
|
2350
|
+
ConditionOpcode.AGG_SIG_PUZZLE,
|
|
2351
|
+
ConditionOpcode.AGG_SIG_AMOUNT,
|
|
2352
|
+
ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
|
|
2353
|
+
ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
|
|
2354
|
+
ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
|
|
2355
|
+
ConditionOpcode.AGG_SIG_UNSAFE,
|
|
2356
|
+
ConditionOpcode.AGG_SIG_ME,
|
|
2357
|
+
],
|
|
2358
|
+
)
|
|
2359
|
+
def test_agg_sig_cost(self, condition: ConditionOpcode, softfork_height: uint32) -> None:
|
|
2360
|
+
pubkey = "0x" + bytes(G1Element.generator()).hex()
|
|
2361
|
+
|
|
2362
|
+
if softfork_height >= test_constants.HARD_FORK_HEIGHT:
|
|
2363
|
+
generator_base_cost = 40
|
|
2364
|
+
else:
|
|
2365
|
+
generator_base_cost = 20512
|
|
2366
|
+
|
|
2367
|
+
expected_cost = ConditionCost.AGG_SIG.value
|
|
2368
|
+
|
|
2369
|
+
# this max cost is exactly enough for the AGG_SIG condition
|
|
2370
|
+
npc_result = generator_condition_tester(
|
|
2371
|
+
f'({condition[0]} {pubkey} "foobar") ',
|
|
2372
|
+
max_cost=generator_base_cost + 117 * COST_PER_BYTE + expected_cost,
|
|
2373
|
+
height=softfork_height,
|
|
2374
|
+
)
|
|
2375
|
+
assert npc_result.error is None
|
|
2376
|
+
assert npc_result.conds is not None
|
|
2377
|
+
assert npc_result.conds.cost == generator_base_cost + 117 * COST_PER_BYTE + expected_cost
|
|
2378
|
+
assert len(npc_result.conds.spends) == 1
|
|
2379
|
+
|
|
2380
|
+
# if we subtract one from max cost, this should fail
|
|
2381
|
+
npc_result = generator_condition_tester(
|
|
2382
|
+
f'({condition[0]} {pubkey} "foobar") ',
|
|
2383
|
+
max_cost=generator_base_cost + 117 * COST_PER_BYTE + expected_cost - 1,
|
|
2384
|
+
height=softfork_height,
|
|
2385
|
+
)
|
|
2386
|
+
assert npc_result.error in {
|
|
2387
|
+
Err.GENERATOR_RUNTIME_ERROR.value,
|
|
2388
|
+
Err.BLOCK_COST_EXCEEDS_MAX.value,
|
|
2389
|
+
Err.INVALID_BLOCK_COST.value,
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
@pytest.mark.parametrize(
|
|
2393
|
+
"condition",
|
|
2394
|
+
[
|
|
2395
|
+
ConditionOpcode.AGG_SIG_PARENT,
|
|
2396
|
+
ConditionOpcode.AGG_SIG_PUZZLE,
|
|
2397
|
+
ConditionOpcode.AGG_SIG_AMOUNT,
|
|
2398
|
+
ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
|
|
2399
|
+
ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
|
|
2400
|
+
ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
|
|
2401
|
+
ConditionOpcode.AGG_SIG_UNSAFE,
|
|
2402
|
+
ConditionOpcode.AGG_SIG_ME,
|
|
2403
|
+
],
|
|
2404
|
+
)
|
|
2405
|
+
@pytest.mark.parametrize("extra_arg", [' "baz"', ""])
|
|
2406
|
+
@pytest.mark.parametrize("mempool", [True, False])
|
|
2407
|
+
def test_agg_sig_extra_arg(
|
|
2408
|
+
self, condition: ConditionOpcode, extra_arg: str, mempool: bool, softfork_height: uint32
|
|
2409
|
+
) -> None:
|
|
2410
|
+
pubkey = "0x" + bytes(G1Element.generator()).hex()
|
|
2411
|
+
|
|
2412
|
+
# in mempool mode, we don't allow extra arguments
|
|
2413
|
+
if mempool and extra_arg != "":
|
|
2414
|
+
expected_error = Err.INVALID_CONDITION.value
|
|
2415
|
+
else:
|
|
2416
|
+
expected_error = None
|
|
2417
|
+
|
|
2418
|
+
# this max cost is exactly enough for the AGG_SIG condition
|
|
2419
|
+
npc_result = generator_condition_tester(
|
|
2420
|
+
f'({condition[0]} {pubkey} "foobar"{extra_arg}) ',
|
|
2421
|
+
max_cost=11000000000,
|
|
2422
|
+
height=softfork_height,
|
|
2423
|
+
mempool_mode=mempool,
|
|
2424
|
+
)
|
|
2425
|
+
assert npc_result.error == expected_error
|
|
2426
|
+
if npc_result.error is None:
|
|
2427
|
+
assert npc_result.conds is not None
|
|
2428
|
+
assert len(npc_result.conds.spends) == 1
|
|
2429
|
+
else:
|
|
2430
|
+
assert npc_result.conds is None
|
|
2431
|
+
|
|
2432
|
+
def test_create_coin_different_parent(self, softfork_height: uint32) -> None:
|
|
2433
|
+
# if the coins we create have different parents, they are never
|
|
2434
|
+
# considered duplicate, even when they have the same puzzle hash and
|
|
2435
|
+
# amount
|
|
2436
|
+
puzzle_hash = "abababababababababababababababab"
|
|
2437
|
+
program = SerializedProgram.from_bytes(
|
|
2438
|
+
binutils.assemble(
|
|
2439
|
+
f'(q ((0x0101010101010101010101010101010101010101010101010101010101010101 (q (51 "{puzzle_hash}" 10)) 123 (() (q . ())))(0x0101010101010101010101010101010101010101010101010101010101010102 (q (51 "{puzzle_hash}" 10)) 123 (() (q . ()))) ))' # noqa
|
|
2440
|
+
).as_bin()
|
|
2441
|
+
)
|
|
2442
|
+
generator = BlockGenerator(program, [])
|
|
2443
|
+
npc_result: NPCResult = get_name_puzzle_conditions(
|
|
2444
|
+
generator, MAX_BLOCK_COST_CLVM, mempool_mode=False, height=softfork_height, constants=test_constants
|
|
2445
|
+
)
|
|
2446
|
+
assert npc_result.error is None
|
|
2447
|
+
assert npc_result.conds is not None
|
|
2448
|
+
assert len(npc_result.conds.spends) == 2
|
|
2449
|
+
for s in npc_result.conds.spends:
|
|
2450
|
+
assert s.create_coin == [(puzzle_hash.encode("ascii"), 10, None)]
|
|
2451
|
+
|
|
2452
|
+
def test_create_coin_different_puzzhash(self, softfork_height: uint32) -> None:
|
|
2453
|
+
# CREATE_COIN
|
|
2454
|
+
# coins with different puzzle hashes are not considered duplicate
|
|
2455
|
+
puzzle_hash_1 = "abababababababababababababababab"
|
|
2456
|
+
puzzle_hash_2 = "cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb"
|
|
2457
|
+
npc_result = generator_condition_tester(
|
|
2458
|
+
f'(51 "{puzzle_hash_1}" 5) (51 "{puzzle_hash_2}" 5)', height=softfork_height
|
|
2459
|
+
)
|
|
2460
|
+
assert npc_result.error is None
|
|
2461
|
+
assert npc_result.conds is not None
|
|
2462
|
+
assert len(npc_result.conds.spends) == 1
|
|
2463
|
+
assert (puzzle_hash_1.encode("ascii"), 5, None) in npc_result.conds.spends[0].create_coin
|
|
2464
|
+
assert (puzzle_hash_2.encode("ascii"), 5, None) in npc_result.conds.spends[0].create_coin
|
|
2465
|
+
|
|
2466
|
+
def test_create_coin_different_amounts(self, softfork_height: uint32) -> None:
|
|
2467
|
+
# CREATE_COIN
|
|
2468
|
+
# coins with different amounts are not considered duplicate
|
|
2469
|
+
puzzle_hash = "abababababababababababababababab"
|
|
2470
|
+
npc_result = generator_condition_tester(
|
|
2471
|
+
f'(51 "{puzzle_hash}" 5) (51 "{puzzle_hash}" 4)', height=softfork_height
|
|
2472
|
+
)
|
|
2473
|
+
assert npc_result.error is None
|
|
2474
|
+
assert npc_result.conds is not None
|
|
2475
|
+
assert len(npc_result.conds.spends) == 1
|
|
2476
|
+
coins = npc_result.conds.spends[0].create_coin
|
|
2477
|
+
assert (puzzle_hash.encode("ascii"), 5, None) in coins
|
|
2478
|
+
assert (puzzle_hash.encode("ascii"), 4, None) in coins
|
|
2479
|
+
|
|
2480
|
+
def test_create_coin_with_hint(self, softfork_height: uint32) -> None:
|
|
2481
|
+
# CREATE_COIN
|
|
2482
|
+
puzzle_hash_1 = "abababababababababababababababab"
|
|
2483
|
+
hint = "12341234123412341234213421341234"
|
|
2484
|
+
npc_result = generator_condition_tester(f'(51 "{puzzle_hash_1}" 5 ("{hint}"))', height=softfork_height)
|
|
2485
|
+
assert npc_result.error is None
|
|
2486
|
+
assert npc_result.conds is not None
|
|
2487
|
+
assert len(npc_result.conds.spends) == 1
|
|
2488
|
+
coins = npc_result.conds.spends[0].create_coin
|
|
2489
|
+
assert coins == [(puzzle_hash_1.encode("ascii"), 5, hint.encode("ascii"))]
|
|
2490
|
+
|
|
2491
|
+
@pytest.mark.parametrize("mempool", [True, False])
|
|
2492
|
+
@pytest.mark.parametrize(
|
|
2493
|
+
"condition",
|
|
2494
|
+
[
|
|
2495
|
+
'(2 100 "foo" "bar")',
|
|
2496
|
+
"(100)",
|
|
2497
|
+
"(4 1) (2 2) (3 3)",
|
|
2498
|
+
'("foobar")',
|
|
2499
|
+
'(0x100 "foobar")',
|
|
2500
|
+
'(0x1ff "foobar")',
|
|
2501
|
+
],
|
|
2502
|
+
)
|
|
2503
|
+
def test_unknown_condition(self, mempool: bool, condition: str, softfork_height: uint32) -> None:
|
|
2504
|
+
npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
|
|
2505
|
+
print(npc_result)
|
|
2506
|
+
if mempool:
|
|
2507
|
+
assert npc_result.error == Err.INVALID_CONDITION.value
|
|
2508
|
+
else:
|
|
2509
|
+
assert npc_result.error is None
|
|
2510
|
+
|
|
2511
|
+
@pytest.mark.parametrize("mempool", [True, False])
|
|
2512
|
+
@pytest.mark.parametrize(
|
|
2513
|
+
"condition, expect_error",
|
|
2514
|
+
[
|
|
2515
|
+
# the softfork condition must include at least 1 argument to
|
|
2516
|
+
# indicate its cost
|
|
2517
|
+
("(90)", Err.INVALID_CONDITION.value),
|
|
2518
|
+
("(90 1000000)", None),
|
|
2519
|
+
],
|
|
2520
|
+
)
|
|
2521
|
+
def test_softfork_condition(
|
|
2522
|
+
self, mempool: bool, condition: str, expect_error: Optional[int], softfork_height: uint32
|
|
2523
|
+
) -> None:
|
|
2524
|
+
npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
|
|
2525
|
+
print(npc_result)
|
|
2526
|
+
|
|
2527
|
+
# in mempool all unknown conditions are always a failure
|
|
2528
|
+
if mempool:
|
|
2529
|
+
expect_error = Err.INVALID_CONDITION.value
|
|
2530
|
+
|
|
2531
|
+
assert npc_result.error == expect_error
|
|
2532
|
+
|
|
2533
|
+
@pytest.mark.parametrize("mempool", [True, False])
|
|
2534
|
+
@pytest.mark.parametrize(
|
|
2535
|
+
"condition, expect_error",
|
|
2536
|
+
[
|
|
2537
|
+
('(66 0 "foo") (67 0 "bar")', Err.MESSAGE_NOT_SENT_OR_RECEIVED.value),
|
|
2538
|
+
('(66 0 "foo") (67 0 "foo")', None),
|
|
2539
|
+
],
|
|
2540
|
+
)
|
|
2541
|
+
def test_message_condition(
|
|
2542
|
+
self, mempool: bool, condition: str, expect_error: Optional[int], softfork_height: uint32
|
|
2543
|
+
) -> None:
|
|
2544
|
+
npc_result = generator_condition_tester(condition, mempool_mode=mempool, height=softfork_height)
|
|
2545
|
+
print(npc_result)
|
|
2546
|
+
assert npc_result.error == expect_error
|
|
2547
|
+
|
|
2548
|
+
|
|
2549
|
+
# the tests below are malicious generator programs
|
|
2550
|
+
|
|
2551
|
+
# this program:
|
|
2552
|
+
# (mod (A B)
|
|
2553
|
+
# (defun large_string (V N)
|
|
2554
|
+
# (if N (large_string (concat V V) (- N 1)) V)
|
|
2555
|
+
# )
|
|
2556
|
+
# (defun iter (V N)
|
|
2557
|
+
# (if N (c V (iter V (- N 1))) ())
|
|
2558
|
+
# )
|
|
2559
|
+
# (iter (c (q . 83) (c (concat (large_string 0x00 A) (q . 100)) ())) B)
|
|
2560
|
+
# )
|
|
2561
|
+
# with A=28 and B specified as {num}
|
|
2562
|
+
|
|
2563
|
+
SINGLE_ARG_INT_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa
|
|
2564
|
+
|
|
2565
|
+
# this program:
|
|
2566
|
+
# (mod (A B)
|
|
2567
|
+
# (defun large_string (V N)
|
|
2568
|
+
# (if N (large_string (concat V V) (- N 1)) V)
|
|
2569
|
+
# )
|
|
2570
|
+
# (defun iter (V N)
|
|
2571
|
+
# (if N (c (c (q . 83) (c V ())) (iter (substr V 1) (- N 1))) ())
|
|
2572
|
+
# )
|
|
2573
|
+
# (iter (concat (large_string 0x00 A) (q . 100)) B)
|
|
2574
|
+
# )
|
|
2575
|
+
# truncates the first byte of the large string being passed down for each
|
|
2576
|
+
# iteration, in an attempt to defeat any caching of integers by node ID.
|
|
2577
|
+
# substr is cheap, and no memory is copied, so we can perform a lot of these
|
|
2578
|
+
SINGLE_ARG_INT_SUBSTR_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 (q . 1)) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa
|
|
2579
|
+
|
|
2580
|
+
# this program:
|
|
2581
|
+
# (mod (A B)
|
|
2582
|
+
# (defun large_string (V N)
|
|
2583
|
+
# (if N (large_string (concat V V) (- N 1)) V)
|
|
2584
|
+
# )
|
|
2585
|
+
# (defun iter (V N)
|
|
2586
|
+
# (if N (c (c (q . 83) (c V ())) (iter (substr V 0 (- (strlen V) 1)) (- N 1))) ())
|
|
2587
|
+
# )
|
|
2588
|
+
# (iter (concat (large_string 0x00 A) (q . 0xffffffff)) B)
|
|
2589
|
+
# )
|
|
2590
|
+
SINGLE_ARG_INT_SUBSTR_TAIL_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 () (- (strlen 5) (q . 1))) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 25 {num})))" # noqa
|
|
2591
|
+
|
|
2592
|
+
# (mod (A B)
|
|
2593
|
+
# (defun large_string (V N)
|
|
2594
|
+
# (if N (large_string (concat V V) (- N 1)) V)
|
|
2595
|
+
# )
|
|
2596
|
+
# (defun iter (V N)
|
|
2597
|
+
# (if N (c (c (q . 83) (c (concat V N) ())) (iter V (- N 1))) ())
|
|
2598
|
+
# )
|
|
2599
|
+
# (iter (large_string 0x00 A) B)
|
|
2600
|
+
# )
|
|
2601
|
+
SINGLE_ARG_INT_LADDER_COND = "(a (q 2 4 (c 2 (c (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c (concat 5 11) ())) (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 24 {num})))" # noqa
|
|
2602
|
+
|
|
2603
|
+
# this program:
|
|
2604
|
+
# (mod (A B)
|
|
2605
|
+
# (defun large_message (N)
|
|
2606
|
+
# (lsh (q . "a") N)
|
|
2607
|
+
# )
|
|
2608
|
+
# (defun iter (V N)
|
|
2609
|
+
# (if N (c V (iter V (- N 1))) ())
|
|
2610
|
+
# )
|
|
2611
|
+
# (iter (c (q . 60) (c (large_message A) ())) B)
|
|
2612
|
+
# )
|
|
2613
|
+
# with B set to {num}
|
|
2614
|
+
|
|
2615
|
+
CREATE_ANNOUNCE_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (a 6 (c 2 (c 5 ()))) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 23 (q . 97) 5) (q 8184 {num})))" # noqa
|
|
2616
|
+
|
|
2617
|
+
# this program:
|
|
2618
|
+
# (mod (A)
|
|
2619
|
+
# (defun iter (V N)
|
|
2620
|
+
# (if N (c V (iter V (- N 1))) ())
|
|
2621
|
+
# )
|
|
2622
|
+
# (iter (q 51 "abababababababababababababababab" 1) A)
|
|
2623
|
+
# )
|
|
2624
|
+
CREATE_COIN = '(a (q 2 2 (c 2 (c (q 51 "abababababababababababababababab" 1) (c 5 ())))) (c (q 2 (i 11 (q 4 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa
|
|
2625
|
+
|
|
2626
|
+
# this program:
|
|
2627
|
+
# (mod (A)
|
|
2628
|
+
# (defun append (L B)
|
|
2629
|
+
# (if L
|
|
2630
|
+
# (c (f L) (append (r L) B))
|
|
2631
|
+
# (c B ())
|
|
2632
|
+
# )
|
|
2633
|
+
# )
|
|
2634
|
+
# (defun iter (V N)
|
|
2635
|
+
# (if N (c (append V N) (iter V (- N 1))) ())
|
|
2636
|
+
# )
|
|
2637
|
+
# (iter (q 51 "abababababababababababababababab") A)
|
|
2638
|
+
# )
|
|
2639
|
+
# creates {num} CREATE_COIN conditions, each with a different amount
|
|
2640
|
+
CREATE_UNIQUE_COINS = '(a (q 2 6 (c 2 (c (q 51 "abababababababababababababababab") (c 5 ())))) (c (q (a (i 5 (q 4 9 (a 4 (c 2 (c 13 (c 11 ()))))) (q 4 11 ())) 1) 2 (i 11 (q 4 (a 4 (c 2 (c 5 (c 11 ())))) (a 6 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa
|
|
2641
|
+
|
|
2642
|
+
|
|
2643
|
+
# some of the malicious tests will fail post soft-fork, this function helps test
|
|
2644
|
+
# the specific error to expect
|
|
2645
|
+
def error_for_condition(cond: ConditionOpcode) -> int:
|
|
2646
|
+
if cond == ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE:
|
|
2647
|
+
return Err.ASSERT_HEIGHT_ABSOLUTE_FAILED.value
|
|
2648
|
+
if cond == ConditionOpcode.ASSERT_HEIGHT_RELATIVE:
|
|
2649
|
+
return Err.ASSERT_HEIGHT_RELATIVE_FAILED.value
|
|
2650
|
+
if cond == ConditionOpcode.ASSERT_SECONDS_ABSOLUTE:
|
|
2651
|
+
return Err.ASSERT_SECONDS_ABSOLUTE_FAILED.value
|
|
2652
|
+
if cond == ConditionOpcode.ASSERT_SECONDS_RELATIVE:
|
|
2653
|
+
return Err.ASSERT_SECONDS_RELATIVE_FAILED.value
|
|
2654
|
+
if cond == ConditionOpcode.RESERVE_FEE:
|
|
2655
|
+
return Err.RESERVE_FEE_CONDITION_FAILED.value
|
|
2656
|
+
assert False
|
|
2657
|
+
|
|
2658
|
+
|
|
2659
|
+
class TestMaliciousGenerators:
|
|
2660
|
+
# TODO: create a lot of announcements. The messages can be made different by
|
|
2661
|
+
# using substr on a large buffer
|
|
2662
|
+
|
|
2663
|
+
# for all the height/time locks, we should only return the most strict
|
|
2664
|
+
# condition, not all of them
|
|
2665
|
+
@pytest.mark.parametrize(
|
|
2666
|
+
"opcode",
|
|
2667
|
+
[
|
|
2668
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2669
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2670
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2671
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2672
|
+
],
|
|
2673
|
+
)
|
|
2674
|
+
def test_duplicate_large_integer_ladder(
|
|
2675
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2676
|
+
) -> None:
|
|
2677
|
+
condition = SINGLE_ARG_INT_LADDER_COND.format(opcode=opcode.value[0], num=28, filler="0x00")
|
|
2678
|
+
|
|
2679
|
+
with benchmark_runner.assert_runtime(seconds=1):
|
|
2680
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2681
|
+
|
|
2682
|
+
assert npc_result.error == error_for_condition(opcode)
|
|
2683
|
+
|
|
2684
|
+
@pytest.mark.parametrize(
|
|
2685
|
+
"opcode",
|
|
2686
|
+
[
|
|
2687
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2688
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2689
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2690
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2691
|
+
],
|
|
2692
|
+
)
|
|
2693
|
+
def test_duplicate_large_integer(
|
|
2694
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2695
|
+
) -> None:
|
|
2696
|
+
condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
|
|
2697
|
+
|
|
2698
|
+
with benchmark_runner.assert_runtime(seconds=3):
|
|
2699
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2700
|
+
|
|
2701
|
+
assert npc_result.error == error_for_condition(opcode)
|
|
2702
|
+
|
|
2703
|
+
@pytest.mark.parametrize(
|
|
2704
|
+
"opcode",
|
|
2705
|
+
[
|
|
2706
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2707
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2708
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2709
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2710
|
+
],
|
|
2711
|
+
)
|
|
2712
|
+
def test_duplicate_large_integer_substr(
|
|
2713
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2714
|
+
) -> None:
|
|
2715
|
+
condition = SINGLE_ARG_INT_SUBSTR_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
|
|
2716
|
+
|
|
2717
|
+
with benchmark_runner.assert_runtime(seconds=2):
|
|
2718
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2719
|
+
|
|
2720
|
+
assert npc_result.error == error_for_condition(opcode)
|
|
2721
|
+
|
|
2722
|
+
@pytest.mark.parametrize(
|
|
2723
|
+
"opcode",
|
|
2724
|
+
[
|
|
2725
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2726
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2727
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2728
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2729
|
+
],
|
|
2730
|
+
)
|
|
2731
|
+
def test_duplicate_large_integer_substr_tail(
|
|
2732
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2733
|
+
) -> None:
|
|
2734
|
+
condition = SINGLE_ARG_INT_SUBSTR_TAIL_COND.format(
|
|
2735
|
+
opcode=opcode.value[0], num=280, val="0xffffffff", filler="0x00"
|
|
2736
|
+
)
|
|
2737
|
+
|
|
2738
|
+
with benchmark_runner.assert_runtime(seconds=1):
|
|
2739
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2740
|
+
|
|
2741
|
+
assert npc_result.error == error_for_condition(opcode)
|
|
2742
|
+
|
|
2743
|
+
@pytest.mark.parametrize(
|
|
2744
|
+
"opcode",
|
|
2745
|
+
[
|
|
2746
|
+
ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE,
|
|
2747
|
+
ConditionOpcode.ASSERT_HEIGHT_RELATIVE,
|
|
2748
|
+
ConditionOpcode.ASSERT_SECONDS_ABSOLUTE,
|
|
2749
|
+
ConditionOpcode.ASSERT_SECONDS_RELATIVE,
|
|
2750
|
+
],
|
|
2751
|
+
)
|
|
2752
|
+
def test_duplicate_large_integer_negative(
|
|
2753
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2754
|
+
) -> None:
|
|
2755
|
+
condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0xff")
|
|
2756
|
+
|
|
2757
|
+
with benchmark_runner.assert_runtime(seconds=2.75):
|
|
2758
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2759
|
+
|
|
2760
|
+
assert npc_result.error is None
|
|
2761
|
+
assert npc_result.conds is not None
|
|
2762
|
+
assert len(npc_result.conds.spends) == 1
|
|
2763
|
+
|
|
2764
|
+
def test_duplicate_reserve_fee(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
|
|
2765
|
+
opcode = ConditionOpcode.RESERVE_FEE
|
|
2766
|
+
condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00")
|
|
2767
|
+
|
|
2768
|
+
with benchmark_runner.assert_runtime(seconds=1.5):
|
|
2769
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2770
|
+
|
|
2771
|
+
assert npc_result.error == error_for_condition(opcode)
|
|
2772
|
+
|
|
2773
|
+
def test_duplicate_reserve_fee_negative(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
|
|
2774
|
+
opcode = ConditionOpcode.RESERVE_FEE
|
|
2775
|
+
condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=200000, val=100, filler="0xff")
|
|
2776
|
+
|
|
2777
|
+
with benchmark_runner.assert_runtime(seconds=1.5):
|
|
2778
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2779
|
+
|
|
2780
|
+
# RESERVE_FEE conditions fail unconditionally if they have a negative
|
|
2781
|
+
# amount
|
|
2782
|
+
assert npc_result.error == Err.RESERVE_FEE_CONDITION_FAILED.value
|
|
2783
|
+
assert npc_result.conds is None
|
|
2784
|
+
|
|
2785
|
+
@pytest.mark.parametrize(
|
|
2786
|
+
"opcode", [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT]
|
|
2787
|
+
)
|
|
2788
|
+
def test_duplicate_coin_announces(
|
|
2789
|
+
self, opcode: ConditionOpcode, softfork_height: uint32, benchmark_runner: BenchmarkRunner
|
|
2790
|
+
) -> None:
|
|
2791
|
+
# we only allow 1024 create- or assert announcements per spend
|
|
2792
|
+
condition = CREATE_ANNOUNCE_COND.format(opcode=opcode.value[0], num=1024)
|
|
2793
|
+
|
|
2794
|
+
with benchmark_runner.assert_runtime(seconds=14):
|
|
2795
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2796
|
+
|
|
2797
|
+
assert npc_result.error is None
|
|
2798
|
+
assert npc_result.conds is not None
|
|
2799
|
+
assert len(npc_result.conds.spends) == 1
|
|
2800
|
+
# coin announcements are not propagated to python, but validated in rust
|
|
2801
|
+
# TODO: optimize clvm to make this run in < 1 second
|
|
2802
|
+
|
|
2803
|
+
def test_create_coin_duplicates(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
|
|
2804
|
+
# CREATE_COIN
|
|
2805
|
+
# this program will emit 6000 identical CREATE_COIN conditions. However,
|
|
2806
|
+
# we'll just end up looking at two of them, and fail at the first
|
|
2807
|
+
# duplicate
|
|
2808
|
+
condition = CREATE_COIN.format(num=600000)
|
|
2809
|
+
|
|
2810
|
+
with benchmark_runner.assert_runtime(seconds=1.5):
|
|
2811
|
+
npc_result = generator_condition_tester(condition, quote=False, height=softfork_height)
|
|
2812
|
+
|
|
2813
|
+
assert npc_result.error == Err.DUPLICATE_OUTPUT.value
|
|
2814
|
+
assert npc_result.conds is None
|
|
2815
|
+
|
|
2816
|
+
def test_many_create_coin(self, softfork_height: uint32, benchmark_runner: BenchmarkRunner) -> None:
|
|
2817
|
+
# CREATE_COIN
|
|
2818
|
+
# this program will emit many CREATE_COIN conditions, all with different
|
|
2819
|
+
# amounts.
|
|
2820
|
+
# the number 6095 was chosen carefully to not exceed the maximum cost
|
|
2821
|
+
condition = CREATE_UNIQUE_COINS.format(num=6094)
|
|
2822
|
+
|
|
2823
|
+
with benchmark_runner.assert_runtime(seconds=0.3):
|
|
2824
|
+
npc_result = generator_condition_tester(
|
|
2825
|
+
condition, quote=False, height=softfork_height, coin_amount=123000000
|
|
2826
|
+
)
|
|
2827
|
+
|
|
2828
|
+
assert npc_result.error is None
|
|
2829
|
+
assert npc_result.conds is not None
|
|
2830
|
+
assert len(npc_result.conds.spends) == 1
|
|
2831
|
+
spend = npc_result.conds.spends[0]
|
|
2832
|
+
assert len(spend.create_coin) == 6094
|
|
2833
|
+
|
|
2834
|
+
@pytest.mark.anyio
|
|
2835
|
+
async def test_invalid_coin_spend_coin(
|
|
2836
|
+
self,
|
|
2837
|
+
one_node_one_block: tuple[FullNodeSimulator, ChiaServer, BlockTools],
|
|
2838
|
+
wallet_a: WalletTool,
|
|
2839
|
+
) -> None:
|
|
2840
|
+
full_node_1, _, bt = one_node_one_block
|
|
2841
|
+
reward_ph = wallet_a.get_new_puzzlehash()
|
|
2842
|
+
blocks = bt.get_consecutive_blocks(
|
|
2843
|
+
5,
|
|
2844
|
+
guarantee_transaction_block=True,
|
|
2845
|
+
farmer_reward_puzzle_hash=reward_ph,
|
|
2846
|
+
pool_reward_puzzle_hash=reward_ph,
|
|
2847
|
+
)
|
|
2848
|
+
|
|
2849
|
+
await add_blocks_in_batches(blocks, full_node_1.full_node)
|
|
2850
|
+
|
|
2851
|
+
await time_out_assert(60, node_height_at_least, True, full_node_1, blocks[-1].height)
|
|
2852
|
+
|
|
2853
|
+
spend_bundle = generate_test_spend_bundle(wallet_a, blocks[-1].get_included_reward_coins()[0])
|
|
2854
|
+
cs = spend_bundle.coin_spends[0]
|
|
2855
|
+
c = cs.coin
|
|
2856
|
+
coin_0 = Coin(c.parent_coin_info, bytes32([1] * 32), c.amount)
|
|
2857
|
+
coin_spend_0 = make_spend(coin_0, cs.puzzle_reveal, cs.solution)
|
|
2858
|
+
new_bundle = recursive_replace(spend_bundle, "coin_spends", [coin_spend_0] + spend_bundle.coin_spends[1:])
|
|
2859
|
+
assert spend_bundle is not None
|
|
2860
|
+
res = await full_node_1.full_node.add_transaction(new_bundle, new_bundle.name(), test=True)
|
|
2861
|
+
assert res == (MempoolInclusionStatus.FAILED, Err.INVALID_SPEND_BUNDLE)
|
|
2862
|
+
|
|
2863
|
+
|
|
2864
|
+
coins = make_test_coins()
|
|
2865
|
+
|
|
2866
|
+
|
|
2867
|
+
# This test makes sure we're properly sorting items by fee rate
|
|
2868
|
+
@pytest.mark.parametrize(
|
|
2869
|
+
"items,expected",
|
|
2870
|
+
[
|
|
2871
|
+
# make sure fractions of fee-rate are ordered correctly (i.e. that
|
|
2872
|
+
# we don't use integer division)
|
|
2873
|
+
(
|
|
2874
|
+
[
|
|
2875
|
+
mk_item(coins[0:1], fee=110, cost=50),
|
|
2876
|
+
mk_item(coins[1:2], fee=100, cost=50),
|
|
2877
|
+
mk_item(coins[2:3], fee=105, cost=50),
|
|
2878
|
+
],
|
|
2879
|
+
[coins[0], coins[2], coins[1]],
|
|
2880
|
+
),
|
|
2881
|
+
# make sure insertion order is a tie-breaker for items with the same
|
|
2882
|
+
# fee-rate
|
|
2883
|
+
(
|
|
2884
|
+
[
|
|
2885
|
+
mk_item(coins[0:1], fee=100, cost=50),
|
|
2886
|
+
mk_item(coins[1:2], fee=100, cost=50),
|
|
2887
|
+
mk_item(coins[2:3], fee=100, cost=50),
|
|
2888
|
+
],
|
|
2889
|
+
[coins[0], coins[1], coins[2]],
|
|
2890
|
+
),
|
|
2891
|
+
# also for items that don't pay fees
|
|
2892
|
+
(
|
|
2893
|
+
[
|
|
2894
|
+
mk_item(coins[2:3], fee=0, cost=50),
|
|
2895
|
+
mk_item(coins[1:2], fee=0, cost=50),
|
|
2896
|
+
mk_item(coins[0:1], fee=0, cost=50),
|
|
2897
|
+
],
|
|
2898
|
+
[coins[2], coins[1], coins[0]],
|
|
2899
|
+
),
|
|
2900
|
+
],
|
|
2901
|
+
)
|
|
2902
|
+
def test_items_by_feerate(items: list[MempoolItem], expected: list[Coin]) -> None:
|
|
2903
|
+
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
|
|
2904
|
+
|
|
2905
|
+
mempool_info = MempoolInfo(
|
|
2906
|
+
CLVMCost(uint64(11000000000 * 3)),
|
|
2907
|
+
FeeRate(uint64(1000000)),
|
|
2908
|
+
CLVMCost(uint64(11000000000)),
|
|
2909
|
+
)
|
|
2910
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
2911
|
+
for i in items:
|
|
2912
|
+
mempool.add_to_pool(i)
|
|
2913
|
+
|
|
2914
|
+
ordered_items = list(mempool.items_by_feerate())
|
|
2915
|
+
|
|
2916
|
+
assert len(ordered_items) == len(expected)
|
|
2917
|
+
|
|
2918
|
+
last_fpc: Optional[float] = None
|
|
2919
|
+
for mi, expected_coin in zip(ordered_items, expected):
|
|
2920
|
+
assert len(mi.spend_bundle.coin_spends) == 1
|
|
2921
|
+
assert mi.spend_bundle.coin_spends[0].coin == expected_coin
|
|
2922
|
+
assert last_fpc is None or last_fpc >= mi.fee_per_cost
|
|
2923
|
+
last_fpc = mi.fee_per_cost
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
def rand_hash() -> bytes32:
|
|
2927
|
+
rng = random.Random()
|
|
2928
|
+
ret = bytearray(32)
|
|
2929
|
+
for i in range(32):
|
|
2930
|
+
ret[i] = rng.getrandbits(8)
|
|
2931
|
+
return bytes32(ret)
|
|
2932
|
+
|
|
2933
|
+
|
|
2934
|
+
def item_cost(cost: int, fee_rate: float) -> MempoolItem:
|
|
2935
|
+
fee = cost * fee_rate
|
|
2936
|
+
amount = uint64(fee + 100)
|
|
2937
|
+
coin = Coin(rand_hash(), rand_hash(), amount)
|
|
2938
|
+
return mk_item([coin], cost=cost, fee=int(cost * fee_rate))
|
|
2939
|
+
|
|
2940
|
+
|
|
2941
|
+
@pytest.mark.parametrize(
|
|
2942
|
+
"items,add,expected",
|
|
2943
|
+
[
|
|
2944
|
+
# the max size is 100
|
|
2945
|
+
# we need to evict two items
|
|
2946
|
+
([50, 25, 13, 12, 5], 10, [10, 50, 25, 13]),
|
|
2947
|
+
# we don't need to evict anything
|
|
2948
|
+
([50, 25, 13], 10, [10, 50, 25, 13]),
|
|
2949
|
+
# we need to evict everything
|
|
2950
|
+
([95, 5], 10, [10]),
|
|
2951
|
+
# we evict a single item
|
|
2952
|
+
([75, 15, 9], 10, [10, 75, 15]),
|
|
2953
|
+
],
|
|
2954
|
+
)
|
|
2955
|
+
def test_full_mempool(items: list[int], add: int, expected: list[int]) -> None:
|
|
2956
|
+
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
|
|
2957
|
+
|
|
2958
|
+
mempool_info = MempoolInfo(
|
|
2959
|
+
CLVMCost(uint64(100)),
|
|
2960
|
+
FeeRate(uint64(1000000)),
|
|
2961
|
+
CLVMCost(uint64(100)),
|
|
2962
|
+
)
|
|
2963
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
2964
|
+
invariant_check_mempool(mempool)
|
|
2965
|
+
fee_rate: float = 3.0
|
|
2966
|
+
for i in items:
|
|
2967
|
+
mempool.add_to_pool(item_cost(i, fee_rate))
|
|
2968
|
+
fee_rate -= 0.1
|
|
2969
|
+
invariant_check_mempool(mempool)
|
|
2970
|
+
|
|
2971
|
+
# now, add the item we're testing
|
|
2972
|
+
mempool.add_to_pool(item_cost(add, 3.1))
|
|
2973
|
+
invariant_check_mempool(mempool)
|
|
2974
|
+
|
|
2975
|
+
ordered_items = list(mempool.items_by_feerate())
|
|
2976
|
+
|
|
2977
|
+
assert len(ordered_items) == len(expected)
|
|
2978
|
+
|
|
2979
|
+
for mi, expected_cost in zip(ordered_items, expected):
|
|
2980
|
+
assert mi.cost == expected_cost
|
|
2981
|
+
|
|
2982
|
+
|
|
2983
|
+
@pytest.mark.parametrize("height", [True, False])
|
|
2984
|
+
@pytest.mark.parametrize(
|
|
2985
|
+
"items,expected,increase_fee",
|
|
2986
|
+
[
|
|
2987
|
+
# the max size is 100
|
|
2988
|
+
# the max block size is 50
|
|
2989
|
+
# which is also the max size for expiring transactions
|
|
2990
|
+
# the increasing fee will order the transactions in the reverse
|
|
2991
|
+
# insertion order
|
|
2992
|
+
([10, 11, 12, 13, 14], [14, 13, 12, 11], True),
|
|
2993
|
+
# decreasing fee rate will make the last one fail to be inserted
|
|
2994
|
+
([10, 11, 12, 13, 14], [10, 11, 12, 13], False),
|
|
2995
|
+
# the last is big enough to evict all previous ones
|
|
2996
|
+
([10, 11, 12, 13, 50], [50], True),
|
|
2997
|
+
# the last one will not evict any earlier ones, because the fee rate is
|
|
2998
|
+
# lower
|
|
2999
|
+
([10, 11, 12, 13, 50], [10, 11, 12, 13], False),
|
|
3000
|
+
],
|
|
3001
|
+
)
|
|
3002
|
+
def test_limit_expiring_transactions(height: bool, items: list[int], expected: list[int], increase_fee: bool) -> None:
|
|
3003
|
+
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
|
|
3004
|
+
|
|
3005
|
+
mempool_info = MempoolInfo(
|
|
3006
|
+
CLVMCost(uint64(100)),
|
|
3007
|
+
FeeRate(uint64(1000000)),
|
|
3008
|
+
CLVMCost(uint64(50)),
|
|
3009
|
+
)
|
|
3010
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
3011
|
+
mempool.new_tx_block(uint32(10), uint64(100000))
|
|
3012
|
+
invariant_check_mempool(mempool)
|
|
3013
|
+
|
|
3014
|
+
# fill the mempool with regular transactions (without expiration)
|
|
3015
|
+
fee_rate: float = 3.0
|
|
3016
|
+
for i in range(1, 20):
|
|
3017
|
+
mempool.add_to_pool(item_cost(i, fee_rate))
|
|
3018
|
+
fee_rate -= 0.1
|
|
3019
|
+
invariant_check_mempool(mempool)
|
|
3020
|
+
|
|
3021
|
+
# now add the expiring transactions from the test case
|
|
3022
|
+
fee_rate = 2.7
|
|
3023
|
+
for cost in items:
|
|
3024
|
+
fee = cost * fee_rate
|
|
3025
|
+
amount = uint64(fee + 100)
|
|
3026
|
+
coin = Coin(rand_hash(), rand_hash(), amount)
|
|
3027
|
+
if height:
|
|
3028
|
+
ret = mempool.add_to_pool(mk_item([coin], cost=cost, fee=int(cost * fee_rate), assert_before_height=15))
|
|
3029
|
+
else:
|
|
3030
|
+
ret = mempool.add_to_pool(mk_item([coin], cost=cost, fee=int(cost * fee_rate), assert_before_seconds=10400))
|
|
3031
|
+
invariant_check_mempool(mempool)
|
|
3032
|
+
if increase_fee:
|
|
3033
|
+
fee_rate += 0.1
|
|
3034
|
+
assert ret.error is None
|
|
3035
|
+
else:
|
|
3036
|
+
fee_rate -= 0.1
|
|
3037
|
+
|
|
3038
|
+
ordered_costs = [
|
|
3039
|
+
item.cost
|
|
3040
|
+
for item in mempool.items_by_feerate()
|
|
3041
|
+
if item.assert_before_height is not None or item.assert_before_seconds is not None
|
|
3042
|
+
]
|
|
3043
|
+
|
|
3044
|
+
assert ordered_costs == expected
|
|
3045
|
+
|
|
3046
|
+
print("")
|
|
3047
|
+
for item in mempool.items_by_feerate():
|
|
3048
|
+
if item.assert_before_seconds is not None or item.assert_before_height is not None:
|
|
3049
|
+
ttl = "yes"
|
|
3050
|
+
else:
|
|
3051
|
+
ttl = "No"
|
|
3052
|
+
print(f"- cost: {item.cost} TTL: {ttl}")
|
|
3053
|
+
|
|
3054
|
+
assert mempool.total_mempool_cost() > 90
|
|
3055
|
+
invariant_check_mempool(mempool)
|
|
3056
|
+
|
|
3057
|
+
|
|
3058
|
+
@pytest.mark.parametrize(
|
|
3059
|
+
"items,coin_ids,expected",
|
|
3060
|
+
[
|
|
3061
|
+
# None of these spend those coins
|
|
3062
|
+
(
|
|
3063
|
+
[mk_item(coins[0:1]), mk_item(coins[1:2]), mk_item(coins[2:3])],
|
|
3064
|
+
[coins[3].name(), coins[4].name()],
|
|
3065
|
+
[],
|
|
3066
|
+
),
|
|
3067
|
+
# One of these spends one of the coins
|
|
3068
|
+
(
|
|
3069
|
+
[mk_item(coins[0:1]), mk_item(coins[1:2]), mk_item(coins[2:3])],
|
|
3070
|
+
[coins[1].name(), coins[3].name()],
|
|
3071
|
+
[mk_item(coins[1:2])],
|
|
3072
|
+
),
|
|
3073
|
+
# One of these spends one another spends two
|
|
3074
|
+
(
|
|
3075
|
+
[mk_item(coins[0:1]), mk_item(coins[1:3]), mk_item(coins[2:4]), mk_item(coins[3:4])],
|
|
3076
|
+
[coins[2].name(), coins[3].name()],
|
|
3077
|
+
[mk_item(coins[1:3]), mk_item(coins[2:4]), mk_item(coins[3:4])],
|
|
3078
|
+
),
|
|
3079
|
+
],
|
|
3080
|
+
)
|
|
3081
|
+
def test_get_items_by_coin_ids(items: list[MempoolItem], coin_ids: list[bytes32], expected: list[MempoolItem]) -> None:
|
|
3082
|
+
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
|
|
3083
|
+
mempool_info = MempoolInfo(
|
|
3084
|
+
CLVMCost(uint64(11000000000 * 3)),
|
|
3085
|
+
FeeRate(uint64(1000000)),
|
|
3086
|
+
CLVMCost(uint64(11000000000)),
|
|
3087
|
+
)
|
|
3088
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
3089
|
+
for i in items:
|
|
3090
|
+
mempool.add_to_pool(i)
|
|
3091
|
+
invariant_check_mempool(mempool)
|
|
3092
|
+
result = mempool.get_items_by_coin_ids(coin_ids)
|
|
3093
|
+
assert set(result) == set(expected)
|
|
3094
|
+
|
|
3095
|
+
|
|
3096
|
+
@pytest.mark.anyio
|
|
3097
|
+
async def test_aggregating_on_a_solution_then_a_more_cost_saving_one_appears() -> None:
|
|
3098
|
+
def always(_: bytes32) -> bool:
|
|
3099
|
+
return True
|
|
3100
|
+
|
|
3101
|
+
async def get_unspent_lineage_info_for_puzzle_hash(_: bytes32) -> Optional[UnspentLineageInfo]:
|
|
3102
|
+
assert False # pragma: no cover
|
|
3103
|
+
|
|
3104
|
+
def make_test_spendbundle(coin: Coin, *, fee: int = 0, with_higher_cost: bool = False) -> SpendBundle:
|
|
3105
|
+
conditions = []
|
|
3106
|
+
actual_fee = fee
|
|
3107
|
+
if with_higher_cost:
|
|
3108
|
+
conditions.extend([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i] for i in range(3)])
|
|
3109
|
+
actual_fee += 3
|
|
3110
|
+
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - actual_fee])
|
|
3111
|
+
sb = spend_bundle_from_conditions(conditions, coin)
|
|
3112
|
+
return sb
|
|
3113
|
+
|
|
3114
|
+
def agg_and_add_sb_returning_cost_info(mempool: Mempool, spend_bundles: list[SpendBundle]) -> uint64:
|
|
3115
|
+
sb = SpendBundle.aggregate(spend_bundles)
|
|
3116
|
+
mi = mempool_item_from_spendbundle(sb)
|
|
3117
|
+
mempool.add_to_pool(mi)
|
|
3118
|
+
invariant_check_mempool(mempool)
|
|
3119
|
+
saved_cost = run_for_cost(
|
|
3120
|
+
sb.coin_spends[0].puzzle_reveal, sb.coin_spends[0].solution, len(mi.additions), mi.cost
|
|
3121
|
+
)
|
|
3122
|
+
return saved_cost
|
|
3123
|
+
|
|
3124
|
+
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
|
|
3125
|
+
mempool_info = MempoolInfo(
|
|
3126
|
+
CLVMCost(uint64(11000000000 * 3)),
|
|
3127
|
+
FeeRate(uint64(1000000)),
|
|
3128
|
+
CLVMCost(uint64(11000000000)),
|
|
3129
|
+
)
|
|
3130
|
+
mempool = Mempool(mempool_info, fee_estimator)
|
|
3131
|
+
coins = [
|
|
3132
|
+
Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount)) for amount in range(2000000000, 2000000020, 2)
|
|
3133
|
+
]
|
|
3134
|
+
# Create a ~10 FPC item that spends the eligible coin[0]
|
|
3135
|
+
sb_A = make_test_spendbundle(coins[0])
|
|
3136
|
+
highest_fee = 58282830
|
|
3137
|
+
sb_high_rate = make_test_spendbundle(coins[1], fee=highest_fee)
|
|
3138
|
+
agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_high_rate])
|
|
3139
|
+
invariant_check_mempool(mempool)
|
|
3140
|
+
# Create a ~2 FPC item that spends the eligible coin using the same solution A
|
|
3141
|
+
sb_low_rate = make_test_spendbundle(coins[2], fee=highest_fee // 5)
|
|
3142
|
+
saved_cost_on_solution_A = agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_low_rate])
|
|
3143
|
+
invariant_check_mempool(mempool)
|
|
3144
|
+
result = await mempool.create_bundle_from_mempool_items(
|
|
3145
|
+
always, get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0)
|
|
3146
|
+
)
|
|
3147
|
+
assert result is not None
|
|
3148
|
+
agg, _ = result
|
|
3149
|
+
# Make sure both items would be processed
|
|
3150
|
+
assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
|
|
3151
|
+
# Now let's add 3 x ~3 FPC items that spend the eligible coin differently
|
|
3152
|
+
# (solution B). It creates a higher (saved) cost than solution A
|
|
3153
|
+
sb_B = make_test_spendbundle(coins[0], with_higher_cost=True)
|
|
3154
|
+
for i in range(3, 6):
|
|
3155
|
+
# We're picking this fee to get a ~3 FPC, and get picked after sb_A1
|
|
3156
|
+
# (which has ~10 FPC) but before sb_A2 (which has ~2 FPC)
|
|
3157
|
+
sb_mid_rate = make_test_spendbundle(coins[i], fee=38004852 - i)
|
|
3158
|
+
saved_cost_on_solution_B = agg_and_add_sb_returning_cost_info(mempool, [sb_B, sb_mid_rate])
|
|
3159
|
+
invariant_check_mempool(mempool)
|
|
3160
|
+
# We'd save more cost if we went with solution B instead of A
|
|
3161
|
+
assert saved_cost_on_solution_B > saved_cost_on_solution_A
|
|
3162
|
+
# If we process everything now, the 3 x ~3 FPC items get skipped because
|
|
3163
|
+
# sb_A1 gets picked before them (~10 FPC), so from then on only sb_A2 (~2 FPC)
|
|
3164
|
+
# would get picked
|
|
3165
|
+
result = await mempool.create_bundle_from_mempool_items(
|
|
3166
|
+
always, get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0)
|
|
3167
|
+
)
|
|
3168
|
+
assert result is not None
|
|
3169
|
+
agg, _ = result
|
|
3170
|
+
# The 3 items got skipped here
|
|
3171
|
+
# We ran with solution A and missed bigger savings on solution B
|
|
3172
|
+
assert mempool.size() == 5
|
|
3173
|
+
assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
|
|
3174
|
+
invariant_check_mempool(mempool)
|
|
3175
|
+
|
|
3176
|
+
|
|
3177
|
+
def test_get_puzzle_and_solution_for_coin_failure() -> None:
|
|
3178
|
+
with pytest.raises(
|
|
3179
|
+
ValueError, match=f"Failed to get puzzle and solution for coin {TEST_COIN}, error: \\('coin not found', '80'\\)"
|
|
3180
|
+
):
|
|
3181
|
+
get_puzzle_and_solution_for_coin(BlockGenerator(SerializedProgram.to(None), []), TEST_COIN, 0, test_constants)
|
|
3182
|
+
|
|
3183
|
+
|
|
3184
|
+
# TODO: import this from chia_rs once we bump the version we depend on
|
|
3185
|
+
ENABLE_KECCAK = 0x200
|
|
3186
|
+
ENABLE_KECCAK_OPS_OUTSIDE_GUARD = 0x100
|
|
3187
|
+
|
|
3188
|
+
|
|
3189
|
+
def test_flags_for_height() -> None:
|
|
3190
|
+
# the keccak operator is supposed to be enabled at soft-fork 6 height
|
|
3191
|
+
flags = get_flags_for_height_and_constants(DEFAULT_CONSTANTS.SOFT_FORK6_HEIGHT, DEFAULT_CONSTANTS)
|
|
3192
|
+
print(f"{flags:x}")
|
|
3193
|
+
assert (flags & ENABLE_KECCAK) != 0
|
|
3194
|
+
|
|
3195
|
+
flags = get_flags_for_height_and_constants(DEFAULT_CONSTANTS.SOFT_FORK6_HEIGHT - 1, DEFAULT_CONSTANTS)
|
|
3196
|
+
print(f"{flags:x}")
|
|
3197
|
+
assert (flags & ENABLE_KECCAK) == 0
|
|
3198
|
+
|
|
3199
|
+
|
|
3200
|
+
def test_keccak() -> None:
|
|
3201
|
+
# the keccak operator is 62. The assemble() function doesn't support it
|
|
3202
|
+
# (yet)
|
|
3203
|
+
|
|
3204
|
+
# keccak256 is available when the softfork has activated
|
|
3205
|
+
keccak_prg = Program.to(
|
|
3206
|
+
assemble(
|
|
3207
|
+
"(softfork (q . 1134) (q . 1) (q a (i "
|
|
3208
|
+
"(= "
|
|
3209
|
+
'(62 (q . "foobar"))'
|
|
3210
|
+
"(q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e))"
|
|
3211
|
+
"(q . 0) (q x)) (q . ())) (q . ()))"
|
|
3212
|
+
)
|
|
3213
|
+
)
|
|
3214
|
+
|
|
3215
|
+
cost, ret = keccak_prg.run_with_flags(1215, ENABLE_KECCAK, [])
|
|
3216
|
+
assert cost == 1215
|
|
3217
|
+
assert ret.atom == b""
|
|
3218
|
+
|
|
3219
|
+
# keccak is ignored when the softfork has not activated
|
|
3220
|
+
cost, ret = keccak_prg.run_with_flags(1215, 0, [])
|
|
3221
|
+
assert cost == 1215
|
|
3222
|
+
assert ret.atom == b""
|
|
3223
|
+
|
|
3224
|
+
# make sure keccak is actually executed, by comparing with the wrong output
|
|
3225
|
+
keccak_prg = Program.to(
|
|
3226
|
+
assemble(
|
|
3227
|
+
"(softfork (q . 1134) (q . 1) (q a (i "
|
|
3228
|
+
'(= (62 (q . "foobar")) '
|
|
3229
|
+
"(q . 0x58d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e))"
|
|
3230
|
+
"(q . 0) (q x)) (q . ())) (q . ()))"
|
|
3231
|
+
)
|
|
3232
|
+
)
|
|
3233
|
+
with pytest.raises(ValueError, match="clvm raise"):
|
|
3234
|
+
keccak_prg.run_with_flags(1215, ENABLE_KECCAK, [])
|
|
3235
|
+
|
|
3236
|
+
# keccak is ignored when the softfork has not activated
|
|
3237
|
+
cost, ret = keccak_prg.run_with_flags(1215, 0, [])
|
|
3238
|
+
assert cost == 1215
|
|
3239
|
+
assert ret.atom == b""
|
|
3240
|
+
|
|
3241
|
+
# === HARD FORK ===
|
|
3242
|
+
# new operators *outside* the softfork guard
|
|
3243
|
+
# keccak256 is available outside the guard with the appropriate flag
|
|
3244
|
+
keccak_prg = Program.to(
|
|
3245
|
+
assemble(
|
|
3246
|
+
"(a (i (= "
|
|
3247
|
+
'(62 (q . "foobar")) '
|
|
3248
|
+
"(q . 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e)) "
|
|
3249
|
+
"(q . 0) (q x)) (q . ()))"
|
|
3250
|
+
)
|
|
3251
|
+
)
|
|
3252
|
+
|
|
3253
|
+
cost, ret = keccak_prg.run_with_flags(994, ENABLE_KECCAK | ENABLE_KECCAK_OPS_OUTSIDE_GUARD, [])
|
|
3254
|
+
assert cost == 994
|
|
3255
|
+
assert ret.atom == b""
|