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,3153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import dataclasses
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import random
|
|
9
|
+
from operator import attrgetter
|
|
10
|
+
from typing import Any, Optional, cast
|
|
11
|
+
from unittest.mock import patch
|
|
12
|
+
|
|
13
|
+
import aiosqlite
|
|
14
|
+
import pytest
|
|
15
|
+
from chia_rs import G1Element, G2Element
|
|
16
|
+
|
|
17
|
+
from chia._tests.environments.wallet import WalletStateTransition, WalletTestFramework
|
|
18
|
+
from chia._tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
|
|
19
|
+
from chia._tests.wallet.test_wallet_coin_store import (
|
|
20
|
+
get_coin_records_amount_filter_tests,
|
|
21
|
+
get_coin_records_amount_range_tests,
|
|
22
|
+
get_coin_records_coin_id_filter_tests,
|
|
23
|
+
get_coin_records_coin_type_tests,
|
|
24
|
+
get_coin_records_confirmed_range_tests,
|
|
25
|
+
get_coin_records_include_total_count_tests,
|
|
26
|
+
get_coin_records_mixed_tests,
|
|
27
|
+
get_coin_records_offset_limit_tests,
|
|
28
|
+
get_coin_records_order_tests,
|
|
29
|
+
get_coin_records_parent_coin_id_filter_tests,
|
|
30
|
+
get_coin_records_puzzle_hash_filter_tests,
|
|
31
|
+
get_coin_records_reverse_tests,
|
|
32
|
+
get_coin_records_spent_range_tests,
|
|
33
|
+
get_coin_records_wallet_id_tests,
|
|
34
|
+
get_coin_records_wallet_type_tests,
|
|
35
|
+
record_1,
|
|
36
|
+
record_2,
|
|
37
|
+
record_3,
|
|
38
|
+
record_4,
|
|
39
|
+
record_5,
|
|
40
|
+
record_6,
|
|
41
|
+
record_7,
|
|
42
|
+
record_8,
|
|
43
|
+
record_9,
|
|
44
|
+
)
|
|
45
|
+
from chia.cmds.coins import CombineCMD, SplitCMD
|
|
46
|
+
from chia.cmds.param_types import CliAmount
|
|
47
|
+
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
|
|
48
|
+
from chia.consensus.coinbase import create_puzzlehash_for_pk
|
|
49
|
+
from chia.rpc.full_node_rpc_client import FullNodeRpcClient
|
|
50
|
+
from chia.rpc.rpc_client import ResponseFailureError
|
|
51
|
+
from chia.rpc.rpc_server import RpcServer
|
|
52
|
+
from chia.rpc.wallet_request_types import (
|
|
53
|
+
AddKey,
|
|
54
|
+
CheckDeleteKey,
|
|
55
|
+
CombineCoins,
|
|
56
|
+
DefaultCAT,
|
|
57
|
+
DeleteKey,
|
|
58
|
+
DIDGetPubkey,
|
|
59
|
+
GetNotifications,
|
|
60
|
+
GetPrivateKey,
|
|
61
|
+
GetSyncStatusResponse,
|
|
62
|
+
GetTimestampForHeight,
|
|
63
|
+
LogIn,
|
|
64
|
+
PushTransactions,
|
|
65
|
+
PushTX,
|
|
66
|
+
SetWalletResyncOnStartup,
|
|
67
|
+
SplitCoins,
|
|
68
|
+
VerifySignature,
|
|
69
|
+
VerifySignatureResponse,
|
|
70
|
+
)
|
|
71
|
+
from chia.rpc.wallet_rpc_api import WalletRpcApi
|
|
72
|
+
from chia.rpc.wallet_rpc_client import WalletRpcClient
|
|
73
|
+
from chia.server.server import ChiaServer
|
|
74
|
+
from chia.server.start_service import Service
|
|
75
|
+
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
76
|
+
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
|
77
|
+
from chia.types.blockchain_format.coin import Coin, coin_as_list
|
|
78
|
+
from chia.types.blockchain_format.program import Program
|
|
79
|
+
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
80
|
+
from chia.types.coin_record import CoinRecord
|
|
81
|
+
from chia.types.coin_spend import CoinSpend, make_spend
|
|
82
|
+
from chia.types.peer_info import PeerInfo
|
|
83
|
+
from chia.types.signing_mode import SigningMode
|
|
84
|
+
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
|
|
85
|
+
from chia.util.config import load_config, lock_and_load_config, save_config
|
|
86
|
+
from chia.util.db_wrapper import DBWrapper2
|
|
87
|
+
from chia.util.hash import std_hash
|
|
88
|
+
from chia.util.ints import uint16, uint32, uint64
|
|
89
|
+
from chia.util.streamable import ConversionError, InvalidTypeError
|
|
90
|
+
from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
|
|
91
|
+
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
|
|
92
|
+
from chia.wallet.cat_wallet.cat_wallet import CATWallet
|
|
93
|
+
from chia.wallet.conditions import (
|
|
94
|
+
ConditionValidTimes,
|
|
95
|
+
CreateCoinAnnouncement,
|
|
96
|
+
CreatePuzzleAnnouncement,
|
|
97
|
+
Remark,
|
|
98
|
+
conditions_to_json_dicts,
|
|
99
|
+
)
|
|
100
|
+
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
|
|
101
|
+
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
|
102
|
+
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
|
103
|
+
from chia.wallet.puzzles.clawback.metadata import AutoClaimSettings
|
|
104
|
+
from chia.wallet.signer_protocol import UnsignedTransaction
|
|
105
|
+
from chia.wallet.trading.trade_status import TradeStatus
|
|
106
|
+
from chia.wallet.transaction_record import TransactionRecord
|
|
107
|
+
from chia.wallet.transaction_sorting import SortKey
|
|
108
|
+
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
|
109
|
+
from chia.wallet.util.address_type import AddressType
|
|
110
|
+
from chia.wallet.util.blind_signer_tl import BLIND_SIGNER_TRANSLATION
|
|
111
|
+
from chia.wallet.util.clvm_streamable import byte_deserialize_clvm_streamable
|
|
112
|
+
from chia.wallet.util.compute_memos import compute_memos
|
|
113
|
+
from chia.wallet.util.query_filter import AmountFilter, HashFilter, TransactionTypeFilter
|
|
114
|
+
from chia.wallet.util.transaction_type import TransactionType
|
|
115
|
+
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
|
|
116
|
+
from chia.wallet.util.wallet_types import CoinType, WalletType
|
|
117
|
+
from chia.wallet.wallet import Wallet
|
|
118
|
+
from chia.wallet.wallet_coin_record import WalletCoinRecord
|
|
119
|
+
from chia.wallet.wallet_coin_store import GetCoinRecords
|
|
120
|
+
from chia.wallet.wallet_node import WalletNode
|
|
121
|
+
from chia.wallet.wallet_protocol import WalletProtocol
|
|
122
|
+
from chia.wallet.wallet_spend_bundle import WalletSpendBundle
|
|
123
|
+
|
|
124
|
+
log = logging.getLogger(__name__)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclasses.dataclass
|
|
128
|
+
class WalletBundle:
|
|
129
|
+
service: Service
|
|
130
|
+
node: WalletNode
|
|
131
|
+
rpc_client: WalletRpcClient
|
|
132
|
+
wallet: Wallet
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclasses.dataclass
|
|
136
|
+
class FullNodeBundle:
|
|
137
|
+
server: ChiaServer
|
|
138
|
+
api: FullNodeSimulator
|
|
139
|
+
rpc_client: FullNodeRpcClient
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclasses.dataclass
|
|
143
|
+
class WalletRpcTestEnvironment:
|
|
144
|
+
wallet_1: WalletBundle
|
|
145
|
+
wallet_2: WalletBundle
|
|
146
|
+
full_node: FullNodeBundle
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def check_client_synced(wallet_client: WalletRpcClient) -> bool:
|
|
150
|
+
return (await wallet_client.get_sync_status()).synced
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
async def farm_transaction_block(full_node_api: FullNodeSimulator, wallet_node: WalletNode):
|
|
154
|
+
await full_node_api.farm_blocks_to_puzzlehash(count=1)
|
|
155
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def check_mempool_spend_count(full_node_api: FullNodeSimulator, num_of_spends):
|
|
159
|
+
return full_node_api.full_node.mempool_manager.mempool.size() == num_of_spends
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
async def farm_transaction(full_node_api: FullNodeSimulator, wallet_node: WalletNode, spend_bundle: WalletSpendBundle):
|
|
163
|
+
await time_out_assert(
|
|
164
|
+
20, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle, spend_bundle.name()
|
|
165
|
+
)
|
|
166
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
167
|
+
assert full_node_api.full_node.mempool_manager.get_spendbundle(spend_bundle.name()) is None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def generate_funds(full_node_api: FullNodeSimulator, wallet_bundle: WalletBundle, num_blocks: int = 1):
|
|
171
|
+
wallet_id = 1
|
|
172
|
+
initial_balances = await wallet_bundle.rpc_client.get_wallet_balance(wallet_id)
|
|
173
|
+
ph: bytes32 = decode_puzzle_hash(await wallet_bundle.rpc_client.get_next_address(wallet_id, True))
|
|
174
|
+
generated_funds = 0
|
|
175
|
+
for i in range(0, num_blocks):
|
|
176
|
+
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
177
|
+
peak_height = full_node_api.full_node.blockchain.get_peak_height()
|
|
178
|
+
assert peak_height is not None
|
|
179
|
+
generated_funds += calculate_pool_reward(peak_height) + calculate_base_farmer_reward(peak_height)
|
|
180
|
+
|
|
181
|
+
# Farm a dummy block to confirm the created funds
|
|
182
|
+
await farm_transaction_block(full_node_api, wallet_bundle.node)
|
|
183
|
+
|
|
184
|
+
expected_confirmed = initial_balances["confirmed_wallet_balance"] + generated_funds
|
|
185
|
+
expected_unconfirmed = initial_balances["unconfirmed_wallet_balance"] + generated_funds
|
|
186
|
+
await time_out_assert(20, get_confirmed_balance, expected_confirmed, wallet_bundle.rpc_client, wallet_id)
|
|
187
|
+
await time_out_assert(20, get_unconfirmed_balance, expected_unconfirmed, wallet_bundle.rpc_client, wallet_id)
|
|
188
|
+
await time_out_assert(20, check_client_synced, True, wallet_bundle.rpc_client)
|
|
189
|
+
|
|
190
|
+
return generated_funds
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@pytest.fixture(scope="function", params=[True, False])
|
|
194
|
+
async def wallet_rpc_environment(two_wallet_nodes_services, request, self_hostname):
|
|
195
|
+
full_node, wallets, bt = two_wallet_nodes_services
|
|
196
|
+
full_node_service = full_node[0]
|
|
197
|
+
full_node_api = full_node_service._api
|
|
198
|
+
full_node_server = full_node_api.full_node.server
|
|
199
|
+
wallet_service = wallets[0]
|
|
200
|
+
wallet_service_2 = wallets[1]
|
|
201
|
+
wallet_node = wallet_service._node
|
|
202
|
+
wallet_node_2 = wallet_service_2._node
|
|
203
|
+
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
204
|
+
wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
205
|
+
|
|
206
|
+
config = bt.config
|
|
207
|
+
hostname = config["self_hostname"]
|
|
208
|
+
|
|
209
|
+
if request.param:
|
|
210
|
+
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
211
|
+
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
212
|
+
else:
|
|
213
|
+
wallet_node.config["trusted_peers"] = {}
|
|
214
|
+
wallet_node_2.config["trusted_peers"] = {}
|
|
215
|
+
|
|
216
|
+
await wallet_node.server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
217
|
+
await wallet_node_2.server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
218
|
+
|
|
219
|
+
async with WalletRpcClient.create_as_context(
|
|
220
|
+
hostname, wallet_service.rpc_server.listen_port, wallet_service.root_path, wallet_service.config
|
|
221
|
+
) as client:
|
|
222
|
+
async with WalletRpcClient.create_as_context(
|
|
223
|
+
hostname, wallet_service_2.rpc_server.listen_port, wallet_service_2.root_path, wallet_service_2.config
|
|
224
|
+
) as client_2:
|
|
225
|
+
async with FullNodeRpcClient.create_as_context(
|
|
226
|
+
hostname,
|
|
227
|
+
full_node_service.rpc_server.listen_port,
|
|
228
|
+
full_node_service.root_path,
|
|
229
|
+
full_node_service.config,
|
|
230
|
+
) as client_node:
|
|
231
|
+
wallet_bundle_1: WalletBundle = WalletBundle(wallet_service, wallet_node, client, wallet)
|
|
232
|
+
wallet_bundle_2: WalletBundle = WalletBundle(wallet_service_2, wallet_node_2, client_2, wallet_2)
|
|
233
|
+
node_bundle: FullNodeBundle = FullNodeBundle(full_node_server, full_node_api, client_node)
|
|
234
|
+
|
|
235
|
+
yield WalletRpcTestEnvironment(wallet_bundle_1, wallet_bundle_2, node_bundle)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
async def create_tx_outputs(wallet: Wallet, output_args: list[tuple[int, Optional[list[str]]]]) -> list[dict[str, Any]]:
|
|
239
|
+
outputs = []
|
|
240
|
+
for args in output_args:
|
|
241
|
+
output = {"amount": uint64(args[0]), "puzzle_hash": await wallet.get_new_puzzlehash()}
|
|
242
|
+
if args[1] is not None:
|
|
243
|
+
assert len(args[1]) > 0
|
|
244
|
+
output["memos"] = args[1]
|
|
245
|
+
outputs.append(output)
|
|
246
|
+
return outputs
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
async def assert_wallet_types(client: WalletRpcClient, expected: dict[WalletType, int]) -> None:
|
|
250
|
+
for wallet_type in WalletType:
|
|
251
|
+
wallets = await client.get_wallets(wallet_type)
|
|
252
|
+
wallet_count = len(wallets)
|
|
253
|
+
if wallet_type in expected:
|
|
254
|
+
assert wallet_count == expected.get(wallet_type, 0)
|
|
255
|
+
for wallet in wallets:
|
|
256
|
+
assert wallet["type"] == wallet_type.value
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def assert_tx_amounts(
|
|
260
|
+
tx: TransactionRecord,
|
|
261
|
+
outputs: list[dict[str, Any]],
|
|
262
|
+
*,
|
|
263
|
+
amount_fee: uint64,
|
|
264
|
+
change_expected: bool,
|
|
265
|
+
is_cat: bool = False,
|
|
266
|
+
) -> None:
|
|
267
|
+
assert tx.fee_amount == amount_fee
|
|
268
|
+
assert tx.amount == sum(output["amount"] for output in outputs)
|
|
269
|
+
expected_additions = len(outputs) + 1 if change_expected else len(outputs)
|
|
270
|
+
assert len(tx.additions) == expected_additions
|
|
271
|
+
addition_amounts = [addition.amount for addition in tx.additions]
|
|
272
|
+
removal_amounts = [removal.amount for removal in tx.removals]
|
|
273
|
+
for output in outputs:
|
|
274
|
+
assert output["amount"] in addition_amounts
|
|
275
|
+
if is_cat:
|
|
276
|
+
assert (sum(removal_amounts) - sum(addition_amounts)) == 0
|
|
277
|
+
else:
|
|
278
|
+
assert (sum(removal_amounts) - sum(addition_amounts)) == amount_fee
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
async def assert_push_tx_error(node_rpc: FullNodeRpcClient, tx: TransactionRecord):
|
|
282
|
+
spend_bundle = tx.spend_bundle
|
|
283
|
+
assert spend_bundle is not None
|
|
284
|
+
# check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error
|
|
285
|
+
try:
|
|
286
|
+
await node_rpc.push_tx(spend_bundle)
|
|
287
|
+
except ValueError as error:
|
|
288
|
+
error_string = error.args[0]["error"]
|
|
289
|
+
if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1:
|
|
290
|
+
raise ValueError from error
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
async def assert_get_balance(rpc_client: WalletRpcClient, wallet_node: WalletNode, wallet: WalletProtocol) -> None:
|
|
294
|
+
expected_balance = await wallet_node.get_balance(wallet.id())
|
|
295
|
+
expected_balance_dict = expected_balance.to_json_dict()
|
|
296
|
+
expected_balance_dict["wallet_id"] = wallet.id()
|
|
297
|
+
expected_balance_dict["wallet_type"] = wallet.type()
|
|
298
|
+
expected_balance_dict["fingerprint"] = wallet_node.logged_in_fingerprint
|
|
299
|
+
if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
|
|
300
|
+
assert isinstance(wallet, CATWallet)
|
|
301
|
+
expected_balance_dict["asset_id"] = wallet.get_asset_id()
|
|
302
|
+
assert await rpc_client.get_wallet_balance(wallet.id()) == expected_balance_dict
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
async def tx_in_mempool(client: WalletRpcClient, transaction_id: bytes32):
|
|
306
|
+
tx = await client.get_transaction(transaction_id)
|
|
307
|
+
return tx.is_in_mempool()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
async def get_confirmed_balance(client: WalletRpcClient, wallet_id: int):
|
|
311
|
+
return (await client.get_wallet_balance(wallet_id))["confirmed_wallet_balance"]
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
async def get_unconfirmed_balance(client: WalletRpcClient, wallet_id: int):
|
|
315
|
+
return (await client.get_wallet_balance(wallet_id))["unconfirmed_wallet_balance"]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@pytest.mark.anyio
|
|
319
|
+
async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
320
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
321
|
+
|
|
322
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
323
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
324
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
325
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
326
|
+
|
|
327
|
+
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
328
|
+
|
|
329
|
+
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
330
|
+
tx_amount = uint64(15600000)
|
|
331
|
+
with pytest.raises(ValueError):
|
|
332
|
+
await client.send_transaction(1, uint64(100000000000000001), addr, DEFAULT_TX_CONFIG)
|
|
333
|
+
|
|
334
|
+
# Tests sending a basic transaction
|
|
335
|
+
extra_conditions = (Remark(Program.to(("test", None))),)
|
|
336
|
+
non_existent_coin = Coin(bytes32.zeros, bytes32.zeros, uint64(0))
|
|
337
|
+
tx_no_push = (
|
|
338
|
+
await client.send_transaction(
|
|
339
|
+
1,
|
|
340
|
+
tx_amount,
|
|
341
|
+
addr,
|
|
342
|
+
memos=["this is a basic tx"],
|
|
343
|
+
tx_config=DEFAULT_TX_CONFIG.override(
|
|
344
|
+
excluded_coin_amounts=[uint64(250000000000)],
|
|
345
|
+
excluded_coin_ids=[non_existent_coin.name()],
|
|
346
|
+
reuse_puzhash=True,
|
|
347
|
+
),
|
|
348
|
+
extra_conditions=extra_conditions,
|
|
349
|
+
push=False,
|
|
350
|
+
)
|
|
351
|
+
).transaction
|
|
352
|
+
response = await client.fetch(
|
|
353
|
+
"send_transaction",
|
|
354
|
+
{
|
|
355
|
+
"wallet_id": 1,
|
|
356
|
+
"amount": tx_amount,
|
|
357
|
+
"address": addr,
|
|
358
|
+
"fee": 0,
|
|
359
|
+
"memos": ["this is a basic tx"],
|
|
360
|
+
"puzzle_decorator": None,
|
|
361
|
+
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
362
|
+
"exclude_coin_amounts": [250000000000],
|
|
363
|
+
"exclude_coins": [non_existent_coin.to_json_dict()],
|
|
364
|
+
"reuse_puzhash": True,
|
|
365
|
+
"CHIP-0029": True,
|
|
366
|
+
"translation": "CHIP-0028",
|
|
367
|
+
"push": True,
|
|
368
|
+
},
|
|
369
|
+
)
|
|
370
|
+
assert response["success"]
|
|
371
|
+
tx = TransactionRecord.from_json_dict_convenience(response["transactions"][0])
|
|
372
|
+
[
|
|
373
|
+
byte_deserialize_clvm_streamable(
|
|
374
|
+
bytes.fromhex(utx), UnsignedTransaction, translation_layer=BLIND_SIGNER_TRANSLATION
|
|
375
|
+
)
|
|
376
|
+
for utx in response["unsigned_transactions"]
|
|
377
|
+
]
|
|
378
|
+
assert tx == dataclasses.replace(tx_no_push, created_at_time=tx.created_at_time)
|
|
379
|
+
transaction_id = tx.name
|
|
380
|
+
spend_bundle = tx.spend_bundle
|
|
381
|
+
assert spend_bundle is not None
|
|
382
|
+
|
|
383
|
+
await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
|
|
384
|
+
await time_out_assert(20, get_unconfirmed_balance, generated_funds - tx_amount, client, 1)
|
|
385
|
+
|
|
386
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
387
|
+
|
|
388
|
+
# Checks that the memo can be retrieved
|
|
389
|
+
tx_confirmed = await client.get_transaction(transaction_id)
|
|
390
|
+
assert tx_confirmed.confirmed
|
|
391
|
+
assert len(tx_confirmed.get_memos()) == 1
|
|
392
|
+
assert [b"this is a basic tx"] in tx_confirmed.get_memos().values()
|
|
393
|
+
assert next(iter(tx_confirmed.get_memos().keys())) in [a.name() for a in spend_bundle.additions()]
|
|
394
|
+
|
|
395
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds - tx_amount, client, 1)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@pytest.mark.anyio
|
|
399
|
+
async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
400
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
401
|
+
|
|
402
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
403
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
404
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
405
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
406
|
+
|
|
407
|
+
await generate_funds(full_node_api, env.wallet_1, num_blocks=2)
|
|
408
|
+
|
|
409
|
+
outputs = await create_tx_outputs(wallet, [(1234321, None)])
|
|
410
|
+
|
|
411
|
+
tx = (
|
|
412
|
+
await client.create_signed_transactions(
|
|
413
|
+
outputs,
|
|
414
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
415
|
+
fee=uint64(100),
|
|
416
|
+
)
|
|
417
|
+
).signed_tx
|
|
418
|
+
|
|
419
|
+
resp_client = await client.push_transactions(
|
|
420
|
+
PushTransactions(transactions=[tx], fee=uint64(10)),
|
|
421
|
+
DEFAULT_TX_CONFIG,
|
|
422
|
+
)
|
|
423
|
+
resp = await client.fetch(
|
|
424
|
+
"push_transactions", {"transactions": [tx.to_json_dict_convenience(wallet_node.config)], "fee": 10}
|
|
425
|
+
)
|
|
426
|
+
assert resp["success"]
|
|
427
|
+
resp = await client.fetch("push_transactions", {"transactions": [bytes(tx).hex()], "fee": 10})
|
|
428
|
+
assert resp["success"]
|
|
429
|
+
|
|
430
|
+
spend_bundle = WalletSpendBundle.aggregate(
|
|
431
|
+
[tx.spend_bundle for tx in resp_client.transactions if tx.spend_bundle is not None]
|
|
432
|
+
)
|
|
433
|
+
assert spend_bundle is not None
|
|
434
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
435
|
+
|
|
436
|
+
for tx in resp_client.transactions:
|
|
437
|
+
assert (await client.get_transaction(transaction_id=tx.name)).confirmed
|
|
438
|
+
|
|
439
|
+
# Just testing NOT failure here really (parsing)
|
|
440
|
+
await client.push_tx(PushTX(spend_bundle))
|
|
441
|
+
resp = await client.fetch("push_tx", {"spend_bundle": bytes(spend_bundle).hex()})
|
|
442
|
+
assert resp["success"]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@pytest.mark.anyio
|
|
446
|
+
async def test_get_balance(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
447
|
+
env = wallet_rpc_environment
|
|
448
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
449
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
450
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
451
|
+
wallet_rpc_client = env.wallet_1.rpc_client
|
|
452
|
+
await full_node_api.farm_blocks_to_wallet(2, wallet)
|
|
453
|
+
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
454
|
+
cat_wallet = await CATWallet.create_new_cat_wallet(
|
|
455
|
+
wallet_node.wallet_state_manager,
|
|
456
|
+
wallet,
|
|
457
|
+
{"identifier": "genesis_by_id"},
|
|
458
|
+
uint64(100),
|
|
459
|
+
action_scope,
|
|
460
|
+
)
|
|
461
|
+
await full_node_api.wait_transaction_records_entered_mempool(action_scope.side_effects.transactions)
|
|
462
|
+
await full_node_api.wait_for_wallet_synced(wallet_node)
|
|
463
|
+
await assert_get_balance(wallet_rpc_client, wallet_node, wallet)
|
|
464
|
+
await assert_get_balance(wallet_rpc_client, wallet_node, cat_wallet)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@pytest.mark.anyio
|
|
468
|
+
async def test_get_farmed_amount(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
469
|
+
env = wallet_rpc_environment
|
|
470
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
471
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
472
|
+
wallet_rpc_client = env.wallet_1.rpc_client
|
|
473
|
+
await full_node_api.farm_blocks_to_wallet(2, wallet)
|
|
474
|
+
|
|
475
|
+
get_farmed_amount_result = await wallet_rpc_client.get_farmed_amount()
|
|
476
|
+
get_timestamp_for_height_result = await wallet_rpc_client.get_timestamp_for_height(
|
|
477
|
+
GetTimestampForHeight(uint32(3))
|
|
478
|
+
) # genesis + 2
|
|
479
|
+
|
|
480
|
+
expected_result = {
|
|
481
|
+
"blocks_won": 2,
|
|
482
|
+
"farmed_amount": 4_000_000_000_000,
|
|
483
|
+
"farmer_reward_amount": 500_000_000_000,
|
|
484
|
+
"fee_amount": 0,
|
|
485
|
+
"last_height_farmed": 3,
|
|
486
|
+
"last_time_farmed": get_timestamp_for_height_result.timestamp,
|
|
487
|
+
"pool_reward_amount": 3_500_000_000_000,
|
|
488
|
+
"success": True,
|
|
489
|
+
}
|
|
490
|
+
assert get_farmed_amount_result == expected_result
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
@pytest.mark.anyio
|
|
494
|
+
async def test_get_farmed_amount_with_fee(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
495
|
+
env = wallet_rpc_environment
|
|
496
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
497
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
498
|
+
wallet_rpc_client = env.wallet_1.rpc_client
|
|
499
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
500
|
+
|
|
501
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
502
|
+
|
|
503
|
+
fee_amount = 100
|
|
504
|
+
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
|
|
505
|
+
await wallet.generate_signed_transaction(
|
|
506
|
+
amount=uint64(5),
|
|
507
|
+
puzzle_hash=bytes32.zeros,
|
|
508
|
+
action_scope=action_scope,
|
|
509
|
+
fee=uint64(fee_amount),
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
our_ph = await wallet.get_new_puzzlehash()
|
|
513
|
+
await full_node_api.wait_transaction_records_entered_mempool(records=action_scope.side_effects.transactions)
|
|
514
|
+
await full_node_api.farm_blocks_to_puzzlehash(count=2, farm_to=our_ph, guarantee_transaction_blocks=True)
|
|
515
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
516
|
+
|
|
517
|
+
result = await wallet_rpc_client.get_farmed_amount()
|
|
518
|
+
assert result["fee_amount"] == fee_amount
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@pytest.mark.anyio
|
|
522
|
+
async def test_get_timestamp_for_height(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
523
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
524
|
+
|
|
525
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
526
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
527
|
+
|
|
528
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
529
|
+
|
|
530
|
+
# This tests that the client returns successfully, rather than raising or returning something unexpected
|
|
531
|
+
await client.get_timestamp_for_height(GetTimestampForHeight(uint32(1)))
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@pytest.mark.parametrize(
|
|
535
|
+
"output_args, fee, select_coin, is_cat",
|
|
536
|
+
[
|
|
537
|
+
([(348026, None)], 0, False, False),
|
|
538
|
+
([(1270495230, ["memo_1"]), (902347, ["memo_2"])], 1, True, False),
|
|
539
|
+
([(84920, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"])], 0, False, False),
|
|
540
|
+
(
|
|
541
|
+
[(32058710, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"]), (923, ["memo_3_0", "memo_3_1"])],
|
|
542
|
+
32804,
|
|
543
|
+
True,
|
|
544
|
+
False,
|
|
545
|
+
),
|
|
546
|
+
([(1337, ["LEET"]), (81000, ["pingwei"])], 817, False, True),
|
|
547
|
+
([(120000000000, None), (120000000000, None)], 10000000000, True, False),
|
|
548
|
+
],
|
|
549
|
+
)
|
|
550
|
+
@pytest.mark.anyio
|
|
551
|
+
async def test_create_signed_transaction(
|
|
552
|
+
wallet_rpc_environment: WalletRpcTestEnvironment,
|
|
553
|
+
output_args: list[tuple[int, Optional[list[str]]]],
|
|
554
|
+
fee: int,
|
|
555
|
+
select_coin: bool,
|
|
556
|
+
is_cat: bool,
|
|
557
|
+
):
|
|
558
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
559
|
+
|
|
560
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
561
|
+
wallet_1_node: WalletNode = env.wallet_1.node
|
|
562
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
563
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
564
|
+
full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
|
|
565
|
+
|
|
566
|
+
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
567
|
+
|
|
568
|
+
wallet_id = 1
|
|
569
|
+
if is_cat:
|
|
570
|
+
generated_funds = 10**9
|
|
571
|
+
|
|
572
|
+
res = await wallet_1_rpc.create_new_cat_and_wallet(uint64(generated_funds), test=True)
|
|
573
|
+
assert res["success"]
|
|
574
|
+
wallet_id = res["wallet_id"]
|
|
575
|
+
|
|
576
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
577
|
+
for i in range(5):
|
|
578
|
+
if check_mempool_spend_count(full_node_api, 0):
|
|
579
|
+
break
|
|
580
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
581
|
+
|
|
582
|
+
outputs = await create_tx_outputs(wallet_2, output_args)
|
|
583
|
+
amount_outputs = sum(output["amount"] for output in outputs)
|
|
584
|
+
amount_fee = uint64(fee)
|
|
585
|
+
|
|
586
|
+
if is_cat:
|
|
587
|
+
amount_total = amount_outputs
|
|
588
|
+
else:
|
|
589
|
+
amount_total = amount_outputs + amount_fee
|
|
590
|
+
|
|
591
|
+
selected_coin = None
|
|
592
|
+
if select_coin:
|
|
593
|
+
selected_coin = await wallet_1_rpc.select_coins(
|
|
594
|
+
amount=amount_total, wallet_id=wallet_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
595
|
+
)
|
|
596
|
+
assert len(selected_coin) == 1
|
|
597
|
+
|
|
598
|
+
txs = (
|
|
599
|
+
await wallet_1_rpc.create_signed_transactions(
|
|
600
|
+
outputs,
|
|
601
|
+
coins=selected_coin,
|
|
602
|
+
fee=amount_fee,
|
|
603
|
+
wallet_id=wallet_id,
|
|
604
|
+
# shouldn't actually block it
|
|
605
|
+
tx_config=DEFAULT_TX_CONFIG.override(
|
|
606
|
+
excluded_coin_amounts=[uint64(selected_coin[0].amount)] if selected_coin is not None else [],
|
|
607
|
+
),
|
|
608
|
+
push=True,
|
|
609
|
+
)
|
|
610
|
+
).transactions
|
|
611
|
+
change_expected = not selected_coin or selected_coin[0].amount - amount_total > 0
|
|
612
|
+
assert_tx_amounts(txs[-1], outputs, amount_fee=amount_fee, change_expected=change_expected, is_cat=is_cat)
|
|
613
|
+
|
|
614
|
+
# Farm the transaction and make sure the wallet balance reflects it correct
|
|
615
|
+
spend_bundle = txs[0].spend_bundle
|
|
616
|
+
assert spend_bundle is not None
|
|
617
|
+
await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
|
|
618
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds - amount_total, wallet_1_rpc, wallet_id)
|
|
619
|
+
|
|
620
|
+
# Assert every coin comes from the same parent
|
|
621
|
+
additions: list[Coin] = spend_bundle.additions()
|
|
622
|
+
assert len({c.parent_coin_info for c in additions}) == 2 if is_cat else 1
|
|
623
|
+
|
|
624
|
+
# Assert you can get the spend for each addition
|
|
625
|
+
for addition in additions:
|
|
626
|
+
cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
|
|
627
|
+
assert cr is not None
|
|
628
|
+
spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
|
|
629
|
+
addition.parent_coin_info, cr.confirmed_block_index
|
|
630
|
+
)
|
|
631
|
+
assert spend is not None
|
|
632
|
+
|
|
633
|
+
# Assert the memos are all correct
|
|
634
|
+
addition_dict: dict[bytes32, Coin] = {addition.name(): addition for addition in additions}
|
|
635
|
+
memo_dictionary: dict[bytes32, list[bytes]] = compute_memos(spend_bundle)
|
|
636
|
+
for output in outputs:
|
|
637
|
+
if "memos" in output:
|
|
638
|
+
found: bool = False
|
|
639
|
+
for addition_id, addition in addition_dict.items():
|
|
640
|
+
if (
|
|
641
|
+
is_cat
|
|
642
|
+
and addition.amount == output["amount"]
|
|
643
|
+
and memo_dictionary[addition_id][0] == output["puzzle_hash"]
|
|
644
|
+
and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output["memos"]]
|
|
645
|
+
) or (
|
|
646
|
+
addition.amount == output["amount"]
|
|
647
|
+
and addition.puzzle_hash == output["puzzle_hash"]
|
|
648
|
+
and memo_dictionary[addition_id] == [memo.encode() for memo in output["memos"]]
|
|
649
|
+
):
|
|
650
|
+
found = True
|
|
651
|
+
assert found
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
@pytest.mark.anyio
|
|
655
|
+
async def test_create_signed_transaction_with_coin_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
656
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
657
|
+
|
|
658
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
659
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
660
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
661
|
+
client_node: FullNodeRpcClient = env.full_node.rpc_client
|
|
662
|
+
|
|
663
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
664
|
+
|
|
665
|
+
signed_tx_amount = uint64(888000)
|
|
666
|
+
tx_coin_announcements = [
|
|
667
|
+
CreateCoinAnnouncement(
|
|
668
|
+
std_hash(b"\xca" + std_hash(b"message")),
|
|
669
|
+
std_hash(b"coin_id_1"),
|
|
670
|
+
),
|
|
671
|
+
CreateCoinAnnouncement(
|
|
672
|
+
bytes(Program.to("a string")),
|
|
673
|
+
std_hash(b"coin_id_2"),
|
|
674
|
+
),
|
|
675
|
+
]
|
|
676
|
+
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
|
|
677
|
+
tx_res: TransactionRecord = (
|
|
678
|
+
await client.create_signed_transactions(
|
|
679
|
+
outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_coin_announcements,)
|
|
680
|
+
)
|
|
681
|
+
).signed_tx
|
|
682
|
+
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
|
|
683
|
+
await assert_push_tx_error(client_node, tx_res)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
@pytest.mark.anyio
|
|
687
|
+
async def test_create_signed_transaction_with_puzzle_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
688
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
689
|
+
|
|
690
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
691
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
692
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
693
|
+
client_node: FullNodeRpcClient = env.full_node.rpc_client
|
|
694
|
+
|
|
695
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
696
|
+
|
|
697
|
+
signed_tx_amount = uint64(888000)
|
|
698
|
+
tx_puzzle_announcements = [
|
|
699
|
+
CreatePuzzleAnnouncement(
|
|
700
|
+
std_hash(b"\xca" + std_hash(b"message")),
|
|
701
|
+
std_hash(b"puzzle_hash_1"),
|
|
702
|
+
),
|
|
703
|
+
CreatePuzzleAnnouncement(
|
|
704
|
+
bytes(Program.to("a string")),
|
|
705
|
+
std_hash(b"puzzle_hash_2"),
|
|
706
|
+
),
|
|
707
|
+
]
|
|
708
|
+
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
|
|
709
|
+
tx_res = (
|
|
710
|
+
await client.create_signed_transactions(
|
|
711
|
+
outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_puzzle_announcements,)
|
|
712
|
+
)
|
|
713
|
+
).signed_tx
|
|
714
|
+
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
|
|
715
|
+
await assert_push_tx_error(client_node, tx_res)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
@pytest.mark.anyio
|
|
719
|
+
async def test_create_signed_transaction_with_excluded_coins(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
|
|
720
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
721
|
+
wallet_1: Wallet = env.wallet_1.wallet
|
|
722
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
723
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
724
|
+
full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
|
|
725
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
726
|
+
|
|
727
|
+
async def it_does_not_include_the_excluded_coins() -> None:
|
|
728
|
+
selected_coins = await wallet_1_rpc.select_coins(
|
|
729
|
+
amount=250000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
730
|
+
)
|
|
731
|
+
assert len(selected_coins) == 1
|
|
732
|
+
outputs = await create_tx_outputs(wallet_1, [(uint64(250000000000), None)])
|
|
733
|
+
|
|
734
|
+
tx = (
|
|
735
|
+
await wallet_1_rpc.create_signed_transactions(
|
|
736
|
+
outputs,
|
|
737
|
+
DEFAULT_TX_CONFIG.override(
|
|
738
|
+
excluded_coin_ids=[c.name() for c in selected_coins],
|
|
739
|
+
),
|
|
740
|
+
)
|
|
741
|
+
).signed_tx
|
|
742
|
+
|
|
743
|
+
assert len(tx.removals) == 1
|
|
744
|
+
assert tx.removals[0] != selected_coins[0]
|
|
745
|
+
assert tx.removals[0].amount == uint64(1750000000000)
|
|
746
|
+
await assert_push_tx_error(full_node_rpc, tx)
|
|
747
|
+
|
|
748
|
+
async def it_throws_an_error_when_all_spendable_coins_are_excluded() -> None:
|
|
749
|
+
selected_coins = await wallet_1_rpc.select_coins(
|
|
750
|
+
amount=1750000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
751
|
+
)
|
|
752
|
+
assert len(selected_coins) == 1
|
|
753
|
+
outputs = await create_tx_outputs(wallet_1, [(uint64(1750000000000), None)])
|
|
754
|
+
|
|
755
|
+
with pytest.raises(ValueError):
|
|
756
|
+
await wallet_1_rpc.create_signed_transactions(
|
|
757
|
+
outputs,
|
|
758
|
+
DEFAULT_TX_CONFIG.override(
|
|
759
|
+
excluded_coin_ids=[c.name() for c in selected_coins],
|
|
760
|
+
),
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
await it_does_not_include_the_excluded_coins()
|
|
764
|
+
await it_throws_an_error_when_all_spendable_coins_are_excluded()
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
@pytest.mark.anyio
|
|
768
|
+
async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
769
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
770
|
+
|
|
771
|
+
wallet_1_node: WalletNode = env.wallet_1.node
|
|
772
|
+
wallet_2_node: WalletNode = env.wallet_2.node
|
|
773
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
774
|
+
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
775
|
+
wallet_1 = wallet_1_node.wallet_state_manager.main_wallet
|
|
776
|
+
wallet_2 = wallet_2_node.wallet_state_manager.main_wallet
|
|
777
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
778
|
+
wallet_2_api = WalletRpcApi(wallet_2_node)
|
|
779
|
+
|
|
780
|
+
generated_funds = await generate_funds(full_node_api, env.wallet_1, 1)
|
|
781
|
+
await generate_funds(full_node_api, env.wallet_2, 1)
|
|
782
|
+
wallet_1_puzhash = await wallet_1.get_new_puzzlehash()
|
|
783
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=20)
|
|
784
|
+
wallet_2_puzhash = await wallet_2.get_new_puzzlehash()
|
|
785
|
+
tx = (
|
|
786
|
+
await wallet_1_rpc.send_transaction(
|
|
787
|
+
wallet_id=1,
|
|
788
|
+
amount=uint64(500),
|
|
789
|
+
address=encode_puzzle_hash(wallet_2_puzhash, "txch"),
|
|
790
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
791
|
+
fee=uint64(0),
|
|
792
|
+
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
|
|
793
|
+
)
|
|
794
|
+
).transaction
|
|
795
|
+
clawback_coin_id_1 = tx.additions[0].name()
|
|
796
|
+
assert tx.spend_bundle is not None
|
|
797
|
+
await farm_transaction(full_node_api, wallet_1_node, tx.spend_bundle)
|
|
798
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=20)
|
|
799
|
+
tx = (
|
|
800
|
+
await wallet_2_rpc.send_transaction(
|
|
801
|
+
wallet_id=1,
|
|
802
|
+
amount=uint64(500),
|
|
803
|
+
address=encode_puzzle_hash(wallet_1_puzhash, "txch"),
|
|
804
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
805
|
+
fee=uint64(0),
|
|
806
|
+
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
|
|
807
|
+
)
|
|
808
|
+
).transaction
|
|
809
|
+
assert tx.spend_bundle is not None
|
|
810
|
+
clawback_coin_id_2 = tx.additions[0].name()
|
|
811
|
+
await farm_transaction(full_node_api, wallet_2_node, tx.spend_bundle)
|
|
812
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds - 500, wallet_1_rpc, 1)
|
|
813
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds - 500, wallet_2_rpc, 1)
|
|
814
|
+
await asyncio.sleep(10)
|
|
815
|
+
# Test missing coin_ids
|
|
816
|
+
has_exception = False
|
|
817
|
+
try:
|
|
818
|
+
await wallet_2_api.spend_clawback_coins({})
|
|
819
|
+
except ValueError:
|
|
820
|
+
has_exception = True
|
|
821
|
+
assert has_exception
|
|
822
|
+
# Test coin ID is not a Clawback coin
|
|
823
|
+
invalid_coin_id = tx.removals[0].name()
|
|
824
|
+
resp = await wallet_2_rpc.spend_clawback_coins([invalid_coin_id], 500)
|
|
825
|
+
assert resp["success"]
|
|
826
|
+
assert resp["transaction_ids"] == []
|
|
827
|
+
# Test unsupported wallet
|
|
828
|
+
coin_record = await wallet_1_node.wallet_state_manager.coin_store.get_coin_record(clawback_coin_id_1)
|
|
829
|
+
assert coin_record is not None
|
|
830
|
+
await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
|
|
831
|
+
dataclasses.replace(coin_record, wallet_type=WalletType.CAT)
|
|
832
|
+
)
|
|
833
|
+
resp = await wallet_1_rpc.spend_clawback_coins([clawback_coin_id_1], 100)
|
|
834
|
+
assert resp["success"]
|
|
835
|
+
assert len(resp["transaction_ids"]) == 0
|
|
836
|
+
# Test missing metadata
|
|
837
|
+
await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(dataclasses.replace(coin_record, metadata=None))
|
|
838
|
+
resp = await wallet_1_rpc.spend_clawback_coins([clawback_coin_id_1], 100)
|
|
839
|
+
assert resp["success"]
|
|
840
|
+
assert len(resp["transaction_ids"]) == 0
|
|
841
|
+
# Test missing incoming tx
|
|
842
|
+
coin_record = await wallet_1_node.wallet_state_manager.coin_store.get_coin_record(clawback_coin_id_2)
|
|
843
|
+
assert coin_record is not None
|
|
844
|
+
fake_coin = Coin(coin_record.coin.parent_coin_info, wallet_2_puzhash, coin_record.coin.amount)
|
|
845
|
+
await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
|
|
846
|
+
dataclasses.replace(coin_record, coin=fake_coin)
|
|
847
|
+
)
|
|
848
|
+
resp = await wallet_1_rpc.spend_clawback_coins([fake_coin.name()], 100)
|
|
849
|
+
assert resp["transaction_ids"] == []
|
|
850
|
+
# Test coin puzzle hash doesn't match the puzzle
|
|
851
|
+
farmed_tx = (await wallet_1.wallet_state_manager.tx_store.get_farming_rewards())[0]
|
|
852
|
+
await wallet_1.wallet_state_manager.tx_store.add_transaction_record(
|
|
853
|
+
dataclasses.replace(farmed_tx, name=fake_coin.name())
|
|
854
|
+
)
|
|
855
|
+
await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
|
|
856
|
+
dataclasses.replace(coin_record, coin=fake_coin)
|
|
857
|
+
)
|
|
858
|
+
resp = await wallet_1_rpc.spend_clawback_coins([fake_coin.name()], 100)
|
|
859
|
+
assert resp["transaction_ids"] == []
|
|
860
|
+
# Test claim spend
|
|
861
|
+
await wallet_2_rpc.set_auto_claim(
|
|
862
|
+
AutoClaimSettings(
|
|
863
|
+
enabled=False,
|
|
864
|
+
tx_fee=uint64(100),
|
|
865
|
+
min_amount=uint64(0),
|
|
866
|
+
batch_size=uint16(1),
|
|
867
|
+
)
|
|
868
|
+
)
|
|
869
|
+
resp = await wallet_2_rpc.spend_clawback_coins([clawback_coin_id_1, clawback_coin_id_2], 100)
|
|
870
|
+
assert resp["success"]
|
|
871
|
+
assert len(resp["transaction_ids"]) == 2
|
|
872
|
+
for _tx in resp["transactions"]:
|
|
873
|
+
clawback_tx = TransactionRecord.from_json_dict_convenience(_tx)
|
|
874
|
+
if clawback_tx.spend_bundle is not None:
|
|
875
|
+
await time_out_assert_not_none(
|
|
876
|
+
10, full_node_api.full_node.mempool_manager.get_spendbundle, clawback_tx.spend_bundle.name()
|
|
877
|
+
)
|
|
878
|
+
await farm_transaction_block(full_node_api, wallet_2_node)
|
|
879
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds + 300, wallet_2_rpc, 1)
|
|
880
|
+
# Test spent coin
|
|
881
|
+
resp = await wallet_2_rpc.spend_clawback_coins([clawback_coin_id_1], 500)
|
|
882
|
+
assert resp["success"]
|
|
883
|
+
assert resp["transaction_ids"] == []
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
@pytest.mark.anyio
|
|
887
|
+
async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
888
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
889
|
+
|
|
890
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
891
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
892
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
893
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
894
|
+
|
|
895
|
+
generated_funds = await generate_funds(full_node_api, env.wallet_1)
|
|
896
|
+
|
|
897
|
+
removals = await client.select_coins(
|
|
898
|
+
1750000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
899
|
+
) # we want a coin that won't be selected by default
|
|
900
|
+
outputs = await create_tx_outputs(wallet_2, [(uint64(1), ["memo_1"]), (uint64(2), ["memo_2"])])
|
|
901
|
+
amount_outputs = sum(output["amount"] for output in outputs)
|
|
902
|
+
amount_fee = uint64(amount_outputs + 1)
|
|
903
|
+
|
|
904
|
+
send_tx_res: TransactionRecord = (
|
|
905
|
+
await client.send_transaction_multi(
|
|
906
|
+
1,
|
|
907
|
+
outputs,
|
|
908
|
+
DEFAULT_TX_CONFIG,
|
|
909
|
+
coins=removals,
|
|
910
|
+
fee=amount_fee,
|
|
911
|
+
)
|
|
912
|
+
).transaction
|
|
913
|
+
spend_bundle = send_tx_res.spend_bundle
|
|
914
|
+
assert spend_bundle is not None
|
|
915
|
+
assert send_tx_res is not None
|
|
916
|
+
|
|
917
|
+
assert_tx_amounts(send_tx_res, outputs, amount_fee=amount_fee, change_expected=True)
|
|
918
|
+
assert send_tx_res.removals == removals
|
|
919
|
+
|
|
920
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
921
|
+
|
|
922
|
+
await time_out_assert(20, get_confirmed_balance, generated_funds - amount_outputs - amount_fee, client, 1)
|
|
923
|
+
|
|
924
|
+
# Checks that the memo can be retrieved
|
|
925
|
+
tx_confirmed = await client.get_transaction(send_tx_res.name)
|
|
926
|
+
assert tx_confirmed.confirmed
|
|
927
|
+
memos = tx_confirmed.get_memos()
|
|
928
|
+
assert len(memos) == len(outputs)
|
|
929
|
+
for output in outputs:
|
|
930
|
+
assert [output["memos"][0].encode()] in memos.values()
|
|
931
|
+
spend_bundle = send_tx_res.spend_bundle
|
|
932
|
+
assert spend_bundle is not None
|
|
933
|
+
for key in memos.keys():
|
|
934
|
+
assert key in [a.name() for a in spend_bundle.additions()]
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
@pytest.mark.anyio
|
|
938
|
+
async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
939
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
940
|
+
|
|
941
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
942
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
943
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
944
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
945
|
+
|
|
946
|
+
await generate_funds(full_node_api, env.wallet_1, 5)
|
|
947
|
+
|
|
948
|
+
all_transactions = await client.get_transactions(1)
|
|
949
|
+
assert len(all_transactions) >= 10
|
|
950
|
+
# Test transaction pagination
|
|
951
|
+
some_transactions = await client.get_transactions(1, 0, 5)
|
|
952
|
+
some_transactions_2 = await client.get_transactions(1, 5, 10)
|
|
953
|
+
assert some_transactions == all_transactions[0:5]
|
|
954
|
+
assert some_transactions_2 == all_transactions[5:10]
|
|
955
|
+
|
|
956
|
+
# Testing sorts
|
|
957
|
+
# Test the default sort (CONFIRMED_AT_HEIGHT)
|
|
958
|
+
assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"))
|
|
959
|
+
all_transactions = await client.get_transactions(1, reverse=True)
|
|
960
|
+
assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"), reverse=True)
|
|
961
|
+
|
|
962
|
+
# Test RELEVANCE
|
|
963
|
+
puzhash = await wallet.get_new_puzzlehash()
|
|
964
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
965
|
+
await client.send_transaction(
|
|
966
|
+
1, uint64(1), encode_puzzle_hash(puzhash, "txch"), DEFAULT_TX_CONFIG
|
|
967
|
+
) # Create a pending tx
|
|
968
|
+
|
|
969
|
+
all_transactions = await client.get_transactions(1, sort_key=SortKey.RELEVANCE)
|
|
970
|
+
sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"), reverse=True)
|
|
971
|
+
sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"), reverse=True)
|
|
972
|
+
sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"))
|
|
973
|
+
assert all_transactions == sorted_transactions
|
|
974
|
+
|
|
975
|
+
all_transactions = await client.get_transactions(1, sort_key=SortKey.RELEVANCE, reverse=True)
|
|
976
|
+
sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"))
|
|
977
|
+
sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"))
|
|
978
|
+
sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"), reverse=True)
|
|
979
|
+
assert all_transactions == sorted_transactions
|
|
980
|
+
|
|
981
|
+
# Test get_transactions to address
|
|
982
|
+
ph_by_addr = await wallet.get_new_puzzlehash()
|
|
983
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
984
|
+
await client.send_transaction(1, uint64(1), encode_puzzle_hash(ph_by_addr, "txch"), DEFAULT_TX_CONFIG)
|
|
985
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
986
|
+
tx_for_address = await client.get_transactions(1, to_address=encode_puzzle_hash(ph_by_addr, "txch"))
|
|
987
|
+
assert len(tx_for_address) == 1
|
|
988
|
+
assert tx_for_address[0].to_puzzle_hash == ph_by_addr
|
|
989
|
+
|
|
990
|
+
# Test type filter
|
|
991
|
+
all_transactions = await client.get_transactions(
|
|
992
|
+
1, type_filter=TransactionTypeFilter.include([TransactionType.COINBASE_REWARD])
|
|
993
|
+
)
|
|
994
|
+
assert len(all_transactions) == 5
|
|
995
|
+
assert all(transaction.type == TransactionType.COINBASE_REWARD for transaction in all_transactions)
|
|
996
|
+
# Test confirmed filter
|
|
997
|
+
all_transactions = await client.get_transactions(1, confirmed=True)
|
|
998
|
+
assert len(all_transactions) == 10
|
|
999
|
+
assert all(transaction.confirmed for transaction in all_transactions)
|
|
1000
|
+
all_transactions = await client.get_transactions(1, confirmed=False)
|
|
1001
|
+
assert len(all_transactions) == 2
|
|
1002
|
+
assert all(not transaction.confirmed for transaction in all_transactions)
|
|
1003
|
+
|
|
1004
|
+
# Test bypass broken txs
|
|
1005
|
+
await wallet.wallet_state_manager.tx_store.add_transaction_record(
|
|
1006
|
+
dataclasses.replace(all_transactions[0], type=uint32(TransactionType.INCOMING_CLAWBACK_SEND))
|
|
1007
|
+
)
|
|
1008
|
+
all_transactions = await client.get_transactions(
|
|
1009
|
+
1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND]), confirmed=False
|
|
1010
|
+
)
|
|
1011
|
+
assert len(all_transactions) == 1
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
@pytest.mark.anyio
|
|
1015
|
+
async def test_get_transaction_count(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1016
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1017
|
+
|
|
1018
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1019
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1020
|
+
|
|
1021
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
1022
|
+
|
|
1023
|
+
all_transactions = await client.get_transactions(1)
|
|
1024
|
+
assert len(all_transactions) > 0
|
|
1025
|
+
transaction_count = await client.get_transaction_count(1)
|
|
1026
|
+
assert transaction_count == len(all_transactions)
|
|
1027
|
+
transaction_count = await client.get_transaction_count(1, confirmed=False)
|
|
1028
|
+
assert transaction_count == 0
|
|
1029
|
+
transaction_count = await client.get_transaction_count(
|
|
1030
|
+
1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND])
|
|
1031
|
+
)
|
|
1032
|
+
assert transaction_count == 0
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
@pytest.mark.anyio
|
|
1036
|
+
async def test_cat_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1037
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1038
|
+
|
|
1039
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1040
|
+
|
|
1041
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1042
|
+
client_2: WalletRpcClient = env.wallet_2.rpc_client
|
|
1043
|
+
|
|
1044
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1045
|
+
|
|
1046
|
+
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
1047
|
+
await generate_funds(full_node_api, env.wallet_2, 1)
|
|
1048
|
+
|
|
1049
|
+
# Test a deprecated path
|
|
1050
|
+
with pytest.raises(ValueError, match="dropped"):
|
|
1051
|
+
await client.fetch(
|
|
1052
|
+
"create_new_wallet",
|
|
1053
|
+
{
|
|
1054
|
+
"wallet_type": "cat_wallet",
|
|
1055
|
+
"mode": "new",
|
|
1056
|
+
},
|
|
1057
|
+
)
|
|
1058
|
+
|
|
1059
|
+
# Creates a CAT wallet with 100 mojos and a CAT with 20 mojos and fee=10
|
|
1060
|
+
await client.create_new_cat_and_wallet(uint64(100), fee=uint64(10), test=True)
|
|
1061
|
+
await time_out_assert(20, check_client_synced, True, client)
|
|
1062
|
+
|
|
1063
|
+
res = await client.create_new_cat_and_wallet(uint64(20), test=True)
|
|
1064
|
+
assert res["success"]
|
|
1065
|
+
cat_0_id = res["wallet_id"]
|
|
1066
|
+
asset_id = bytes32.fromhex(res["asset_id"])
|
|
1067
|
+
assert len(asset_id) > 0
|
|
1068
|
+
|
|
1069
|
+
await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
|
|
1070
|
+
await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1})
|
|
1071
|
+
|
|
1072
|
+
bal_0 = await client.get_wallet_balance(cat_0_id)
|
|
1073
|
+
assert bal_0["confirmed_wallet_balance"] == 0
|
|
1074
|
+
assert bal_0["pending_coin_removal_count"] == 1
|
|
1075
|
+
col = await client.get_cat_asset_id(cat_0_id)
|
|
1076
|
+
assert col == asset_id
|
|
1077
|
+
assert (await client.get_cat_name(cat_0_id)) == CATWallet.default_wallet_name_for_unknown_cat(asset_id.hex())
|
|
1078
|
+
await client.set_cat_name(cat_0_id, "My cat")
|
|
1079
|
+
assert (await client.get_cat_name(cat_0_id)) == "My cat"
|
|
1080
|
+
result = await client.cat_asset_id_to_name(col)
|
|
1081
|
+
assert result is not None
|
|
1082
|
+
wid, name = result
|
|
1083
|
+
assert wid == cat_0_id
|
|
1084
|
+
assert name == "My cat"
|
|
1085
|
+
result = await client.cat_asset_id_to_name(bytes32.zeros)
|
|
1086
|
+
assert result is None
|
|
1087
|
+
verified_asset_id = next(iter(DEFAULT_CATS.items()))[1]["asset_id"]
|
|
1088
|
+
result = await client.cat_asset_id_to_name(bytes32.from_hexstr(verified_asset_id))
|
|
1089
|
+
assert result is not None
|
|
1090
|
+
should_be_none, name = result
|
|
1091
|
+
assert should_be_none is None
|
|
1092
|
+
assert name == next(iter(DEFAULT_CATS.items()))[1]["name"]
|
|
1093
|
+
|
|
1094
|
+
# make sure spend is in mempool before farming tx block
|
|
1095
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
|
|
1096
|
+
for i in range(5):
|
|
1097
|
+
if check_mempool_spend_count(full_node_api, 0):
|
|
1098
|
+
break
|
|
1099
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1100
|
+
|
|
1101
|
+
# check that we farmed the transaction
|
|
1102
|
+
assert check_mempool_spend_count(full_node_api, 0)
|
|
1103
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
|
|
1104
|
+
|
|
1105
|
+
await time_out_assert(5, get_confirmed_balance, 20, client, cat_0_id)
|
|
1106
|
+
bal_0 = await client.get_wallet_balance(cat_0_id)
|
|
1107
|
+
assert bal_0["pending_coin_removal_count"] == 0
|
|
1108
|
+
assert bal_0["unspent_coin_count"] == 1
|
|
1109
|
+
|
|
1110
|
+
# Creates a second wallet with the same CAT
|
|
1111
|
+
res = await client_2.create_wallet_for_existing_cat(asset_id)
|
|
1112
|
+
assert res["success"]
|
|
1113
|
+
cat_1_id = res["wallet_id"]
|
|
1114
|
+
cat_1_asset_id = bytes.fromhex(res["asset_id"])
|
|
1115
|
+
assert cat_1_asset_id == asset_id
|
|
1116
|
+
|
|
1117
|
+
await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
|
|
1118
|
+
await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 1})
|
|
1119
|
+
|
|
1120
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1121
|
+
|
|
1122
|
+
bal_1 = await client_2.get_wallet_balance(cat_1_id)
|
|
1123
|
+
assert bal_1["confirmed_wallet_balance"] == 0
|
|
1124
|
+
|
|
1125
|
+
addr_0 = await client.get_next_address(cat_0_id, False)
|
|
1126
|
+
addr_1 = await client_2.get_next_address(cat_1_id, False)
|
|
1127
|
+
|
|
1128
|
+
assert addr_0 != addr_1
|
|
1129
|
+
|
|
1130
|
+
# Test CAT spend without a fee
|
|
1131
|
+
with pytest.raises(ValueError):
|
|
1132
|
+
await client.cat_spend(
|
|
1133
|
+
cat_0_id,
|
|
1134
|
+
DEFAULT_TX_CONFIG.override(
|
|
1135
|
+
excluded_coin_amounts=[uint64(20)],
|
|
1136
|
+
excluded_coin_ids=[bytes32.zeros],
|
|
1137
|
+
),
|
|
1138
|
+
uint64(4),
|
|
1139
|
+
addr_1,
|
|
1140
|
+
uint64(0),
|
|
1141
|
+
["the cat memo"],
|
|
1142
|
+
)
|
|
1143
|
+
tx_res = await client.cat_spend(cat_0_id, DEFAULT_TX_CONFIG, uint64(4), addr_1, uint64(0), ["the cat memo"])
|
|
1144
|
+
|
|
1145
|
+
spend_bundle = tx_res.transaction.spend_bundle
|
|
1146
|
+
assert spend_bundle is not None
|
|
1147
|
+
assert uncurry_puzzle(spend_bundle.coin_spends[0].puzzle_reveal).mod == CAT_MOD
|
|
1148
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
1149
|
+
|
|
1150
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1151
|
+
|
|
1152
|
+
# Test CAT spend with a fee
|
|
1153
|
+
tx_res = await client.cat_spend(cat_0_id, DEFAULT_TX_CONFIG, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"])
|
|
1154
|
+
|
|
1155
|
+
spend_bundle = tx_res.transaction.spend_bundle
|
|
1156
|
+
assert spend_bundle is not None
|
|
1157
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
1158
|
+
|
|
1159
|
+
# Test CAT spend with a fee and pre-specified removals / coins
|
|
1160
|
+
removals = await client.select_coins(
|
|
1161
|
+
amount=uint64(2), wallet_id=cat_0_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
1162
|
+
)
|
|
1163
|
+
tx_res = await client.cat_spend(
|
|
1164
|
+
cat_0_id, DEFAULT_TX_CONFIG, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"], removals=removals
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
spend_bundle = tx_res.transaction.spend_bundle
|
|
1168
|
+
assert spend_bundle is not None
|
|
1169
|
+
assert removals[0] in {removal for tx in tx_res.transactions for removal in tx.removals}
|
|
1170
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
1171
|
+
|
|
1172
|
+
# Test unacknowledged CAT
|
|
1173
|
+
await wallet_node.wallet_state_manager.interested_store.add_unacknowledged_token(
|
|
1174
|
+
asset_id, "Unknown", uint32(10000), bytes32(b"\00" * 32)
|
|
1175
|
+
)
|
|
1176
|
+
cats = await client.get_stray_cats()
|
|
1177
|
+
assert len(cats) == 1
|
|
1178
|
+
|
|
1179
|
+
await time_out_assert(20, get_confirmed_balance, 14, client, cat_0_id)
|
|
1180
|
+
await time_out_assert(20, get_confirmed_balance, 6, client_2, cat_1_id)
|
|
1181
|
+
|
|
1182
|
+
# Test CAT coin selection
|
|
1183
|
+
selected_coins = await client.select_coins(
|
|
1184
|
+
amount=1, wallet_id=cat_0_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
|
|
1185
|
+
)
|
|
1186
|
+
assert len(selected_coins) > 0
|
|
1187
|
+
|
|
1188
|
+
# Test get_cat_list
|
|
1189
|
+
cat_list = (await client.get_cat_list()).cat_list
|
|
1190
|
+
assert len(DEFAULT_CATS) == len(cat_list)
|
|
1191
|
+
default_cats_set = {
|
|
1192
|
+
DefaultCAT(asset_id=bytes32.from_hexstr(cat["asset_id"]), name=cat["name"], symbol=cat["symbol"])
|
|
1193
|
+
for cat in DEFAULT_CATS.values()
|
|
1194
|
+
}
|
|
1195
|
+
assert default_cats_set == set(cat_list)
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
@pytest.mark.anyio
|
|
1199
|
+
async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1200
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1201
|
+
|
|
1202
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1203
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
1204
|
+
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
1205
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1206
|
+
|
|
1207
|
+
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
1208
|
+
await generate_funds(full_node_api, env.wallet_2, 1)
|
|
1209
|
+
|
|
1210
|
+
# Creates a CAT wallet with 20 mojos
|
|
1211
|
+
res = await wallet_1_rpc.create_new_cat_and_wallet(uint64(20), test=True)
|
|
1212
|
+
assert res["success"]
|
|
1213
|
+
cat_wallet_id = res["wallet_id"]
|
|
1214
|
+
cat_asset_id = bytes32.fromhex(res["asset_id"])
|
|
1215
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1216
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1217
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
|
|
1218
|
+
|
|
1219
|
+
await time_out_assert(5, get_confirmed_balance, 20, wallet_1_rpc, cat_wallet_id)
|
|
1220
|
+
|
|
1221
|
+
# Creates a wallet for the same CAT on wallet_2 and send 4 CAT from wallet_1 to it
|
|
1222
|
+
await wallet_2_rpc.create_wallet_for_existing_cat(cat_asset_id)
|
|
1223
|
+
wallet_2_address = await wallet_2_rpc.get_next_address(cat_wallet_id, False)
|
|
1224
|
+
adds = [{"puzzle_hash": decode_puzzle_hash(wallet_2_address), "amount": uint64(4), "memos": ["the cat memo"]}]
|
|
1225
|
+
tx_res = (
|
|
1226
|
+
await wallet_1_rpc.send_transaction_multi(
|
|
1227
|
+
cat_wallet_id, additions=adds, tx_config=DEFAULT_TX_CONFIG, fee=uint64(0)
|
|
1228
|
+
)
|
|
1229
|
+
).transaction
|
|
1230
|
+
spend_bundle = tx_res.spend_bundle
|
|
1231
|
+
assert spend_bundle is not None
|
|
1232
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
1233
|
+
await time_out_assert(5, get_confirmed_balance, 4, wallet_2_rpc, cat_wallet_id)
|
|
1234
|
+
test_crs: list[CoinRecord] = await wallet_1_rpc.get_coin_records_by_names(
|
|
1235
|
+
[a.name() for a in spend_bundle.additions() if a.amount != 4]
|
|
1236
|
+
)
|
|
1237
|
+
for cr in test_crs:
|
|
1238
|
+
assert cr.coin in spend_bundle.additions()
|
|
1239
|
+
with pytest.raises(ValueError):
|
|
1240
|
+
await wallet_1_rpc.get_coin_records_by_names([a.name() for a in spend_bundle.additions() if a.amount == 4])
|
|
1241
|
+
# Create an offer of 5 chia for one CAT
|
|
1242
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1243
|
+
{uint32(1): -5, cat_asset_id.hex(): 1}, DEFAULT_TX_CONFIG, validate_only=True
|
|
1244
|
+
)
|
|
1245
|
+
all_offers = await wallet_1_rpc.get_all_offers()
|
|
1246
|
+
assert len(all_offers) == 0
|
|
1247
|
+
|
|
1248
|
+
driver_dict: dict[str, Any] = {cat_asset_id.hex(): {"type": "CAT", "tail": "0x" + cat_asset_id.hex()}}
|
|
1249
|
+
|
|
1250
|
+
create_res = await wallet_1_rpc.create_offer_for_ids(
|
|
1251
|
+
{uint32(1): -5, cat_asset_id.hex(): 1},
|
|
1252
|
+
DEFAULT_TX_CONFIG,
|
|
1253
|
+
driver_dict=driver_dict,
|
|
1254
|
+
fee=uint64(1),
|
|
1255
|
+
)
|
|
1256
|
+
offer = create_res.offer
|
|
1257
|
+
|
|
1258
|
+
id, summary = await wallet_1_rpc.get_offer_summary(offer)
|
|
1259
|
+
assert id == offer.name()
|
|
1260
|
+
id, advanced_summary = await wallet_1_rpc.get_offer_summary(offer, advanced=True)
|
|
1261
|
+
assert id == offer.name()
|
|
1262
|
+
assert summary == {
|
|
1263
|
+
"offered": {"xch": 5},
|
|
1264
|
+
"requested": {cat_asset_id.hex(): 1},
|
|
1265
|
+
"infos": driver_dict,
|
|
1266
|
+
"fees": 1,
|
|
1267
|
+
"additions": [c.name().hex() for c in offer.additions()],
|
|
1268
|
+
"removals": [c.name().hex() for c in offer.removals()],
|
|
1269
|
+
"valid_times": {
|
|
1270
|
+
"max_height": None,
|
|
1271
|
+
"max_time": None,
|
|
1272
|
+
"min_height": None,
|
|
1273
|
+
"min_time": None,
|
|
1274
|
+
},
|
|
1275
|
+
}
|
|
1276
|
+
assert advanced_summary == summary
|
|
1277
|
+
|
|
1278
|
+
id, _valid = await wallet_1_rpc.check_offer_validity(offer)
|
|
1279
|
+
assert id == offer.name()
|
|
1280
|
+
|
|
1281
|
+
all_offers = await wallet_1_rpc.get_all_offers(file_contents=True)
|
|
1282
|
+
assert len(all_offers) == 1
|
|
1283
|
+
assert TradeStatus(all_offers[0].status) == TradeStatus.PENDING_ACCEPT
|
|
1284
|
+
assert all_offers[0].offer == bytes(offer)
|
|
1285
|
+
|
|
1286
|
+
offer_count = await wallet_1_rpc.get_offers_count()
|
|
1287
|
+
assert offer_count.total == 1
|
|
1288
|
+
assert offer_count.my_offers_count == 1
|
|
1289
|
+
assert offer_count.taken_offers_count == 0
|
|
1290
|
+
|
|
1291
|
+
trade_record = (await wallet_2_rpc.take_offer(offer, DEFAULT_TX_CONFIG, fee=uint64(1))).trade_record
|
|
1292
|
+
assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM
|
|
1293
|
+
|
|
1294
|
+
await wallet_1_rpc.cancel_offer(offer.name(), DEFAULT_TX_CONFIG, secure=False)
|
|
1295
|
+
|
|
1296
|
+
trade_record = await wallet_1_rpc.get_offer(offer.name(), file_contents=True)
|
|
1297
|
+
assert trade_record.offer == bytes(offer)
|
|
1298
|
+
assert TradeStatus(trade_record.status) == TradeStatus.CANCELLED
|
|
1299
|
+
|
|
1300
|
+
await wallet_1_rpc.cancel_offer(offer.name(), DEFAULT_TX_CONFIG, fee=uint64(1), secure=True)
|
|
1301
|
+
|
|
1302
|
+
trade_record = await wallet_1_rpc.get_offer(offer.name())
|
|
1303
|
+
assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CANCEL
|
|
1304
|
+
|
|
1305
|
+
create_res = await wallet_1_rpc.create_offer_for_ids(
|
|
1306
|
+
{uint32(1): -5, cat_wallet_id: 1}, DEFAULT_TX_CONFIG, fee=uint64(1)
|
|
1307
|
+
)
|
|
1308
|
+
all_offers = await wallet_1_rpc.get_all_offers()
|
|
1309
|
+
assert len(all_offers) == 2
|
|
1310
|
+
offer_count = await wallet_1_rpc.get_offers_count()
|
|
1311
|
+
assert offer_count.total == 2
|
|
1312
|
+
assert offer_count.my_offers_count == 2
|
|
1313
|
+
assert offer_count.taken_offers_count == 0
|
|
1314
|
+
new_trade_record = create_res.trade_record
|
|
1315
|
+
|
|
1316
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1317
|
+
|
|
1318
|
+
async def is_trade_confirmed(client, trade) -> bool:
|
|
1319
|
+
trade_record = await client.get_offer(trade.name())
|
|
1320
|
+
return TradeStatus(trade_record.status) == TradeStatus.CONFIRMED
|
|
1321
|
+
|
|
1322
|
+
await time_out_assert(15, is_trade_confirmed, True, wallet_1_rpc, offer)
|
|
1323
|
+
|
|
1324
|
+
# Test trade sorting
|
|
1325
|
+
def only_ids(trades):
|
|
1326
|
+
return [t.trade_id for t in trades]
|
|
1327
|
+
|
|
1328
|
+
trade_record = await wallet_1_rpc.get_offer(offer.name())
|
|
1329
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True) # confirmed at index descending
|
|
1330
|
+
assert len(all_offers) == 2
|
|
1331
|
+
assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
|
|
1332
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, reverse=True) # confirmed at index ascending
|
|
1333
|
+
assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
|
|
1334
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, sort_key="RELEVANCE") # most relevant
|
|
1335
|
+
assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
|
|
1336
|
+
all_offers = await wallet_1_rpc.get_all_offers(
|
|
1337
|
+
include_completed=True, sort_key="RELEVANCE", reverse=True
|
|
1338
|
+
) # least relevant
|
|
1339
|
+
assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
|
|
1340
|
+
# Test pagination
|
|
1341
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=1)
|
|
1342
|
+
assert len(all_offers) == 1
|
|
1343
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=50)
|
|
1344
|
+
assert len(all_offers) == 0
|
|
1345
|
+
all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=50)
|
|
1346
|
+
assert len(all_offers) == 2
|
|
1347
|
+
|
|
1348
|
+
###
|
|
1349
|
+
# This is temporary code, delete it when we no longer care about incorrectly parsing old offers
|
|
1350
|
+
# There's also temp code in wallet_rpc_api.py
|
|
1351
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1352
|
+
await wallet_1_rpc.fetch(
|
|
1353
|
+
"get_offer_summary",
|
|
1354
|
+
{
|
|
1355
|
+
"offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
|
|
1356
|
+
},
|
|
1357
|
+
)
|
|
1358
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1359
|
+
await wallet_1_rpc.fetch(
|
|
1360
|
+
"check_offer_validity",
|
|
1361
|
+
{
|
|
1362
|
+
"offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
|
|
1363
|
+
},
|
|
1364
|
+
)
|
|
1365
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1366
|
+
await wallet_1_rpc.fetch(
|
|
1367
|
+
"take_offer",
|
|
1368
|
+
{
|
|
1369
|
+
"offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
|
|
1370
|
+
},
|
|
1371
|
+
)
|
|
1372
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1373
|
+
await wallet_1_rpc.fetch(
|
|
1374
|
+
"get_offer_summary",
|
|
1375
|
+
{
|
|
1376
|
+
"offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
|
|
1377
|
+
},
|
|
1378
|
+
)
|
|
1379
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1380
|
+
await wallet_1_rpc.fetch(
|
|
1381
|
+
"check_offer_validity",
|
|
1382
|
+
{
|
|
1383
|
+
"offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
|
|
1384
|
+
},
|
|
1385
|
+
)
|
|
1386
|
+
with pytest.raises(ValueError, match="Old offer format is no longer supported"):
|
|
1387
|
+
await wallet_1_rpc.fetch(
|
|
1388
|
+
"take_offer",
|
|
1389
|
+
{
|
|
1390
|
+
"offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
|
|
1391
|
+
},
|
|
1392
|
+
)
|
|
1393
|
+
###
|
|
1394
|
+
|
|
1395
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1396
|
+
{uint32(1): -5, cat_asset_id.hex(): 1},
|
|
1397
|
+
DEFAULT_TX_CONFIG,
|
|
1398
|
+
driver_dict=driver_dict,
|
|
1399
|
+
)
|
|
1400
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 2
|
|
1401
|
+
await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, batch_size=1)
|
|
1402
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
|
|
1403
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
|
|
1404
|
+
|
|
1405
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1406
|
+
|
|
1407
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1408
|
+
{uint32(1): -5, cat_asset_id.hex(): 1},
|
|
1409
|
+
DEFAULT_TX_CONFIG,
|
|
1410
|
+
driver_dict=driver_dict,
|
|
1411
|
+
)
|
|
1412
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1413
|
+
{uint32(1): 5, cat_asset_id.hex(): -1},
|
|
1414
|
+
DEFAULT_TX_CONFIG,
|
|
1415
|
+
driver_dict=driver_dict,
|
|
1416
|
+
)
|
|
1417
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 2
|
|
1418
|
+
await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, cancel_all=True)
|
|
1419
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
|
|
1420
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1421
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
1422
|
+
|
|
1423
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1424
|
+
{uint32(1): 5, cat_asset_id.hex(): -1},
|
|
1425
|
+
DEFAULT_TX_CONFIG,
|
|
1426
|
+
driver_dict=driver_dict,
|
|
1427
|
+
)
|
|
1428
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 1
|
|
1429
|
+
await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, asset_id=bytes32.zeros)
|
|
1430
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 1
|
|
1431
|
+
await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, asset_id=cat_asset_id)
|
|
1432
|
+
assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
|
|
1433
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1434
|
+
|
|
1435
|
+
with pytest.raises(ValueError, match="not currently supported"):
|
|
1436
|
+
await wallet_1_rpc.create_offer_for_ids(
|
|
1437
|
+
{uint32(1): -5, cat_asset_id.hex(): 1},
|
|
1438
|
+
DEFAULT_TX_CONFIG,
|
|
1439
|
+
driver_dict=driver_dict,
|
|
1440
|
+
timelock_info=ConditionValidTimes(min_secs_since_created=uint64(1)),
|
|
1441
|
+
)
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
@pytest.mark.anyio
|
|
1445
|
+
async def test_get_coin_records_by_names(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
|
|
1446
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1447
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1448
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1449
|
+
store = wallet_node.wallet_state_manager.coin_store
|
|
1450
|
+
full_node_api = env.full_node.api
|
|
1451
|
+
# Generate some funds
|
|
1452
|
+
generated_funds = await generate_funds(full_node_api, env.wallet_1, 5)
|
|
1453
|
+
address = encode_puzzle_hash(await env.wallet_1.wallet.get_new_puzzlehash(), "txch")
|
|
1454
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
1455
|
+
|
|
1456
|
+
# Spend half of it back to the same wallet get some spent coins in the wallet
|
|
1457
|
+
tx = (await client.send_transaction(1, uint64(generated_funds / 2), address, DEFAULT_TX_CONFIG)).transaction
|
|
1458
|
+
assert tx.spend_bundle is not None
|
|
1459
|
+
await time_out_assert(20, tx_in_mempool, True, client, tx.name)
|
|
1460
|
+
await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
|
|
1461
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
|
|
1462
|
+
# Prepare some records and parameters first
|
|
1463
|
+
result = await store.get_coin_records()
|
|
1464
|
+
coins = {record.coin for record in result.records}
|
|
1465
|
+
coins_unspent = {record.coin for record in result.records if not record.spent}
|
|
1466
|
+
coin_ids = [coin.name() for coin in coins]
|
|
1467
|
+
coin_ids_unspent = [coin.name() for coin in coins_unspent]
|
|
1468
|
+
assert len(coin_ids) > 0
|
|
1469
|
+
assert len(coin_ids_unspent) > 0
|
|
1470
|
+
# Do some queries to trigger all parameters
|
|
1471
|
+
# 1. Empty coin_ids
|
|
1472
|
+
assert await client.get_coin_records_by_names([]) == []
|
|
1473
|
+
# 2. All coins
|
|
1474
|
+
rpc_result = await client.get_coin_records_by_names(coin_ids + coin_ids_unspent)
|
|
1475
|
+
assert {record.coin for record in rpc_result} == {*coins, *coins_unspent}
|
|
1476
|
+
# 3. All spent coins
|
|
1477
|
+
rpc_result = await client.get_coin_records_by_names(coin_ids, include_spent_coins=True)
|
|
1478
|
+
assert {record.coin for record in rpc_result} == coins
|
|
1479
|
+
# 4. All unspent coins
|
|
1480
|
+
rpc_result = await client.get_coin_records_by_names(coin_ids_unspent, include_spent_coins=False)
|
|
1481
|
+
assert {record.coin for record in rpc_result} == coins_unspent
|
|
1482
|
+
# 5. Filter start/end height
|
|
1483
|
+
filter_records = result.records[:10]
|
|
1484
|
+
assert len(filter_records) == 10
|
|
1485
|
+
filter_coin_ids = [record.name() for record in filter_records]
|
|
1486
|
+
filter_coins = {record.coin for record in filter_records}
|
|
1487
|
+
min_height = min(record.confirmed_block_height for record in filter_records)
|
|
1488
|
+
max_height = max(record.confirmed_block_height for record in filter_records)
|
|
1489
|
+
assert min_height != max_height
|
|
1490
|
+
rpc_result = await client.get_coin_records_by_names(filter_coin_ids, start_height=min_height, end_height=max_height)
|
|
1491
|
+
assert {record.coin for record in rpc_result} == filter_coins
|
|
1492
|
+
# 8. Test the failure case
|
|
1493
|
+
with pytest.raises(ValueError, match="not found"):
|
|
1494
|
+
await client.get_coin_records_by_names(coin_ids, include_spent_coins=False)
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
@pytest.mark.anyio
|
|
1498
|
+
async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1499
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1500
|
+
|
|
1501
|
+
wallet_1: Wallet = env.wallet_1.wallet
|
|
1502
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
1503
|
+
wallet_1_node: WalletNode = env.wallet_1.node
|
|
1504
|
+
wallet_2_node: WalletNode = env.wallet_2.node
|
|
1505
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
1506
|
+
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
1507
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1508
|
+
wallet_1_id = wallet_1.id()
|
|
1509
|
+
|
|
1510
|
+
await generate_funds(env.full_node.api, env.wallet_1, 5)
|
|
1511
|
+
|
|
1512
|
+
# Create a DID wallet
|
|
1513
|
+
res = await wallet_1_rpc.create_new_did_wallet(amount=1, tx_config=DEFAULT_TX_CONFIG, name="Profile 1")
|
|
1514
|
+
assert res["success"]
|
|
1515
|
+
did_wallet_id_0 = res["wallet_id"]
|
|
1516
|
+
did_id_0 = res["my_did"]
|
|
1517
|
+
|
|
1518
|
+
# Get wallet name
|
|
1519
|
+
res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
|
|
1520
|
+
assert res["success"]
|
|
1521
|
+
assert res["name"] == "Profile 1"
|
|
1522
|
+
nft_wallet: WalletProtocol = wallet_1_node.wallet_state_manager.wallets[did_wallet_id_0 + 1]
|
|
1523
|
+
assert isinstance(nft_wallet, NFTWallet)
|
|
1524
|
+
assert nft_wallet.get_name() == "Profile 1 NFT Wallet"
|
|
1525
|
+
|
|
1526
|
+
# Set wallet name
|
|
1527
|
+
new_wallet_name = "test name"
|
|
1528
|
+
res = await wallet_1_rpc.did_set_wallet_name(did_wallet_id_0, new_wallet_name)
|
|
1529
|
+
assert res["success"]
|
|
1530
|
+
res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
|
|
1531
|
+
assert res["success"]
|
|
1532
|
+
assert res["name"] == new_wallet_name
|
|
1533
|
+
with pytest.raises(ValueError, match="wallet id 1 is of type Wallet but type DIDWallet is required"):
|
|
1534
|
+
await wallet_1_rpc.did_set_wallet_name(wallet_1_id, new_wallet_name)
|
|
1535
|
+
|
|
1536
|
+
# Check DID ID
|
|
1537
|
+
res = await wallet_1_rpc.get_did_id(did_wallet_id_0)
|
|
1538
|
+
assert res["success"]
|
|
1539
|
+
assert did_id_0 == res["my_did"]
|
|
1540
|
+
# Create backup file
|
|
1541
|
+
res = await wallet_1_rpc.create_did_backup_file(did_wallet_id_0, "backup.did")
|
|
1542
|
+
assert res["success"]
|
|
1543
|
+
|
|
1544
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1545
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
1546
|
+
# Update recovery list
|
|
1547
|
+
update_res = await wallet_1_rpc.update_did_recovery_list(did_wallet_id_0, [did_id_0], 1, DEFAULT_TX_CONFIG)
|
|
1548
|
+
assert len(update_res.transactions) > 0
|
|
1549
|
+
res = await wallet_1_rpc.get_did_recovery_list(did_wallet_id_0)
|
|
1550
|
+
assert res["num_required"] == 1
|
|
1551
|
+
assert res["recovery_list"][0] == did_id_0
|
|
1552
|
+
|
|
1553
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1554
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
1555
|
+
|
|
1556
|
+
# Update metadata
|
|
1557
|
+
with pytest.raises(ValueError, match="wallet id 1 is of type Wallet but type DIDWallet is required"):
|
|
1558
|
+
await wallet_1_rpc.update_did_metadata(wallet_1_id, {"Twitter": "Https://test"}, DEFAULT_TX_CONFIG)
|
|
1559
|
+
await wallet_1_rpc.update_did_metadata(did_wallet_id_0, {"Twitter": "Https://test"}, DEFAULT_TX_CONFIG)
|
|
1560
|
+
|
|
1561
|
+
res = await wallet_1_rpc.get_did_metadata(did_wallet_id_0)
|
|
1562
|
+
assert res["metadata"]["Twitter"] == "Https://test"
|
|
1563
|
+
|
|
1564
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1565
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
1566
|
+
|
|
1567
|
+
# Transfer DID
|
|
1568
|
+
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
1569
|
+
await wallet_1_rpc.did_transfer_did(did_wallet_id_0, addr, 0, True, DEFAULT_TX_CONFIG)
|
|
1570
|
+
|
|
1571
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1572
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
1573
|
+
|
|
1574
|
+
async def num_wallets() -> int:
|
|
1575
|
+
return len(await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries())
|
|
1576
|
+
|
|
1577
|
+
await time_out_assert(30, num_wallets, 2)
|
|
1578
|
+
|
|
1579
|
+
did_wallets = list(
|
|
1580
|
+
filter(
|
|
1581
|
+
lambda w: (w.type == WalletType.DECENTRALIZED_ID),
|
|
1582
|
+
await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(),
|
|
1583
|
+
)
|
|
1584
|
+
)
|
|
1585
|
+
did_wallet_2: WalletProtocol = wallet_2_node.wallet_state_manager.wallets[did_wallets[0].id]
|
|
1586
|
+
assert isinstance(did_wallet_2, DIDWallet)
|
|
1587
|
+
assert (
|
|
1588
|
+
encode_puzzle_hash(bytes32.from_hexstr(did_wallet_2.get_my_DID()), AddressType.DID.hrp(wallet_2_node.config))
|
|
1589
|
+
== did_id_0
|
|
1590
|
+
)
|
|
1591
|
+
metadata = json.loads(did_wallet_2.did_info.metadata)
|
|
1592
|
+
assert metadata["Twitter"] == "Https://test"
|
|
1593
|
+
|
|
1594
|
+
last_did_coin = await did_wallet_2.get_coin()
|
|
1595
|
+
await wallet_2_rpc.did_message_spend(did_wallet_2.id(), DEFAULT_TX_CONFIG, push=True)
|
|
1596
|
+
await wallet_2_node.wallet_state_manager.add_interested_coin_ids([last_did_coin.name()])
|
|
1597
|
+
|
|
1598
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1599
|
+
await farm_transaction_block(full_node_api, wallet_2_node)
|
|
1600
|
+
|
|
1601
|
+
next_did_coin = await did_wallet_2.get_coin()
|
|
1602
|
+
assert next_did_coin.parent_coin_info == last_did_coin.name()
|
|
1603
|
+
last_did_coin = next_did_coin
|
|
1604
|
+
|
|
1605
|
+
await wallet_2_rpc.did_message_spend(did_wallet_2.id(), DEFAULT_TX_CONFIG.override(reuse_puzhash=True), push=True)
|
|
1606
|
+
await wallet_2_node.wallet_state_manager.add_interested_coin_ids([last_did_coin.name()])
|
|
1607
|
+
|
|
1608
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1609
|
+
await farm_transaction_block(full_node_api, wallet_2_node)
|
|
1610
|
+
|
|
1611
|
+
next_did_coin = await did_wallet_2.get_coin()
|
|
1612
|
+
assert next_did_coin.parent_coin_info == last_did_coin.name()
|
|
1613
|
+
assert next_did_coin.puzzle_hash == last_did_coin.puzzle_hash
|
|
1614
|
+
|
|
1615
|
+
# Test did_get_pubkey
|
|
1616
|
+
pubkey_res = await wallet_2_rpc.get_did_pubkey(DIDGetPubkey(did_wallet_2.id()))
|
|
1617
|
+
assert isinstance(pubkey_res.pubkey, G1Element)
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
@pytest.mark.anyio
|
|
1621
|
+
async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1622
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1623
|
+
wallet_1_node: WalletNode = env.wallet_1.node
|
|
1624
|
+
wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
|
|
1625
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
1626
|
+
wallet_2_node: WalletNode = env.wallet_2.node
|
|
1627
|
+
wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
|
|
1628
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1629
|
+
|
|
1630
|
+
await generate_funds(env.full_node.api, env.wallet_1, 5)
|
|
1631
|
+
|
|
1632
|
+
res = await wallet_1_rpc.create_new_nft_wallet(None)
|
|
1633
|
+
nft_wallet_id = res["wallet_id"]
|
|
1634
|
+
mint_res = await wallet_1_rpc.mint_nft(
|
|
1635
|
+
nft_wallet_id,
|
|
1636
|
+
None,
|
|
1637
|
+
None,
|
|
1638
|
+
"0xD4584AD463139FA8C0D9F68F4B59F185",
|
|
1639
|
+
["https://www.chia.net/img/branding/chia-logo.svg"],
|
|
1640
|
+
DEFAULT_TX_CONFIG,
|
|
1641
|
+
)
|
|
1642
|
+
|
|
1643
|
+
spend_bundle = mint_res.spend_bundle
|
|
1644
|
+
|
|
1645
|
+
await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
|
|
1646
|
+
|
|
1647
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=15)
|
|
1648
|
+
|
|
1649
|
+
nft_wallet: WalletProtocol = wallet_1_node.wallet_state_manager.wallets[nft_wallet_id]
|
|
1650
|
+
assert isinstance(nft_wallet, NFTWallet)
|
|
1651
|
+
|
|
1652
|
+
async def have_nfts():
|
|
1653
|
+
return await nft_wallet.get_nft_count() > 0
|
|
1654
|
+
|
|
1655
|
+
await time_out_assert(15, have_nfts, True)
|
|
1656
|
+
|
|
1657
|
+
# Test with the hex version of nft_id
|
|
1658
|
+
nft_id = (await nft_wallet.get_current_nfts())[0].coin.name().hex()
|
|
1659
|
+
nft_info = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
|
|
1660
|
+
assert nft_info["nft_coin_id"][2:] == (await nft_wallet.get_current_nfts())[0].coin.name().hex()
|
|
1661
|
+
# Test with the bech32m version of nft_id
|
|
1662
|
+
hmr_nft_id = encode_puzzle_hash(
|
|
1663
|
+
(await nft_wallet.get_current_nfts())[0].coin.name(), AddressType.NFT.hrp(wallet_1_node.config)
|
|
1664
|
+
)
|
|
1665
|
+
nft_info = (await wallet_1_rpc.get_nft_info(hmr_nft_id))["nft_info"]
|
|
1666
|
+
assert nft_info["nft_coin_id"][2:] == (await nft_wallet.get_current_nfts())[0].coin.name().hex()
|
|
1667
|
+
|
|
1668
|
+
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
1669
|
+
await wallet_1_rpc.transfer_nft(nft_wallet_id, nft_id, addr, 0, DEFAULT_TX_CONFIG)
|
|
1670
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
1671
|
+
await farm_transaction_block(full_node_api, wallet_1_node)
|
|
1672
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 0)
|
|
1673
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=5)
|
|
1674
|
+
|
|
1675
|
+
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=5)
|
|
1676
|
+
|
|
1677
|
+
nft_wallet_id_1 = (
|
|
1678
|
+
await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(wallet_type=WalletType.NFT)
|
|
1679
|
+
)[0].id
|
|
1680
|
+
nft_wallet_1: WalletProtocol = wallet_2_node.wallet_state_manager.wallets[nft_wallet_id_1]
|
|
1681
|
+
assert isinstance(nft_wallet_1, NFTWallet)
|
|
1682
|
+
nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id, False))["nft_info"]
|
|
1683
|
+
assert nft_info_1 == nft_info
|
|
1684
|
+
nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
|
|
1685
|
+
assert nft_info_1["nft_coin_id"][2:] == (await nft_wallet_1.get_current_nfts())[0].coin.name().hex()
|
|
1686
|
+
# Cross-check NFT
|
|
1687
|
+
nft_info_2 = (await wallet_2_rpc.list_nfts(nft_wallet_id_1))["nft_list"][0]
|
|
1688
|
+
assert nft_info_1 == nft_info_2
|
|
1689
|
+
|
|
1690
|
+
# Test royalty endpoint
|
|
1691
|
+
royalty_summary = await wallet_1_rpc.nft_calculate_royalties(
|
|
1692
|
+
{
|
|
1693
|
+
"my asset": ("my address", uint16(10000)),
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
None: uint64(10000),
|
|
1697
|
+
},
|
|
1698
|
+
)
|
|
1699
|
+
assert royalty_summary == {
|
|
1700
|
+
"my asset": [
|
|
1701
|
+
{
|
|
1702
|
+
"asset": None,
|
|
1703
|
+
"address": "my address",
|
|
1704
|
+
"amount": 10000,
|
|
1705
|
+
}
|
|
1706
|
+
],
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
async def _check_delete_key(
|
|
1711
|
+
client: WalletRpcClient, wallet_node: WalletNode, farmer_fp: int, pool_fp: int, observer: bool = False
|
|
1712
|
+
) -> None:
|
|
1713
|
+
# Add in reward addresses into farmer and pool for testing delete key checks
|
|
1714
|
+
# set farmer to first private key
|
|
1715
|
+
create_sk = master_sk_to_wallet_sk_unhardened if observer else master_sk_to_wallet_sk
|
|
1716
|
+
|
|
1717
|
+
sk = await wallet_node.get_key_for_fingerprint(farmer_fp, private=True)
|
|
1718
|
+
assert sk is not None
|
|
1719
|
+
farmer_ph = create_puzzlehash_for_pk(create_sk(sk, uint32(0)).get_g1())
|
|
1720
|
+
|
|
1721
|
+
sk = await wallet_node.get_key_for_fingerprint(pool_fp, private=True)
|
|
1722
|
+
assert sk is not None
|
|
1723
|
+
pool_ph = create_puzzlehash_for_pk(create_sk(sk, uint32(0)).get_g1())
|
|
1724
|
+
|
|
1725
|
+
with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
|
|
1726
|
+
test_config["farmer"]["xch_target_address"] = encode_puzzle_hash(farmer_ph, "txch")
|
|
1727
|
+
test_config["pool"]["xch_target_address"] = encode_puzzle_hash(pool_ph, "txch")
|
|
1728
|
+
save_config(wallet_node.root_path, "config.yaml", test_config)
|
|
1729
|
+
|
|
1730
|
+
# Check farmer_fp key
|
|
1731
|
+
resp = await client.check_delete_key(CheckDeleteKey(uint32(farmer_fp)))
|
|
1732
|
+
assert resp.fingerprint == farmer_fp
|
|
1733
|
+
assert resp.used_for_farmer_rewards is True
|
|
1734
|
+
assert resp.used_for_pool_rewards is False
|
|
1735
|
+
|
|
1736
|
+
# Check pool_fp key
|
|
1737
|
+
resp = await client.check_delete_key(CheckDeleteKey(uint32(pool_fp)))
|
|
1738
|
+
assert resp.fingerprint == pool_fp
|
|
1739
|
+
assert resp.used_for_farmer_rewards is False
|
|
1740
|
+
assert resp.used_for_pool_rewards is True
|
|
1741
|
+
|
|
1742
|
+
# Check unknown key
|
|
1743
|
+
resp = await client.check_delete_key(CheckDeleteKey(uint32(123456), uint16(10)))
|
|
1744
|
+
assert resp.fingerprint == 123456
|
|
1745
|
+
assert resp.used_for_farmer_rewards is False
|
|
1746
|
+
assert resp.used_for_pool_rewards is False
|
|
1747
|
+
|
|
1748
|
+
|
|
1749
|
+
@pytest.mark.anyio
|
|
1750
|
+
async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1751
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1752
|
+
|
|
1753
|
+
wallet: Wallet = env.wallet_1.wallet
|
|
1754
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1755
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1756
|
+
|
|
1757
|
+
address = await client.get_next_address(1, True)
|
|
1758
|
+
assert len(address) > 10
|
|
1759
|
+
|
|
1760
|
+
pks = (await client.get_public_keys()).pk_fingerprints
|
|
1761
|
+
assert len(pks) == 1
|
|
1762
|
+
|
|
1763
|
+
await generate_funds(env.full_node.api, env.wallet_1)
|
|
1764
|
+
|
|
1765
|
+
assert (await client.get_height_info()).height > 0
|
|
1766
|
+
|
|
1767
|
+
ph = await wallet.get_new_puzzlehash()
|
|
1768
|
+
addr = encode_puzzle_hash(ph, "txch")
|
|
1769
|
+
tx_amount = uint64(15600000)
|
|
1770
|
+
await env.full_node.api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
1771
|
+
created_tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
|
|
1772
|
+
|
|
1773
|
+
await time_out_assert(20, tx_in_mempool, True, client, created_tx.name)
|
|
1774
|
+
assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1
|
|
1775
|
+
await client.delete_unconfirmed_transactions(1)
|
|
1776
|
+
assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 0
|
|
1777
|
+
|
|
1778
|
+
sk_resp = await client.get_private_key(GetPrivateKey(pks[0]))
|
|
1779
|
+
assert sk_resp.private_key.fingerprint == pks[0]
|
|
1780
|
+
assert sk_resp.private_key.seed is not None
|
|
1781
|
+
|
|
1782
|
+
resp = await client.generate_mnemonic()
|
|
1783
|
+
assert len(resp.mnemonic) == 24
|
|
1784
|
+
|
|
1785
|
+
await client.add_key(AddKey(resp.mnemonic))
|
|
1786
|
+
|
|
1787
|
+
pks = (await client.get_public_keys()).pk_fingerprints
|
|
1788
|
+
assert len(pks) == 2
|
|
1789
|
+
|
|
1790
|
+
await client.log_in(LogIn(pks[1]))
|
|
1791
|
+
sk_resp = await client.get_private_key(GetPrivateKey(pks[1]))
|
|
1792
|
+
assert sk_resp.private_key.fingerprint == pks[1]
|
|
1793
|
+
|
|
1794
|
+
# test hardened keys
|
|
1795
|
+
await _check_delete_key(client=client, wallet_node=wallet_node, farmer_fp=pks[0], pool_fp=pks[1], observer=False)
|
|
1796
|
+
|
|
1797
|
+
# test observer keys
|
|
1798
|
+
await _check_delete_key(client=client, wallet_node=wallet_node, farmer_fp=pks[0], pool_fp=pks[1], observer=True)
|
|
1799
|
+
|
|
1800
|
+
# set farmer to empty string
|
|
1801
|
+
with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
|
|
1802
|
+
test_config["farmer"]["xch_target_address"] = ""
|
|
1803
|
+
save_config(wallet_node.root_path, "config.yaml", test_config)
|
|
1804
|
+
|
|
1805
|
+
# Check key
|
|
1806
|
+
delete_key_resp = await client.check_delete_key(CheckDeleteKey(pks[1]))
|
|
1807
|
+
assert delete_key_resp.fingerprint == pks[1]
|
|
1808
|
+
assert delete_key_resp.used_for_farmer_rewards is False
|
|
1809
|
+
assert delete_key_resp.used_for_pool_rewards is True
|
|
1810
|
+
|
|
1811
|
+
# set farmer and pool to empty string
|
|
1812
|
+
with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
|
|
1813
|
+
test_config["farmer"]["xch_target_address"] = ""
|
|
1814
|
+
test_config["pool"]["xch_target_address"] = ""
|
|
1815
|
+
save_config(wallet_node.root_path, "config.yaml", test_config)
|
|
1816
|
+
|
|
1817
|
+
# Check key
|
|
1818
|
+
delete_key_resp = await client.check_delete_key(CheckDeleteKey(pks[0]))
|
|
1819
|
+
assert delete_key_resp.fingerprint == pks[0]
|
|
1820
|
+
assert delete_key_resp.used_for_farmer_rewards is False
|
|
1821
|
+
assert delete_key_resp.used_for_pool_rewards is False
|
|
1822
|
+
|
|
1823
|
+
await client.delete_key(DeleteKey(pks[0]))
|
|
1824
|
+
await client.log_in(LogIn(uint32(pks[1])))
|
|
1825
|
+
assert len((await client.get_public_keys()).pk_fingerprints) == 1
|
|
1826
|
+
|
|
1827
|
+
assert not (await client.get_sync_status()).synced
|
|
1828
|
+
|
|
1829
|
+
wallets = await client.get_wallets()
|
|
1830
|
+
assert len(wallets) == 1
|
|
1831
|
+
assert await get_unconfirmed_balance(client, int(wallets[0]["id"])) == 0
|
|
1832
|
+
|
|
1833
|
+
with pytest.raises(ValueError):
|
|
1834
|
+
await client.send_transaction(wallets[0]["id"], uint64(100), addr, DEFAULT_TX_CONFIG)
|
|
1835
|
+
|
|
1836
|
+
# Delete all keys
|
|
1837
|
+
await client.delete_all_keys()
|
|
1838
|
+
assert len((await client.get_public_keys()).pk_fingerprints) == 0
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
@pytest.mark.anyio
|
|
1842
|
+
async def test_select_coins_rpc(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
1843
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1844
|
+
|
|
1845
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
1846
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1847
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
1848
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1849
|
+
client_2: WalletRpcClient = env.wallet_2.rpc_client
|
|
1850
|
+
|
|
1851
|
+
funds = await generate_funds(full_node_api, env.wallet_1)
|
|
1852
|
+
|
|
1853
|
+
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
|
|
1854
|
+
coin_300: list[Coin]
|
|
1855
|
+
tx_amounts: list[uint64] = [uint64(1000), uint64(300), uint64(1000), uint64(1000), uint64(10000)]
|
|
1856
|
+
for tx_amount in tx_amounts:
|
|
1857
|
+
funds -= tx_amount
|
|
1858
|
+
# create coins for tests
|
|
1859
|
+
tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
|
|
1860
|
+
spend_bundle = tx.spend_bundle
|
|
1861
|
+
assert spend_bundle is not None
|
|
1862
|
+
for coin in spend_bundle.additions():
|
|
1863
|
+
if coin.amount == uint64(300):
|
|
1864
|
+
coin_300 = [coin]
|
|
1865
|
+
|
|
1866
|
+
await time_out_assert(20, tx_in_mempool, True, client, tx.name)
|
|
1867
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
1868
|
+
await time_out_assert(20, get_confirmed_balance, funds, client, 1)
|
|
1869
|
+
|
|
1870
|
+
# test min coin amount
|
|
1871
|
+
min_coins: list[Coin] = await client_2.select_coins(
|
|
1872
|
+
amount=1000,
|
|
1873
|
+
wallet_id=1,
|
|
1874
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(min_coin_amount=uint64(1001)),
|
|
1875
|
+
)
|
|
1876
|
+
assert min_coins is not None
|
|
1877
|
+
assert len(min_coins) == 1 and min_coins[0].amount == uint64(10000)
|
|
1878
|
+
|
|
1879
|
+
# test max coin amount
|
|
1880
|
+
max_coins: list[Coin] = await client_2.select_coins(
|
|
1881
|
+
amount=2000,
|
|
1882
|
+
wallet_id=1,
|
|
1883
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
|
|
1884
|
+
min_coin_amount=uint64(999), max_coin_amount=uint64(9999)
|
|
1885
|
+
),
|
|
1886
|
+
)
|
|
1887
|
+
assert max_coins is not None
|
|
1888
|
+
assert len(max_coins) == 2 and max_coins[0].amount == uint64(1000)
|
|
1889
|
+
|
|
1890
|
+
# test excluded coin amounts
|
|
1891
|
+
non_1000_amt: int = sum(a for a in tx_amounts if a != 1000)
|
|
1892
|
+
excluded_amt_coins: list[Coin] = await client_2.select_coins(
|
|
1893
|
+
amount=non_1000_amt,
|
|
1894
|
+
wallet_id=1,
|
|
1895
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
|
|
1896
|
+
)
|
|
1897
|
+
assert excluded_amt_coins is not None
|
|
1898
|
+
assert (
|
|
1899
|
+
len(excluded_amt_coins) == len(tuple(a for a in tx_amounts if a != 1000))
|
|
1900
|
+
and sum(c.amount for c in excluded_amt_coins) == non_1000_amt
|
|
1901
|
+
)
|
|
1902
|
+
|
|
1903
|
+
# test excluded coins
|
|
1904
|
+
with pytest.raises(ValueError):
|
|
1905
|
+
await client_2.select_coins(
|
|
1906
|
+
amount=5000,
|
|
1907
|
+
wallet_id=1,
|
|
1908
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
|
|
1909
|
+
excluded_coin_ids=[c.name() for c in min_coins]
|
|
1910
|
+
),
|
|
1911
|
+
)
|
|
1912
|
+
excluded_test = await client_2.select_coins(
|
|
1913
|
+
amount=1300,
|
|
1914
|
+
wallet_id=1,
|
|
1915
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_ids=[c.name() for c in coin_300]),
|
|
1916
|
+
)
|
|
1917
|
+
assert len(excluded_test) == 2
|
|
1918
|
+
for coin in excluded_test:
|
|
1919
|
+
assert coin != coin_300[0]
|
|
1920
|
+
|
|
1921
|
+
# test backwards compatibility in the RPC
|
|
1922
|
+
identical_test = (
|
|
1923
|
+
await client_2.fetch(
|
|
1924
|
+
"select_coins",
|
|
1925
|
+
{
|
|
1926
|
+
"amount": 1300,
|
|
1927
|
+
"wallet_id": 1,
|
|
1928
|
+
"exclude_coins": [c.to_json_dict() for c in coin_300],
|
|
1929
|
+
},
|
|
1930
|
+
)
|
|
1931
|
+
)["coins"]
|
|
1932
|
+
assert len(identical_test) == 2
|
|
1933
|
+
for coin in identical_test:
|
|
1934
|
+
assert coin != coin_300[0]
|
|
1935
|
+
|
|
1936
|
+
# test get coins
|
|
1937
|
+
all_coins, _, _ = await client_2.get_spendable_coins(
|
|
1938
|
+
wallet_id=1,
|
|
1939
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
|
|
1940
|
+
excluded_coin_ids=[c.name() for c in excluded_amt_coins]
|
|
1941
|
+
),
|
|
1942
|
+
)
|
|
1943
|
+
assert set(excluded_amt_coins).intersection({rec.coin for rec in all_coins}) == set()
|
|
1944
|
+
all_coins, _, _ = await client_2.get_spendable_coins(
|
|
1945
|
+
wallet_id=1,
|
|
1946
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
|
|
1947
|
+
)
|
|
1948
|
+
assert len([rec for rec in all_coins if rec.coin.amount == 1000]) == 0
|
|
1949
|
+
all_coins_2, _, _ = await client_2.get_spendable_coins(
|
|
1950
|
+
wallet_id=1,
|
|
1951
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(max_coin_amount=uint64(999)),
|
|
1952
|
+
)
|
|
1953
|
+
assert all_coins_2[0].coin == coin_300[0]
|
|
1954
|
+
with pytest.raises(ValueError): # validate fail on invalid coin id.
|
|
1955
|
+
await client_2.get_spendable_coins(
|
|
1956
|
+
wallet_id=1,
|
|
1957
|
+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_ids=[b"a"]),
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1960
|
+
|
|
1961
|
+
@pytest.mark.anyio
|
|
1962
|
+
async def test_get_coin_records_rpc(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
|
|
1963
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
1964
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
1965
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
1966
|
+
store = wallet_node.wallet_state_manager.coin_store
|
|
1967
|
+
|
|
1968
|
+
for record in [record_1, record_2, record_3, record_4, record_5, record_6, record_7, record_8, record_9]:
|
|
1969
|
+
await store.add_coin_record(record)
|
|
1970
|
+
|
|
1971
|
+
async def run_test_case(
|
|
1972
|
+
test_case: str,
|
|
1973
|
+
test_request: GetCoinRecords,
|
|
1974
|
+
test_total_count: Optional[int],
|
|
1975
|
+
test_records: list[WalletCoinRecord],
|
|
1976
|
+
):
|
|
1977
|
+
response = await client.get_coin_records(test_request)
|
|
1978
|
+
assert response["coin_records"] == [coin.to_json_dict_parsed_metadata() for coin in test_records], test_case
|
|
1979
|
+
assert response["total_count"] == test_total_count, test_case
|
|
1980
|
+
|
|
1981
|
+
for name, tests in {
|
|
1982
|
+
"offset_limit": get_coin_records_offset_limit_tests,
|
|
1983
|
+
"wallet_id": get_coin_records_wallet_id_tests,
|
|
1984
|
+
"wallet_type": get_coin_records_wallet_type_tests,
|
|
1985
|
+
"coin_type": get_coin_records_coin_type_tests,
|
|
1986
|
+
"coin_id_filter": get_coin_records_coin_id_filter_tests,
|
|
1987
|
+
"puzzle_hash_filter": get_coin_records_puzzle_hash_filter_tests,
|
|
1988
|
+
"parent_coin_id_filter": get_coin_records_parent_coin_id_filter_tests,
|
|
1989
|
+
"amount_filter": get_coin_records_amount_filter_tests,
|
|
1990
|
+
"amount_range": get_coin_records_amount_range_tests,
|
|
1991
|
+
"confirmed_range": get_coin_records_confirmed_range_tests,
|
|
1992
|
+
"spent_range": get_coin_records_spent_range_tests,
|
|
1993
|
+
"order": get_coin_records_order_tests,
|
|
1994
|
+
"reverse": get_coin_records_reverse_tests,
|
|
1995
|
+
}.items():
|
|
1996
|
+
for i, (request, expected_records) in enumerate(tests):
|
|
1997
|
+
await run_test_case(f"{name}-{i}", request, None, expected_records)
|
|
1998
|
+
|
|
1999
|
+
for name, total_count_tests in {
|
|
2000
|
+
"total_count": get_coin_records_include_total_count_tests,
|
|
2001
|
+
"mixed": get_coin_records_mixed_tests,
|
|
2002
|
+
}.items():
|
|
2003
|
+
for i, (request, expected_total_count, expected_records) in enumerate(total_count_tests):
|
|
2004
|
+
await run_test_case(f"{name}-{i}", request, expected_total_count, expected_records)
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
@pytest.mark.anyio
|
|
2008
|
+
async def test_get_coin_records_rpc_limits(
|
|
2009
|
+
wallet_rpc_environment: WalletRpcTestEnvironment,
|
|
2010
|
+
seeded_random: random.Random,
|
|
2011
|
+
) -> None:
|
|
2012
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2013
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2014
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2015
|
+
rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
|
|
2016
|
+
assert rpc_server is not None
|
|
2017
|
+
api: WalletRpcApi = cast(WalletRpcApi, rpc_server.rpc_api)
|
|
2018
|
+
store = wallet_node.wallet_state_manager.coin_store
|
|
2019
|
+
|
|
2020
|
+
# Adjust the limits for faster testing
|
|
2021
|
+
WalletRpcApi.max_get_coin_records_limit = uint32(5)
|
|
2022
|
+
WalletRpcApi.max_get_coin_records_filter_items = uint32(5)
|
|
2023
|
+
|
|
2024
|
+
max_coins = api.max_get_coin_records_limit * 10
|
|
2025
|
+
coin_records = [
|
|
2026
|
+
WalletCoinRecord(
|
|
2027
|
+
Coin(bytes32.random(seeded_random), bytes32.random(seeded_random), uint64(seeded_random.randrange(2**64))),
|
|
2028
|
+
uint32(seeded_random.randrange(2**32)),
|
|
2029
|
+
uint32(0),
|
|
2030
|
+
False,
|
|
2031
|
+
False,
|
|
2032
|
+
WalletType.STANDARD_WALLET,
|
|
2033
|
+
uint32(0),
|
|
2034
|
+
CoinType.NORMAL,
|
|
2035
|
+
None,
|
|
2036
|
+
)
|
|
2037
|
+
for _ in range(max_coins)
|
|
2038
|
+
]
|
|
2039
|
+
for record in coin_records:
|
|
2040
|
+
await store.add_coin_record(record)
|
|
2041
|
+
|
|
2042
|
+
limit = api.max_get_coin_records_limit
|
|
2043
|
+
response_records = []
|
|
2044
|
+
for i in range(int(max_coins / api.max_get_coin_records_limit)):
|
|
2045
|
+
offset = uint32(api.max_get_coin_records_limit * i)
|
|
2046
|
+
response = await client.get_coin_records(GetCoinRecords(limit=limit, offset=offset, include_total_count=True))
|
|
2047
|
+
response_records.extend(list(response["coin_records"]))
|
|
2048
|
+
|
|
2049
|
+
assert len(response_records) == max_coins
|
|
2050
|
+
# Make sure we got all expected records
|
|
2051
|
+
parsed_records = [coin.to_json_dict_parsed_metadata() for coin in coin_records]
|
|
2052
|
+
for expected_record in parsed_records:
|
|
2053
|
+
assert expected_record in response_records
|
|
2054
|
+
|
|
2055
|
+
# Request coins with the max number of filter items
|
|
2056
|
+
max_filter_items = api.max_get_coin_records_filter_items
|
|
2057
|
+
filter_records = coin_records[:max_filter_items]
|
|
2058
|
+
coin_id_filter = HashFilter.include([coin.name() for coin in filter_records])
|
|
2059
|
+
puzzle_hash_filter = HashFilter.include([coin.coin.puzzle_hash for coin in filter_records])
|
|
2060
|
+
parent_coin_id_filter = HashFilter.include([coin.coin.parent_coin_info for coin in filter_records])
|
|
2061
|
+
amount_filter = AmountFilter.include([uint64(coin.coin.amount) for coin in coin_records[:max_filter_items]])
|
|
2062
|
+
for request in [
|
|
2063
|
+
GetCoinRecords(coin_id_filter=coin_id_filter),
|
|
2064
|
+
GetCoinRecords(puzzle_hash_filter=puzzle_hash_filter),
|
|
2065
|
+
GetCoinRecords(parent_coin_id_filter=parent_coin_id_filter),
|
|
2066
|
+
GetCoinRecords(amount_filter=amount_filter),
|
|
2067
|
+
GetCoinRecords(
|
|
2068
|
+
coin_id_filter=coin_id_filter,
|
|
2069
|
+
puzzle_hash_filter=puzzle_hash_filter,
|
|
2070
|
+
parent_coin_id_filter=parent_coin_id_filter,
|
|
2071
|
+
amount_filter=amount_filter,
|
|
2072
|
+
),
|
|
2073
|
+
]:
|
|
2074
|
+
response = await client.get_coin_records(request)
|
|
2075
|
+
parsed_records = [coin.to_json_dict_parsed_metadata() for coin in filter_records]
|
|
2076
|
+
for expected_record in parsed_records:
|
|
2077
|
+
assert expected_record in response["coin_records"]
|
|
2078
|
+
|
|
2079
|
+
|
|
2080
|
+
@pytest.mark.anyio
|
|
2081
|
+
async def test_get_coin_records_rpc_failures(
|
|
2082
|
+
wallet_rpc_environment: WalletRpcTestEnvironment,
|
|
2083
|
+
seeded_random: random.Random,
|
|
2084
|
+
) -> None:
|
|
2085
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2086
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2087
|
+
rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
|
|
2088
|
+
assert rpc_server is not None
|
|
2089
|
+
api = cast(WalletRpcApi, rpc_server.rpc_api)
|
|
2090
|
+
|
|
2091
|
+
too_many_hashes = [bytes32.random(seeded_random) for _ in range(api.max_get_coin_records_filter_items + 1)]
|
|
2092
|
+
too_many_amounts = [
|
|
2093
|
+
uint64(uint64(seeded_random.randrange(2**64))) for _ in range(api.max_get_coin_records_filter_items + 1)
|
|
2094
|
+
]
|
|
2095
|
+
# Run requests which exceeds the allowed limit and contain too much filter items
|
|
2096
|
+
for name, request in {
|
|
2097
|
+
"limit": GetCoinRecords(limit=uint32(api.max_get_coin_records_limit + 1)),
|
|
2098
|
+
"coin_id_filter": GetCoinRecords(coin_id_filter=HashFilter.include(too_many_hashes)),
|
|
2099
|
+
"puzzle_hash_filter": GetCoinRecords(puzzle_hash_filter=HashFilter.include(too_many_hashes)),
|
|
2100
|
+
"parent_coin_id_filter": GetCoinRecords(parent_coin_id_filter=HashFilter.include(too_many_hashes)),
|
|
2101
|
+
"amount_filter": GetCoinRecords(amount_filter=AmountFilter.include(too_many_amounts)),
|
|
2102
|
+
}.items():
|
|
2103
|
+
with pytest.raises(ValueError, match=name):
|
|
2104
|
+
await client.get_coin_records(request)
|
|
2105
|
+
|
|
2106
|
+
# Type validation is handled via `Streamable.from_json_dict` but the below should make at least sure it triggers.
|
|
2107
|
+
for field, value in {
|
|
2108
|
+
"offset": "invalid",
|
|
2109
|
+
"limit": "invalid",
|
|
2110
|
+
"wallet_id": "invalid",
|
|
2111
|
+
"wallet_type": 100,
|
|
2112
|
+
"coin_type": 100,
|
|
2113
|
+
"coin_id_filter": "invalid",
|
|
2114
|
+
"puzzle_hash_filter": "invalid",
|
|
2115
|
+
"parent_coin_id_filter": "invalid",
|
|
2116
|
+
"amount_filter": "invalid",
|
|
2117
|
+
"amount_range": "invalid",
|
|
2118
|
+
"confirmed_range": "invalid",
|
|
2119
|
+
"spent_range": "invalid",
|
|
2120
|
+
"order": 8,
|
|
2121
|
+
}.items():
|
|
2122
|
+
with pytest.raises((ConversionError, InvalidTypeError, ValueError)):
|
|
2123
|
+
json_dict = GetCoinRecords().to_json_dict()
|
|
2124
|
+
json_dict[field] = value
|
|
2125
|
+
await api.get_coin_records(json_dict)
|
|
2126
|
+
|
|
2127
|
+
|
|
2128
|
+
@pytest.mark.anyio
|
|
2129
|
+
async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2130
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2131
|
+
|
|
2132
|
+
wallet_2: Wallet = env.wallet_2.wallet
|
|
2133
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2134
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2135
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2136
|
+
client_2: WalletRpcClient = env.wallet_2.rpc_client
|
|
2137
|
+
|
|
2138
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2139
|
+
|
|
2140
|
+
env.wallet_2.node.config["enable_notifications"] = True
|
|
2141
|
+
env.wallet_2.node.config["required_notification_amount"] = 100000000000
|
|
2142
|
+
tx = await client.send_notification(
|
|
2143
|
+
await wallet_2.get_new_puzzlehash(),
|
|
2144
|
+
b"hello",
|
|
2145
|
+
uint64(100000000000),
|
|
2146
|
+
fee=uint64(100000000000),
|
|
2147
|
+
)
|
|
2148
|
+
|
|
2149
|
+
assert tx.spend_bundle is not None
|
|
2150
|
+
await time_out_assert(
|
|
2151
|
+
5,
|
|
2152
|
+
full_node_api.full_node.mempool_manager.get_spendbundle,
|
|
2153
|
+
tx.spend_bundle,
|
|
2154
|
+
tx.spend_bundle.name(),
|
|
2155
|
+
)
|
|
2156
|
+
await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
|
|
2157
|
+
await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(100000000000))
|
|
2158
|
+
|
|
2159
|
+
notification = (await client_2.get_notifications(GetNotifications())).notifications[0]
|
|
2160
|
+
assert [notification] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
|
|
2161
|
+
assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(0), uint32(0)))).notifications
|
|
2162
|
+
assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, uint32(1)))).notifications
|
|
2163
|
+
assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(1), None))).notifications
|
|
2164
|
+
assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, None))).notifications
|
|
2165
|
+
assert await client_2.delete_notifications()
|
|
2166
|
+
assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
|
|
2167
|
+
|
|
2168
|
+
tx = await client.send_notification(
|
|
2169
|
+
await wallet_2.get_new_puzzlehash(),
|
|
2170
|
+
b"hello",
|
|
2171
|
+
uint64(100000000000),
|
|
2172
|
+
fee=uint64(100000000000),
|
|
2173
|
+
)
|
|
2174
|
+
|
|
2175
|
+
assert tx.spend_bundle is not None
|
|
2176
|
+
await time_out_assert(
|
|
2177
|
+
5,
|
|
2178
|
+
full_node_api.full_node.mempool_manager.get_spendbundle,
|
|
2179
|
+
tx.spend_bundle,
|
|
2180
|
+
tx.spend_bundle.name(),
|
|
2181
|
+
)
|
|
2182
|
+
await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
|
|
2183
|
+
await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(200000000000))
|
|
2184
|
+
|
|
2185
|
+
notification = (await client_2.get_notifications(GetNotifications())).notifications[0]
|
|
2186
|
+
assert await client_2.delete_notifications([notification.id])
|
|
2187
|
+
assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
|
|
2188
|
+
|
|
2189
|
+
|
|
2190
|
+
# The signatures below were made from an ephemeral key pair that isn't included in the test code.
|
|
2191
|
+
# When modifying this test, any key can be used to generate signatures. Only the pubkey needs to
|
|
2192
|
+
# be included in the test code.
|
|
2193
|
+
#
|
|
2194
|
+
# Example 1:
|
|
2195
|
+
# $ chia keys generate
|
|
2196
|
+
# $ chia keys sign -d 'hello world' -t 'm/12381/8444/1/1'
|
|
2197
|
+
#
|
|
2198
|
+
# Example 2:
|
|
2199
|
+
# $ chia wallet get_address
|
|
2200
|
+
# xch1vk0dj7cx7d638h80mcuw70xqlnr56pmuhzajemn5ym02vhl3mzyqrrd4wp
|
|
2201
|
+
# $ chia wallet sign_message -m $(echo -n 'hello world' | xxd -p)
|
|
2202
|
+
# -a xch1vk0dj7cx7d638h80mcuw70xqlnr56pmuhzajemn5ym02vhl3mzyqrrd4wp
|
|
2203
|
+
#
|
|
2204
|
+
@pytest.mark.parametrize(
|
|
2205
|
+
["rpc_request", "rpc_response"],
|
|
2206
|
+
[
|
|
2207
|
+
# Valid signatures
|
|
2208
|
+
(
|
|
2209
|
+
# chia keys sign -d "Let's eat, Grandma" -t "m/12381/8444/1/1"
|
|
2210
|
+
{
|
|
2211
|
+
"message": "4c65742773206561742c204772616e646d61", # Let's eat, Grandma
|
|
2212
|
+
"pubkey": (
|
|
2213
|
+
"89d8e2a225c2ff543222bd0f2ba457a44acbdd147e4dfa02"
|
|
2214
|
+
"eadaef73eae49450dc708fd7c86800b60e8bc456e77563e4"
|
|
2215
|
+
),
|
|
2216
|
+
"signature": (
|
|
2217
|
+
"8006f63537563f038321eeda25f3838613d8f938e95f19d1d19ccbe634e9ee4d69552536aab08b4fe961305"
|
|
2218
|
+
"e534ffddf096199ae936b272dac88c936e8774bfc7a6f24025085026db3b7c3c41b472db3daf99b5e6cabf2"
|
|
2219
|
+
"6034d8782d10ef148d"
|
|
2220
|
+
),
|
|
2221
|
+
},
|
|
2222
|
+
VerifySignatureResponse(isValid=True),
|
|
2223
|
+
),
|
|
2224
|
+
(
|
|
2225
|
+
# chia wallet sign_message -m $(echo -n 'Happy happy joy joy' | xxd -p)
|
|
2226
|
+
# -a xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh
|
|
2227
|
+
{
|
|
2228
|
+
"message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
|
|
2229
|
+
"pubkey": (
|
|
2230
|
+
"8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
|
|
2231
|
+
"03a1df2dd477d622be928379625b759ef3b388b286ee8658"
|
|
2232
|
+
),
|
|
2233
|
+
"signature": (
|
|
2234
|
+
"a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
|
|
2235
|
+
"5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
|
|
2236
|
+
"91246eb1c7f1b66a6a"
|
|
2237
|
+
),
|
|
2238
|
+
"signing_mode": SigningMode.CHIP_0002.value,
|
|
2239
|
+
},
|
|
2240
|
+
VerifySignatureResponse(isValid=True),
|
|
2241
|
+
),
|
|
2242
|
+
(
|
|
2243
|
+
# chia wallet sign_message -m $(echo -n 'Happy happy joy joy' | xxd -p)
|
|
2244
|
+
# -a xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh
|
|
2245
|
+
{
|
|
2246
|
+
"message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
|
|
2247
|
+
"pubkey": (
|
|
2248
|
+
"8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
|
|
2249
|
+
"03a1df2dd477d622be928379625b759ef3b388b286ee8658"
|
|
2250
|
+
),
|
|
2251
|
+
"signature": (
|
|
2252
|
+
"a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
|
|
2253
|
+
"5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
|
|
2254
|
+
"91246eb1c7f1b66a6a"
|
|
2255
|
+
),
|
|
2256
|
+
"signing_mode": SigningMode.CHIP_0002.value,
|
|
2257
|
+
"address": "xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh",
|
|
2258
|
+
},
|
|
2259
|
+
VerifySignatureResponse(isValid=True),
|
|
2260
|
+
),
|
|
2261
|
+
(
|
|
2262
|
+
{
|
|
2263
|
+
"message": "4f7a6f6e65", # Ozone
|
|
2264
|
+
"pubkey": (
|
|
2265
|
+
"8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
|
|
2266
|
+
"24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
|
|
2267
|
+
),
|
|
2268
|
+
"signature": (
|
|
2269
|
+
"92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
|
|
2270
|
+
"c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
|
|
2271
|
+
"334981b974a6cebfd0"
|
|
2272
|
+
),
|
|
2273
|
+
"signing_mode": SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS.value,
|
|
2274
|
+
"address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
|
|
2275
|
+
},
|
|
2276
|
+
VerifySignatureResponse(isValid=True),
|
|
2277
|
+
),
|
|
2278
|
+
# Negative tests
|
|
2279
|
+
(
|
|
2280
|
+
# Message was modified
|
|
2281
|
+
{
|
|
2282
|
+
"message": "4c6574277320656174204772616e646d61", # Let's eat Grandma
|
|
2283
|
+
"pubkey": (
|
|
2284
|
+
"89d8e2a225c2ff543222bd0f2ba457a44acbdd147e4dfa02"
|
|
2285
|
+
"eadaef73eae49450dc708fd7c86800b60e8bc456e77563e4"
|
|
2286
|
+
),
|
|
2287
|
+
"signature": (
|
|
2288
|
+
"8006f63537563f038321eeda25f3838613d8f938e95f19d1d19ccbe634e9ee4d69552536aab08b4fe961305"
|
|
2289
|
+
"e534ffddf096199ae936b272dac88c936e8774bfc7a6f24025085026db3b7c3c41b472db3daf99b5e6cabf2"
|
|
2290
|
+
"6034d8782d10ef148d"
|
|
2291
|
+
),
|
|
2292
|
+
},
|
|
2293
|
+
VerifySignatureResponse(isValid=False, error="Signature is invalid."),
|
|
2294
|
+
),
|
|
2295
|
+
(
|
|
2296
|
+
# Valid signature but address doesn't match pubkey
|
|
2297
|
+
{
|
|
2298
|
+
"message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
|
|
2299
|
+
"pubkey": (
|
|
2300
|
+
"8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
|
|
2301
|
+
"03a1df2dd477d622be928379625b759ef3b388b286ee8658"
|
|
2302
|
+
),
|
|
2303
|
+
"signature": (
|
|
2304
|
+
"a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
|
|
2305
|
+
"5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
|
|
2306
|
+
"91246eb1c7f1b66a6a"
|
|
2307
|
+
),
|
|
2308
|
+
"signing_mode": SigningMode.CHIP_0002.value,
|
|
2309
|
+
"address": "xch1d0rekc2javy5gpruzmcnk4e4qq834jzlvxt5tcgl2ylt49t26gdsjen7t0",
|
|
2310
|
+
},
|
|
2311
|
+
VerifySignatureResponse(isValid=False, error="Public key doesn't match the address"),
|
|
2312
|
+
),
|
|
2313
|
+
(
|
|
2314
|
+
{
|
|
2315
|
+
"message": "4f7a6f6e65", # Ozone
|
|
2316
|
+
"pubkey": (
|
|
2317
|
+
"8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
|
|
2318
|
+
"24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
|
|
2319
|
+
),
|
|
2320
|
+
"signature": (
|
|
2321
|
+
"92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
|
|
2322
|
+
"c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
|
|
2323
|
+
"334981b974a6cebfd0"
|
|
2324
|
+
),
|
|
2325
|
+
"address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
|
|
2326
|
+
},
|
|
2327
|
+
VerifySignatureResponse(isValid=False, error="Public key doesn't match the address"),
|
|
2328
|
+
),
|
|
2329
|
+
],
|
|
2330
|
+
)
|
|
2331
|
+
@pytest.mark.parametrize("prefix_hex_strings", [True, False], ids=["with 0x", "no 0x"])
|
|
2332
|
+
@pytest.mark.anyio
|
|
2333
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2334
|
+
async def test_verify_signature(
|
|
2335
|
+
wallet_rpc_environment: WalletRpcTestEnvironment,
|
|
2336
|
+
rpc_request: dict[str, Any],
|
|
2337
|
+
rpc_response: dict[str, Any],
|
|
2338
|
+
prefix_hex_strings: bool,
|
|
2339
|
+
):
|
|
2340
|
+
rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
|
|
2341
|
+
assert rpc_server is not None
|
|
2342
|
+
updated_request = rpc_request.copy()
|
|
2343
|
+
updated_request["pubkey"] = ("0x" if prefix_hex_strings else "") + updated_request["pubkey"]
|
|
2344
|
+
updated_request["signature"] = ("0x" if prefix_hex_strings else "") + updated_request["signature"]
|
|
2345
|
+
res = await wallet_rpc_environment.wallet_1.rpc_client.verify_signature(
|
|
2346
|
+
VerifySignature.from_json_dict(updated_request)
|
|
2347
|
+
)
|
|
2348
|
+
assert res == rpc_response
|
|
2349
|
+
|
|
2350
|
+
|
|
2351
|
+
@pytest.mark.anyio
|
|
2352
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2353
|
+
async def test_set_auto_claim(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2354
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2355
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2356
|
+
rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
|
|
2357
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2358
|
+
assert rpc_server is not None
|
|
2359
|
+
api: WalletRpcApi = cast(WalletRpcApi, rpc_server.rpc_api)
|
|
2360
|
+
req = {"enabled": False, "tx_fee": -1, "min_amount": 100}
|
|
2361
|
+
has_exception = False
|
|
2362
|
+
try:
|
|
2363
|
+
# Manually using API to test error condition
|
|
2364
|
+
await api.set_auto_claim(req)
|
|
2365
|
+
except ConversionError:
|
|
2366
|
+
has_exception = True
|
|
2367
|
+
assert has_exception
|
|
2368
|
+
req = {"enabled": False, "batch_size": 0, "min_amount": 100}
|
|
2369
|
+
res = await env.wallet_1.rpc_client.set_auto_claim(
|
|
2370
|
+
AutoClaimSettings(enabled=False, batch_size=uint16(0), min_amount=uint64(100))
|
|
2371
|
+
)
|
|
2372
|
+
assert not res.enabled
|
|
2373
|
+
assert res.tx_fee == 0
|
|
2374
|
+
assert res.min_amount == 100
|
|
2375
|
+
assert res.batch_size == 50
|
|
2376
|
+
|
|
2377
|
+
|
|
2378
|
+
@pytest.mark.anyio
|
|
2379
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2380
|
+
async def test_get_auto_claim(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2381
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2382
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2383
|
+
rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
|
|
2384
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2385
|
+
assert rpc_server is not None
|
|
2386
|
+
res = await env.wallet_1.rpc_client.get_auto_claim()
|
|
2387
|
+
assert not res.enabled
|
|
2388
|
+
assert res.tx_fee == 0
|
|
2389
|
+
assert res.min_amount == 0
|
|
2390
|
+
assert res.batch_size == 50
|
|
2391
|
+
|
|
2392
|
+
|
|
2393
|
+
@pytest.mark.anyio
|
|
2394
|
+
async def test_set_wallet_resync_on_startup(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2395
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2396
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2397
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2398
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2399
|
+
wc = env.wallet_1.rpc_client
|
|
2400
|
+
await wc.create_new_did_wallet(1, DEFAULT_TX_CONFIG, 0)
|
|
2401
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
2402
|
+
await farm_transaction_block(full_node_api, env.wallet_1.node)
|
|
2403
|
+
await time_out_assert(20, check_client_synced, True, wc)
|
|
2404
|
+
|
|
2405
|
+
nft_wallet = await wc.create_new_nft_wallet(None)
|
|
2406
|
+
nft_wallet_id = nft_wallet["wallet_id"]
|
|
2407
|
+
address = await wc.get_next_address(env.wallet_1.wallet.id(), True)
|
|
2408
|
+
await wc.mint_nft(
|
|
2409
|
+
nft_wallet_id,
|
|
2410
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
2411
|
+
royalty_address=address,
|
|
2412
|
+
target_address=address,
|
|
2413
|
+
hash="deadbeef",
|
|
2414
|
+
uris=["http://test.nft"],
|
|
2415
|
+
)
|
|
2416
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
|
|
2417
|
+
await farm_transaction_block(full_node_api, env.wallet_1.node)
|
|
2418
|
+
await time_out_assert(20, check_client_synced, True, wc)
|
|
2419
|
+
|
|
2420
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2421
|
+
wallet_node_2: WalletNode = env.wallet_2.node
|
|
2422
|
+
# Test Clawback resync
|
|
2423
|
+
tx = (
|
|
2424
|
+
await wc.send_transaction(
|
|
2425
|
+
wallet_id=1,
|
|
2426
|
+
amount=uint64(500),
|
|
2427
|
+
address=address,
|
|
2428
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
2429
|
+
fee=uint64(0),
|
|
2430
|
+
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
|
|
2431
|
+
)
|
|
2432
|
+
).transaction
|
|
2433
|
+
clawback_coin_id = tx.additions[0].name()
|
|
2434
|
+
assert tx.spend_bundle is not None
|
|
2435
|
+
await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
|
|
2436
|
+
await time_out_assert(20, check_client_synced, True, wc)
|
|
2437
|
+
await asyncio.sleep(10)
|
|
2438
|
+
resp = await wc.spend_clawback_coins([clawback_coin_id], 0)
|
|
2439
|
+
assert resp["success"]
|
|
2440
|
+
assert len(resp["transaction_ids"]) == 1
|
|
2441
|
+
await time_out_assert_not_none(
|
|
2442
|
+
10, full_node_api.full_node.mempool_manager.get_spendbundle, bytes32.from_hexstr(resp["transaction_ids"][0])
|
|
2443
|
+
)
|
|
2444
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
2445
|
+
await time_out_assert(20, check_client_synced, True, wc)
|
|
2446
|
+
wallet_node_2._close()
|
|
2447
|
+
await wallet_node_2._await_closed()
|
|
2448
|
+
# set flag to reset wallet sync data on start
|
|
2449
|
+
await client.set_wallet_resync_on_startup(SetWalletResyncOnStartup())
|
|
2450
|
+
fingerprint = wallet_node.logged_in_fingerprint
|
|
2451
|
+
assert wallet_node._wallet_state_manager
|
|
2452
|
+
# 2 reward coins, 1 DID, 1 NFT, 1 clawbacked coin
|
|
2453
|
+
assert len(await wallet_node._wallet_state_manager.coin_store.get_all_unspent_coins()) == 5
|
|
2454
|
+
assert await wallet_node._wallet_state_manager.nft_store.count() == 1
|
|
2455
|
+
# standard wallet, did wallet, nft wallet, did nft wallet
|
|
2456
|
+
assert len(await wallet_node.wallet_state_manager.user_store.get_all_wallet_info_entries()) == 4
|
|
2457
|
+
before_txs = await wallet_node.wallet_state_manager.tx_store.get_all_transactions()
|
|
2458
|
+
wallet_node._close()
|
|
2459
|
+
await wallet_node._await_closed()
|
|
2460
|
+
config = load_config(wallet_node.root_path, "config.yaml")
|
|
2461
|
+
# check that flag was set in config file
|
|
2462
|
+
assert config["wallet"]["reset_sync_for_fingerprint"] == fingerprint
|
|
2463
|
+
new_config = wallet_node.config.copy()
|
|
2464
|
+
new_config["reset_sync_for_fingerprint"] = config["wallet"]["reset_sync_for_fingerprint"]
|
|
2465
|
+
wallet_node_2.config = new_config
|
|
2466
|
+
wallet_node_2.root_path = wallet_node.root_path
|
|
2467
|
+
wallet_node_2.local_keychain = wallet_node.local_keychain
|
|
2468
|
+
# use second node to start the same wallet, reusing config and db
|
|
2469
|
+
await wallet_node_2._start_with_fingerprint(fingerprint)
|
|
2470
|
+
assert wallet_node_2._wallet_state_manager
|
|
2471
|
+
after_txs = await wallet_node_2.wallet_state_manager.tx_store.get_all_transactions()
|
|
2472
|
+
# transactions should be the same
|
|
2473
|
+
assert after_txs == before_txs
|
|
2474
|
+
# Check clawback
|
|
2475
|
+
clawback_tx = await wallet_node_2.wallet_state_manager.tx_store.get_transaction_record(clawback_coin_id)
|
|
2476
|
+
assert clawback_tx is not None
|
|
2477
|
+
assert clawback_tx.confirmed
|
|
2478
|
+
# only coin_store was populated in this case, but now should be empty
|
|
2479
|
+
assert len(await wallet_node_2._wallet_state_manager.coin_store.get_all_unspent_coins()) == 0
|
|
2480
|
+
assert await wallet_node_2._wallet_state_manager.nft_store.count() == 0
|
|
2481
|
+
# we don't delete wallets
|
|
2482
|
+
assert len(await wallet_node_2.wallet_state_manager.user_store.get_all_wallet_info_entries()) == 4
|
|
2483
|
+
updated_config = load_config(wallet_node.root_path, "config.yaml")
|
|
2484
|
+
# check that it's disabled after reset
|
|
2485
|
+
assert updated_config["wallet"].get("reset_sync_for_fingerprint") is None
|
|
2486
|
+
wallet_node_2._close()
|
|
2487
|
+
await wallet_node_2._await_closed()
|
|
2488
|
+
|
|
2489
|
+
|
|
2490
|
+
@pytest.mark.anyio
|
|
2491
|
+
async def test_set_wallet_resync_on_startup_disable(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2492
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2493
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2494
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2495
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2496
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2497
|
+
wallet_node_2: WalletNode = env.wallet_2.node
|
|
2498
|
+
wallet_node_2._close()
|
|
2499
|
+
await wallet_node_2._await_closed()
|
|
2500
|
+
# set flag to reset wallet sync data on start
|
|
2501
|
+
await client.set_wallet_resync_on_startup(SetWalletResyncOnStartup())
|
|
2502
|
+
fingerprint = wallet_node.logged_in_fingerprint
|
|
2503
|
+
assert wallet_node._wallet_state_manager
|
|
2504
|
+
assert len(await wallet_node._wallet_state_manager.coin_store.get_all_unspent_coins()) == 2
|
|
2505
|
+
before_txs = await wallet_node.wallet_state_manager.tx_store.get_all_transactions()
|
|
2506
|
+
await client.set_wallet_resync_on_startup(SetWalletResyncOnStartup(False))
|
|
2507
|
+
wallet_node._close()
|
|
2508
|
+
await wallet_node._await_closed()
|
|
2509
|
+
config = load_config(wallet_node.root_path, "config.yaml")
|
|
2510
|
+
# check that flag was set in config file
|
|
2511
|
+
assert config["wallet"].get("reset_sync_for_fingerprint") is None
|
|
2512
|
+
new_config = wallet_node.config.copy()
|
|
2513
|
+
new_config["reset_sync_for_fingerprint"] = config["wallet"].get("reset_sync_for_fingerprint")
|
|
2514
|
+
wallet_node_2.config = new_config
|
|
2515
|
+
wallet_node_2.root_path = wallet_node.root_path
|
|
2516
|
+
wallet_node_2.local_keychain = wallet_node.local_keychain
|
|
2517
|
+
# use second node to start the same wallet, reusing config and db
|
|
2518
|
+
await wallet_node_2._start_with_fingerprint(fingerprint)
|
|
2519
|
+
assert wallet_node_2._wallet_state_manager
|
|
2520
|
+
after_txs = await wallet_node_2.wallet_state_manager.tx_store.get_all_transactions()
|
|
2521
|
+
# transactions should be the same
|
|
2522
|
+
assert after_txs == before_txs
|
|
2523
|
+
# only coin_store was populated in this case, but now should be empty
|
|
2524
|
+
assert len(await wallet_node_2._wallet_state_manager.coin_store.get_all_unspent_coins()) == 2
|
|
2525
|
+
wallet_node_2._close()
|
|
2526
|
+
await wallet_node_2._await_closed()
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
@pytest.mark.anyio
|
|
2530
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2531
|
+
async def test_set_wallet_resync_schema(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2532
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2533
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2534
|
+
await generate_funds(full_node_api, env.wallet_1)
|
|
2535
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2536
|
+
fingerprint = wallet_node.logged_in_fingerprint
|
|
2537
|
+
assert fingerprint
|
|
2538
|
+
db_path = wallet_node.wallet_state_manager.db_path
|
|
2539
|
+
assert await wallet_node.reset_sync_db(
|
|
2540
|
+
db_path, fingerprint
|
|
2541
|
+
), "Schema has been changed, reset sync db won't work, please update WalletNode.reset_sync_db function"
|
|
2542
|
+
dbw: DBWrapper2 = wallet_node.wallet_state_manager.db_wrapper
|
|
2543
|
+
conn: aiosqlite.Connection
|
|
2544
|
+
async with dbw.writer() as conn:
|
|
2545
|
+
await conn.execute("CREATE TABLE blah(temp int)")
|
|
2546
|
+
await wallet_node.reset_sync_db(db_path, fingerprint)
|
|
2547
|
+
assert (
|
|
2548
|
+
len(list(await conn.execute_fetchall("SELECT name FROM sqlite_master WHERE type='table' AND name='blah'"))) == 0
|
|
2549
|
+
)
|
|
2550
|
+
|
|
2551
|
+
|
|
2552
|
+
@pytest.mark.anyio
|
|
2553
|
+
async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2554
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2555
|
+
|
|
2556
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2557
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2558
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2559
|
+
full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
|
|
2560
|
+
|
|
2561
|
+
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
2562
|
+
|
|
2563
|
+
# Send to a CAT with an anyone can spend TAIL
|
|
2564
|
+
our_ph: bytes32 = await env.wallet_1.wallet.get_new_puzzlehash()
|
|
2565
|
+
cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), Program.to(1))
|
|
2566
|
+
addr = encode_puzzle_hash(
|
|
2567
|
+
cat_puzzle.get_tree_hash(),
|
|
2568
|
+
"txch",
|
|
2569
|
+
)
|
|
2570
|
+
tx_amount = uint64(100)
|
|
2571
|
+
|
|
2572
|
+
tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
|
|
2573
|
+
transaction_id = tx.name
|
|
2574
|
+
spend_bundle = tx.spend_bundle
|
|
2575
|
+
assert spend_bundle is not None
|
|
2576
|
+
|
|
2577
|
+
await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
|
|
2578
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
2579
|
+
|
|
2580
|
+
# Do the eve spend back to our wallet
|
|
2581
|
+
cat_coin = next(c for c in spend_bundle.additions() if c.amount == tx_amount)
|
|
2582
|
+
eve_spend = WalletSpendBundle(
|
|
2583
|
+
[
|
|
2584
|
+
make_spend(
|
|
2585
|
+
cat_coin,
|
|
2586
|
+
cat_puzzle,
|
|
2587
|
+
Program.to(
|
|
2588
|
+
[
|
|
2589
|
+
Program.to([[51, our_ph, tx_amount, [our_ph]], [51, None, -113, None, None]]),
|
|
2590
|
+
None,
|
|
2591
|
+
cat_coin.name(),
|
|
2592
|
+
coin_as_list(cat_coin),
|
|
2593
|
+
[cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_coin.amount],
|
|
2594
|
+
0,
|
|
2595
|
+
0,
|
|
2596
|
+
]
|
|
2597
|
+
),
|
|
2598
|
+
)
|
|
2599
|
+
],
|
|
2600
|
+
G2Element(),
|
|
2601
|
+
)
|
|
2602
|
+
await full_node_rpc.push_tx(eve_spend)
|
|
2603
|
+
await farm_transaction(full_node_api, wallet_node, eve_spend)
|
|
2604
|
+
|
|
2605
|
+
# Make sure we have the CAT
|
|
2606
|
+
res = await client.create_wallet_for_existing_cat(Program.to(None).get_tree_hash())
|
|
2607
|
+
assert res["success"]
|
|
2608
|
+
cat_wallet_id = res["wallet_id"]
|
|
2609
|
+
await time_out_assert(20, get_confirmed_balance, tx_amount, client, cat_wallet_id)
|
|
2610
|
+
|
|
2611
|
+
# Attempt to melt it fully
|
|
2612
|
+
tx = (
|
|
2613
|
+
await client.cat_spend(
|
|
2614
|
+
cat_wallet_id,
|
|
2615
|
+
amount=uint64(0),
|
|
2616
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
2617
|
+
inner_address=encode_puzzle_hash(our_ph, "txch"),
|
|
2618
|
+
cat_discrepancy=(tx_amount * -1, Program.to(None), Program.to(None)),
|
|
2619
|
+
)
|
|
2620
|
+
).transaction
|
|
2621
|
+
transaction_id = tx.name
|
|
2622
|
+
spend_bundle = tx.spend_bundle
|
|
2623
|
+
assert spend_bundle is not None
|
|
2624
|
+
|
|
2625
|
+
await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
|
|
2626
|
+
await farm_transaction(full_node_api, wallet_node, spend_bundle)
|
|
2627
|
+
|
|
2628
|
+
await time_out_assert(20, get_confirmed_balance, 0, client, cat_wallet_id)
|
|
2629
|
+
|
|
2630
|
+
|
|
2631
|
+
@pytest.mark.anyio
|
|
2632
|
+
async def test_get_balances(wallet_rpc_environment: WalletRpcTestEnvironment):
|
|
2633
|
+
env: WalletRpcTestEnvironment = wallet_rpc_environment
|
|
2634
|
+
|
|
2635
|
+
client: WalletRpcClient = env.wallet_1.rpc_client
|
|
2636
|
+
wallet_node: WalletNode = env.wallet_1.node
|
|
2637
|
+
|
|
2638
|
+
full_node_api: FullNodeSimulator = env.full_node.api
|
|
2639
|
+
|
|
2640
|
+
await generate_funds(full_node_api, env.wallet_1, 1)
|
|
2641
|
+
|
|
2642
|
+
await time_out_assert(20, check_client_synced, True, client)
|
|
2643
|
+
# Creates a CAT wallet with 100 mojos and a CAT with 20 mojos
|
|
2644
|
+
await client.create_new_cat_and_wallet(uint64(100), test=True)
|
|
2645
|
+
|
|
2646
|
+
await time_out_assert(20, check_client_synced, True, client)
|
|
2647
|
+
res = await client.create_new_cat_and_wallet(uint64(20), test=True)
|
|
2648
|
+
assert res["success"]
|
|
2649
|
+
await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
|
|
2650
|
+
await farm_transaction_block(full_node_api, wallet_node)
|
|
2651
|
+
await time_out_assert(20, check_client_synced, True, client)
|
|
2652
|
+
bal = await client.get_wallet_balances()
|
|
2653
|
+
assert len(bal) == 3
|
|
2654
|
+
assert bal["1"]["confirmed_wallet_balance"] == 1999999999880
|
|
2655
|
+
assert bal["2"]["confirmed_wallet_balance"] == 100
|
|
2656
|
+
assert bal["3"]["confirmed_wallet_balance"] == 20
|
|
2657
|
+
bal_ids = await client.get_wallet_balances([3, 2])
|
|
2658
|
+
assert len(bal_ids) == 2
|
|
2659
|
+
assert bal["2"]["confirmed_wallet_balance"] == 100
|
|
2660
|
+
assert bal["3"]["confirmed_wallet_balance"] == 20
|
|
2661
|
+
|
|
2662
|
+
|
|
2663
|
+
@pytest.mark.parametrize(
|
|
2664
|
+
"wallet_environments",
|
|
2665
|
+
[
|
|
2666
|
+
{
|
|
2667
|
+
"num_environments": 1,
|
|
2668
|
+
"blocks_needed": [1],
|
|
2669
|
+
}
|
|
2670
|
+
],
|
|
2671
|
+
indirect=True,
|
|
2672
|
+
)
|
|
2673
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2674
|
+
@pytest.mark.anyio
|
|
2675
|
+
async def test_split_coins(wallet_environments: WalletTestFramework, capsys: pytest.CaptureFixture[str]) -> None:
|
|
2676
|
+
env = wallet_environments.environments[0]
|
|
2677
|
+
env.wallet_aliases = {
|
|
2678
|
+
"xch": 1,
|
|
2679
|
+
"cat": 2,
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
# Test XCH first
|
|
2683
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
|
|
2684
|
+
target_coin = next(iter(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope)))
|
|
2685
|
+
assert target_coin.amount == 250_000_000_000
|
|
2686
|
+
|
|
2687
|
+
xch_request = SplitCMD(
|
|
2688
|
+
**{
|
|
2689
|
+
**wallet_environments.cmd_tx_endpoint_args(env),
|
|
2690
|
+
**dict(
|
|
2691
|
+
id=env.wallet_aliases["xch"],
|
|
2692
|
+
number_of_coins=100,
|
|
2693
|
+
amount_per_coin=CliAmount(amount=uint64(100), mojos=True),
|
|
2694
|
+
target_coin_id=target_coin.name(),
|
|
2695
|
+
fee=uint64(1_000_000_000_000), # 1 XCH
|
|
2696
|
+
push=True,
|
|
2697
|
+
),
|
|
2698
|
+
}
|
|
2699
|
+
)
|
|
2700
|
+
|
|
2701
|
+
with pytest.raises(ResponseFailureError, match="501 coins is greater then the maximum limit of 500 coins"):
|
|
2702
|
+
await dataclasses.replace(xch_request, number_of_coins=501).run()
|
|
2703
|
+
|
|
2704
|
+
with pytest.raises(ResponseFailureError, match="Could not find coin with ID 00000000000000000"):
|
|
2705
|
+
await dataclasses.replace(xch_request, target_coin_id=bytes32.zeros).run()
|
|
2706
|
+
|
|
2707
|
+
with pytest.raises(ResponseFailureError, match="is less than the total amount of the split"):
|
|
2708
|
+
await dataclasses.replace(
|
|
2709
|
+
xch_request, amount_per_coin=CliAmount(amount=uint64(1_000_000_000_000), mojos=True)
|
|
2710
|
+
).run()
|
|
2711
|
+
|
|
2712
|
+
# We catch this one
|
|
2713
|
+
capsys.readouterr()
|
|
2714
|
+
await dataclasses.replace(xch_request, id=50).run()
|
|
2715
|
+
output = (capsys.readouterr()).out
|
|
2716
|
+
assert "Wallet id: 50 not found" in output
|
|
2717
|
+
|
|
2718
|
+
# This one only "works" on the RPC
|
|
2719
|
+
env.wallet_state_manager.wallets[uint32(42)] = object() # type: ignore[assignment]
|
|
2720
|
+
with pytest.raises(ResponseFailureError, match="Cannot split coins from non-fungible wallet types"):
|
|
2721
|
+
assert xch_request.amount_per_coin is not None # hey there mypy
|
|
2722
|
+
rpc_request = SplitCoins(
|
|
2723
|
+
wallet_id=uint32(42),
|
|
2724
|
+
number_of_coins=uint16(xch_request.number_of_coins),
|
|
2725
|
+
amount_per_coin=xch_request.amount_per_coin.convert_amount(1),
|
|
2726
|
+
target_coin_id=xch_request.target_coin_id,
|
|
2727
|
+
fee=xch_request.fee,
|
|
2728
|
+
push=xch_request.push,
|
|
2729
|
+
)
|
|
2730
|
+
await env.rpc_client.split_coins(rpc_request, wallet_environments.tx_config)
|
|
2731
|
+
|
|
2732
|
+
del env.wallet_state_manager.wallets[uint32(42)]
|
|
2733
|
+
|
|
2734
|
+
await dataclasses.replace(xch_request, number_of_coins=0).run()
|
|
2735
|
+
output = (capsys.readouterr()).out
|
|
2736
|
+
assert "Transaction sent" not in output
|
|
2737
|
+
|
|
2738
|
+
with wallet_environments.new_puzzle_hashes_allowed():
|
|
2739
|
+
await xch_request.run()
|
|
2740
|
+
|
|
2741
|
+
await wallet_environments.process_pending_states(
|
|
2742
|
+
[
|
|
2743
|
+
WalletStateTransition(
|
|
2744
|
+
pre_block_balance_updates={
|
|
2745
|
+
"xch": {
|
|
2746
|
+
"unconfirmed_wallet_balance": -1_000_000_000_000, # just the fee
|
|
2747
|
+
"spendable_balance": -2_000_000_000_000,
|
|
2748
|
+
"pending_change": 1_000_000_000_000,
|
|
2749
|
+
"max_send_amount": -2_000_000_000_000,
|
|
2750
|
+
"pending_coin_removal_count": 2,
|
|
2751
|
+
}
|
|
2752
|
+
},
|
|
2753
|
+
post_block_balance_updates={
|
|
2754
|
+
"xch": {
|
|
2755
|
+
"confirmed_wallet_balance": -1_000_000_000_000, # just the fee
|
|
2756
|
+
"spendable_balance": 1_000_000_000_000,
|
|
2757
|
+
"pending_change": -1_000_000_000_000,
|
|
2758
|
+
"max_send_amount": 1_000_000_000_000,
|
|
2759
|
+
"pending_coin_removal_count": -2,
|
|
2760
|
+
"unspent_coin_count": 99, # split 1 into 100 i.e. +99
|
|
2761
|
+
}
|
|
2762
|
+
},
|
|
2763
|
+
)
|
|
2764
|
+
]
|
|
2765
|
+
)
|
|
2766
|
+
|
|
2767
|
+
# Now do CATs
|
|
2768
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
|
|
2769
|
+
cat_wallet = await CATWallet.create_new_cat_wallet(
|
|
2770
|
+
env.wallet_state_manager,
|
|
2771
|
+
env.xch_wallet,
|
|
2772
|
+
{"identifier": "genesis_by_id"},
|
|
2773
|
+
uint64(50),
|
|
2774
|
+
action_scope,
|
|
2775
|
+
)
|
|
2776
|
+
|
|
2777
|
+
await wallet_environments.process_pending_states(
|
|
2778
|
+
[
|
|
2779
|
+
WalletStateTransition(
|
|
2780
|
+
# no need to test this, it is tested elsewhere
|
|
2781
|
+
pre_block_balance_updates={
|
|
2782
|
+
"xch": {"set_remainder": True},
|
|
2783
|
+
"cat": {"init": True, "set_remainder": True},
|
|
2784
|
+
},
|
|
2785
|
+
post_block_balance_updates={
|
|
2786
|
+
"xch": {"set_remainder": True},
|
|
2787
|
+
"cat": {"set_remainder": True},
|
|
2788
|
+
},
|
|
2789
|
+
)
|
|
2790
|
+
]
|
|
2791
|
+
)
|
|
2792
|
+
|
|
2793
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
|
|
2794
|
+
target_coin = next(iter(await cat_wallet.select_coins(uint64(50), action_scope)))
|
|
2795
|
+
assert target_coin.amount == 50
|
|
2796
|
+
|
|
2797
|
+
cat_request = SplitCMD(
|
|
2798
|
+
**{
|
|
2799
|
+
**wallet_environments.cmd_tx_endpoint_args(env),
|
|
2800
|
+
**dict(
|
|
2801
|
+
id=env.wallet_aliases["cat"],
|
|
2802
|
+
number_of_coins=50,
|
|
2803
|
+
amount_per_coin=CliAmount(amount=uint64(1), mojos=True),
|
|
2804
|
+
target_coin_id=target_coin.name(),
|
|
2805
|
+
push=True,
|
|
2806
|
+
),
|
|
2807
|
+
}
|
|
2808
|
+
)
|
|
2809
|
+
|
|
2810
|
+
with wallet_environments.new_puzzle_hashes_allowed():
|
|
2811
|
+
await dataclasses.replace(cat_request).run()
|
|
2812
|
+
|
|
2813
|
+
await wallet_environments.process_pending_states(
|
|
2814
|
+
[
|
|
2815
|
+
WalletStateTransition(
|
|
2816
|
+
pre_block_balance_updates={
|
|
2817
|
+
"cat": {
|
|
2818
|
+
"unconfirmed_wallet_balance": 0,
|
|
2819
|
+
"spendable_balance": -50,
|
|
2820
|
+
"pending_change": 50,
|
|
2821
|
+
"max_send_amount": -50,
|
|
2822
|
+
"pending_coin_removal_count": 1,
|
|
2823
|
+
}
|
|
2824
|
+
},
|
|
2825
|
+
post_block_balance_updates={
|
|
2826
|
+
"cat": {
|
|
2827
|
+
"confirmed_wallet_balance": 0,
|
|
2828
|
+
"spendable_balance": 50,
|
|
2829
|
+
"pending_change": -50,
|
|
2830
|
+
"max_send_amount": 50,
|
|
2831
|
+
"pending_coin_removal_count": -1,
|
|
2832
|
+
"unspent_coin_count": 49, # split 1 into 50 i.e. +49
|
|
2833
|
+
}
|
|
2834
|
+
},
|
|
2835
|
+
)
|
|
2836
|
+
]
|
|
2837
|
+
)
|
|
2838
|
+
|
|
2839
|
+
# Test a not synced error
|
|
2840
|
+
assert xch_request.rpc_info.client_info is not None
|
|
2841
|
+
|
|
2842
|
+
async def not_synced() -> GetSyncStatusResponse:
|
|
2843
|
+
return GetSyncStatusResponse(False, False)
|
|
2844
|
+
|
|
2845
|
+
xch_request.rpc_info.client_info.client.get_sync_status = not_synced # type: ignore[method-assign]
|
|
2846
|
+
await xch_request.run()
|
|
2847
|
+
output = (capsys.readouterr()).out
|
|
2848
|
+
assert "Wallet not synced. Please wait." in output
|
|
2849
|
+
|
|
2850
|
+
|
|
2851
|
+
@pytest.mark.parametrize(
|
|
2852
|
+
"wallet_environments",
|
|
2853
|
+
[
|
|
2854
|
+
{
|
|
2855
|
+
"num_environments": 1,
|
|
2856
|
+
"blocks_needed": [2],
|
|
2857
|
+
}
|
|
2858
|
+
],
|
|
2859
|
+
indirect=True,
|
|
2860
|
+
)
|
|
2861
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
2862
|
+
@pytest.mark.anyio
|
|
2863
|
+
async def test_combine_coins(wallet_environments: WalletTestFramework, capsys: pytest.CaptureFixture[str]) -> None:
|
|
2864
|
+
env = wallet_environments.environments[0]
|
|
2865
|
+
env.wallet_aliases = {
|
|
2866
|
+
"xch": 1,
|
|
2867
|
+
"cat": 2,
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
# Should have 4 coins, two 1.75 XCH, two 0.25 XCH
|
|
2871
|
+
|
|
2872
|
+
# Grab one of the 0.25 ones to specify
|
|
2873
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
|
|
2874
|
+
target_coin = next(iter(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope)))
|
|
2875
|
+
assert target_coin.amount == 250_000_000_000
|
|
2876
|
+
|
|
2877
|
+
# These parameters will give us the maximum amount of behavior coverage
|
|
2878
|
+
# - More amount than the coin we specify
|
|
2879
|
+
# - Less amount than will have to be selected in order create it
|
|
2880
|
+
# - Higher # coins than necessary to create it
|
|
2881
|
+
fee = uint64(100)
|
|
2882
|
+
xch_combine_request = CombineCMD(
|
|
2883
|
+
**{
|
|
2884
|
+
**wallet_environments.cmd_tx_endpoint_args(env),
|
|
2885
|
+
**dict(
|
|
2886
|
+
id=env.wallet_aliases["xch"],
|
|
2887
|
+
target_amount=CliAmount(amount=uint64(1_000_000_000_000), mojos=True),
|
|
2888
|
+
number_of_coins=uint16(3),
|
|
2889
|
+
input_coins=(target_coin.name(),),
|
|
2890
|
+
fee=fee,
|
|
2891
|
+
push=True,
|
|
2892
|
+
),
|
|
2893
|
+
}
|
|
2894
|
+
)
|
|
2895
|
+
|
|
2896
|
+
# Test some error cases first
|
|
2897
|
+
with pytest.raises(ResponseFailureError, match="greater then the maximum limit"):
|
|
2898
|
+
await dataclasses.replace(xch_combine_request, number_of_coins=uint16(501)).run()
|
|
2899
|
+
|
|
2900
|
+
with pytest.raises(ResponseFailureError, match="You need at least two coins to combine"):
|
|
2901
|
+
await dataclasses.replace(xch_combine_request, number_of_coins=uint16(0)).run()
|
|
2902
|
+
|
|
2903
|
+
with pytest.raises(ResponseFailureError, match="More coin IDs specified than desired number of coins to combine"):
|
|
2904
|
+
await dataclasses.replace(xch_combine_request, input_coins=(bytes32.zeros,) * 100).run()
|
|
2905
|
+
|
|
2906
|
+
# We catch this one
|
|
2907
|
+
capsys.readouterr()
|
|
2908
|
+
await dataclasses.replace(xch_combine_request, id=50).run()
|
|
2909
|
+
output = (capsys.readouterr()).out
|
|
2910
|
+
assert "Wallet id: 50 not found" in output
|
|
2911
|
+
|
|
2912
|
+
# This one only "works" on the RPC
|
|
2913
|
+
env.wallet_state_manager.wallets[uint32(42)] = object() # type: ignore[assignment]
|
|
2914
|
+
with pytest.raises(ResponseFailureError, match="Cannot combine coins from non-fungible wallet types"):
|
|
2915
|
+
assert xch_combine_request.target_amount is not None # hey there mypy
|
|
2916
|
+
rpc_request = CombineCoins(
|
|
2917
|
+
wallet_id=uint32(42),
|
|
2918
|
+
target_coin_amount=xch_combine_request.target_amount.convert_amount(1),
|
|
2919
|
+
number_of_coins=uint16(xch_combine_request.number_of_coins),
|
|
2920
|
+
target_coin_ids=list(xch_combine_request.input_coins),
|
|
2921
|
+
fee=xch_combine_request.fee,
|
|
2922
|
+
push=xch_combine_request.push,
|
|
2923
|
+
)
|
|
2924
|
+
await env.rpc_client.combine_coins(rpc_request, wallet_environments.tx_config)
|
|
2925
|
+
|
|
2926
|
+
del env.wallet_state_manager.wallets[uint32(42)]
|
|
2927
|
+
|
|
2928
|
+
# Now push the request
|
|
2929
|
+
with patch("sys.stdin", new=io.StringIO("y\n")):
|
|
2930
|
+
await xch_combine_request.run()
|
|
2931
|
+
|
|
2932
|
+
await wallet_environments.process_pending_states(
|
|
2933
|
+
[
|
|
2934
|
+
WalletStateTransition(
|
|
2935
|
+
pre_block_balance_updates={
|
|
2936
|
+
"xch": {
|
|
2937
|
+
"unconfirmed_wallet_balance": -fee,
|
|
2938
|
+
"spendable_balance": -2_250_000_000_000,
|
|
2939
|
+
"pending_change": 2_250_000_000_000 - fee,
|
|
2940
|
+
"max_send_amount": -2_250_000_000_000,
|
|
2941
|
+
"pending_coin_removal_count": 3,
|
|
2942
|
+
}
|
|
2943
|
+
},
|
|
2944
|
+
post_block_balance_updates={
|
|
2945
|
+
"xch": {
|
|
2946
|
+
"confirmed_wallet_balance": -fee,
|
|
2947
|
+
"spendable_balance": 2_250_000_000_000 - fee,
|
|
2948
|
+
"pending_change": -(2_250_000_000_000 - fee),
|
|
2949
|
+
"max_send_amount": 2_250_000_000_000 - fee,
|
|
2950
|
+
"pending_coin_removal_count": -3,
|
|
2951
|
+
"unspent_coin_count": -1, # combine 3 into 1 + change
|
|
2952
|
+
}
|
|
2953
|
+
},
|
|
2954
|
+
)
|
|
2955
|
+
]
|
|
2956
|
+
)
|
|
2957
|
+
|
|
2958
|
+
# Now do CATs
|
|
2959
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
|
|
2960
|
+
cat_wallet = await CATWallet.create_new_cat_wallet(
|
|
2961
|
+
env.wallet_state_manager,
|
|
2962
|
+
env.xch_wallet,
|
|
2963
|
+
{"identifier": "genesis_by_id"},
|
|
2964
|
+
uint64(50),
|
|
2965
|
+
action_scope,
|
|
2966
|
+
)
|
|
2967
|
+
|
|
2968
|
+
await wallet_environments.process_pending_states(
|
|
2969
|
+
[
|
|
2970
|
+
WalletStateTransition(
|
|
2971
|
+
# no need to test this, it is tested elsewhere
|
|
2972
|
+
pre_block_balance_updates={
|
|
2973
|
+
"xch": {"set_remainder": True},
|
|
2974
|
+
"cat": {"init": True, "set_remainder": True},
|
|
2975
|
+
},
|
|
2976
|
+
post_block_balance_updates={
|
|
2977
|
+
"xch": {"set_remainder": True},
|
|
2978
|
+
"cat": {"set_remainder": True},
|
|
2979
|
+
},
|
|
2980
|
+
)
|
|
2981
|
+
]
|
|
2982
|
+
)
|
|
2983
|
+
|
|
2984
|
+
BIG_COIN_AMOUNT = uint64(30)
|
|
2985
|
+
SMALL_COIN_AMOUNT = uint64(15)
|
|
2986
|
+
REALLY_SMALL_COIN_AMOUNT = uint64(5)
|
|
2987
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
|
|
2988
|
+
await cat_wallet.generate_signed_transaction(
|
|
2989
|
+
[BIG_COIN_AMOUNT, SMALL_COIN_AMOUNT, REALLY_SMALL_COIN_AMOUNT],
|
|
2990
|
+
[await env.xch_wallet.get_puzzle_hash(new=not action_scope.config.tx_config.reuse_puzhash)] * 3,
|
|
2991
|
+
action_scope,
|
|
2992
|
+
)
|
|
2993
|
+
|
|
2994
|
+
await wallet_environments.process_pending_states(
|
|
2995
|
+
[
|
|
2996
|
+
WalletStateTransition(
|
|
2997
|
+
# no need to test this, it is tested elsewhere
|
|
2998
|
+
pre_block_balance_updates={
|
|
2999
|
+
"xch": {"set_remainder": True},
|
|
3000
|
+
"cat": {"init": True, "set_remainder": True},
|
|
3001
|
+
},
|
|
3002
|
+
post_block_balance_updates={
|
|
3003
|
+
"xch": {"set_remainder": True},
|
|
3004
|
+
"cat": {"set_remainder": True},
|
|
3005
|
+
},
|
|
3006
|
+
)
|
|
3007
|
+
]
|
|
3008
|
+
)
|
|
3009
|
+
|
|
3010
|
+
# We're going to test that we select the two smaller coins
|
|
3011
|
+
cat_combine_request = CombineCMD(
|
|
3012
|
+
**{
|
|
3013
|
+
**wallet_environments.cmd_tx_endpoint_args(env),
|
|
3014
|
+
**dict(
|
|
3015
|
+
id=env.wallet_aliases["cat"],
|
|
3016
|
+
target_amount=None,
|
|
3017
|
+
number_of_coins=uint16(2),
|
|
3018
|
+
input_coins=(),
|
|
3019
|
+
largest_first=False,
|
|
3020
|
+
fee=fee,
|
|
3021
|
+
push=True,
|
|
3022
|
+
),
|
|
3023
|
+
}
|
|
3024
|
+
)
|
|
3025
|
+
|
|
3026
|
+
with patch("sys.stdin", new=io.StringIO("y\n")):
|
|
3027
|
+
await cat_combine_request.run()
|
|
3028
|
+
|
|
3029
|
+
await wallet_environments.process_pending_states(
|
|
3030
|
+
[
|
|
3031
|
+
WalletStateTransition(
|
|
3032
|
+
pre_block_balance_updates={
|
|
3033
|
+
"xch": {
|
|
3034
|
+
"unconfirmed_wallet_balance": -fee,
|
|
3035
|
+
"set_remainder": True, # We only really care that a fee was in fact attached
|
|
3036
|
+
},
|
|
3037
|
+
"cat": {
|
|
3038
|
+
"spendable_balance": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
|
|
3039
|
+
"pending_change": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
|
|
3040
|
+
"max_send_amount": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
|
|
3041
|
+
"pending_coin_removal_count": 2,
|
|
3042
|
+
},
|
|
3043
|
+
},
|
|
3044
|
+
post_block_balance_updates={
|
|
3045
|
+
"xch": {
|
|
3046
|
+
"confirmed_wallet_balance": -fee,
|
|
3047
|
+
"set_remainder": True, # We only really care that a fee was in fact attached
|
|
3048
|
+
},
|
|
3049
|
+
"cat": {
|
|
3050
|
+
"spendable_balance": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
|
|
3051
|
+
"pending_change": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
|
|
3052
|
+
"max_send_amount": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
|
|
3053
|
+
"pending_coin_removal_count": -2,
|
|
3054
|
+
"unspent_coin_count": -1,
|
|
3055
|
+
},
|
|
3056
|
+
},
|
|
3057
|
+
)
|
|
3058
|
+
]
|
|
3059
|
+
)
|
|
3060
|
+
|
|
3061
|
+
# Test a not synced error
|
|
3062
|
+
assert xch_combine_request.rpc_info.client_info is not None
|
|
3063
|
+
|
|
3064
|
+
async def not_synced() -> GetSyncStatusResponse:
|
|
3065
|
+
return GetSyncStatusResponse(False, False)
|
|
3066
|
+
|
|
3067
|
+
xch_combine_request.rpc_info.client_info.client.get_sync_status = not_synced # type: ignore[method-assign]
|
|
3068
|
+
await xch_combine_request.run()
|
|
3069
|
+
output = (capsys.readouterr()).out
|
|
3070
|
+
assert "Wallet not synced. Please wait." in output
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
@pytest.mark.parametrize(
|
|
3074
|
+
"wallet_environments",
|
|
3075
|
+
[
|
|
3076
|
+
{
|
|
3077
|
+
"num_environments": 1,
|
|
3078
|
+
"blocks_needed": [2],
|
|
3079
|
+
"trusted": True, # irrelevant
|
|
3080
|
+
"reuse_puzhash": True, # irrelevant
|
|
3081
|
+
}
|
|
3082
|
+
],
|
|
3083
|
+
indirect=True,
|
|
3084
|
+
)
|
|
3085
|
+
@pytest.mark.limit_consensus_modes(reason="irrelevant")
|
|
3086
|
+
@pytest.mark.anyio
|
|
3087
|
+
async def test_fee_bigger_than_selection_coin_combining(wallet_environments: WalletTestFramework) -> None:
|
|
3088
|
+
"""
|
|
3089
|
+
This tests the case where the coins we would otherwise select are not enough to pay the fee.
|
|
3090
|
+
"""
|
|
3091
|
+
|
|
3092
|
+
env = wallet_environments.environments[0]
|
|
3093
|
+
env.wallet_aliases = {
|
|
3094
|
+
"xch": 1,
|
|
3095
|
+
"cat": 2,
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
# Should have 4 coins, two 1.75 XCH, two 0.25 XCH
|
|
3099
|
+
|
|
3100
|
+
# Grab one of the 0.25 ones to specify
|
|
3101
|
+
async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
|
|
3102
|
+
target_coin = next(iter(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope)))
|
|
3103
|
+
assert target_coin.amount == 250_000_000_000
|
|
3104
|
+
|
|
3105
|
+
fee = uint64(1_750_000_000_000)
|
|
3106
|
+
# Under standard circumstances we would select the small coins, but this is not enough to pay the fee
|
|
3107
|
+
# Instead, we will grab the big coin first and combine it with one of the smaller coins
|
|
3108
|
+
xch_combine_request = CombineCMD(
|
|
3109
|
+
**{
|
|
3110
|
+
**wallet_environments.cmd_tx_endpoint_args(env),
|
|
3111
|
+
**dict(
|
|
3112
|
+
id=env.wallet_aliases["xch"],
|
|
3113
|
+
number_of_coins=uint16(2),
|
|
3114
|
+
input_coins=(),
|
|
3115
|
+
fee=fee,
|
|
3116
|
+
push=True,
|
|
3117
|
+
largest_first=False,
|
|
3118
|
+
),
|
|
3119
|
+
}
|
|
3120
|
+
)
|
|
3121
|
+
|
|
3122
|
+
# First test an error where fee selection causes too many coins to be selected
|
|
3123
|
+
with pytest.raises(ResponseFailureError, match="without selecting more coins than specified: 3"):
|
|
3124
|
+
await dataclasses.replace(xch_combine_request, fee=uint64(2_250_000_000_000)).run()
|
|
3125
|
+
|
|
3126
|
+
with patch("sys.stdin", new=io.StringIO("y\n")):
|
|
3127
|
+
await xch_combine_request.run()
|
|
3128
|
+
|
|
3129
|
+
await wallet_environments.process_pending_states(
|
|
3130
|
+
[
|
|
3131
|
+
WalletStateTransition(
|
|
3132
|
+
pre_block_balance_updates={
|
|
3133
|
+
"xch": {
|
|
3134
|
+
"unconfirmed_wallet_balance": -fee,
|
|
3135
|
+
"spendable_balance": -2_000_000_000_000,
|
|
3136
|
+
"pending_change": 250_000_000_000,
|
|
3137
|
+
"max_send_amount": -2_000_000_000_000,
|
|
3138
|
+
"pending_coin_removal_count": 2,
|
|
3139
|
+
}
|
|
3140
|
+
},
|
|
3141
|
+
post_block_balance_updates={
|
|
3142
|
+
"xch": {
|
|
3143
|
+
"confirmed_wallet_balance": -fee,
|
|
3144
|
+
"spendable_balance": 250_000_000_000,
|
|
3145
|
+
"pending_change": -250_000_000_000,
|
|
3146
|
+
"max_send_amount": 250_000_000_000,
|
|
3147
|
+
"pending_coin_removal_count": -2,
|
|
3148
|
+
"unspent_coin_count": -1, # combine 2 into 1
|
|
3149
|
+
}
|
|
3150
|
+
},
|
|
3151
|
+
)
|
|
3152
|
+
]
|
|
3153
|
+
)
|