chia-blockchain 2.5.4rc2__py3-none-any.whl → 2.5.5rc1__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/_tests/blockchain/blockchain_test_utils.py +2 -3
- chia/_tests/blockchain/test_augmented_chain.py +2 -3
- chia/_tests/blockchain/test_blockchain.py +261 -44
- chia/_tests/blockchain/test_blockchain_transactions.py +4 -3
- chia/_tests/blockchain/test_build_chains.py +197 -1
- chia/_tests/blockchain/test_get_block_generator.py +1 -1
- chia/_tests/blockchain/test_lookup_fork_chain.py +1 -1
- chia/_tests/clvm/benchmark_costs.py +1 -1
- chia/_tests/clvm/coin_store.py +3 -4
- chia/_tests/clvm/test_message_conditions.py +2 -2
- chia/_tests/clvm/test_puzzle_compression.py +2 -3
- chia/_tests/clvm/test_puzzles.py +1 -2
- chia/_tests/clvm/test_singletons.py +2 -3
- chia/_tests/clvm/test_spend_sim.py +7 -7
- chia/_tests/cmds/cmd_test_utils.py +30 -25
- chia/_tests/cmds/test_dev_gh.py +1 -1
- chia/_tests/cmds/test_farm_cmd.py +1 -1
- chia/_tests/cmds/test_show.py +1 -2
- chia/_tests/cmds/wallet/test_did.py +101 -56
- chia/_tests/cmds/wallet/test_nft.py +109 -84
- chia/_tests/cmds/wallet/test_notifications.py +1 -1
- chia/_tests/cmds/wallet/test_offer.toffer +1 -1
- chia/_tests/cmds/wallet/test_vcs.py +8 -8
- chia/_tests/cmds/wallet/test_wallet.py +100 -46
- chia/_tests/conftest.py +31 -20
- chia/_tests/connection_utils.py +1 -1
- chia/_tests/core/consensus/stores/__init__.py +0 -0
- chia/_tests/core/consensus/stores/test_coin_store_protocol.py +40 -0
- chia/_tests/core/consensus/test_block_creation.py +2 -31
- chia/_tests/core/consensus/test_pot_iterations.py +38 -3
- chia/_tests/core/custom_types/test_proof_of_space.py +154 -26
- chia/_tests/core/custom_types/test_spend_bundle.py +2 -3
- chia/_tests/core/daemon/test_daemon.py +80 -0
- chia/_tests/core/data_layer/test_data_layer.py +1 -1
- chia/_tests/core/data_layer/test_data_layer_util.py +1 -1
- chia/_tests/core/data_layer/test_data_rpc.py +14 -10
- chia/_tests/core/data_layer/test_data_store.py +5 -5
- chia/_tests/core/farmer/test_farmer_api.py +2 -2
- chia/_tests/core/full_node/full_sync/test_full_sync.py +446 -406
- chia/_tests/core/full_node/ram_db.py +3 -1
- chia/_tests/core/full_node/stores/test_block_store.py +28 -16
- chia/_tests/core/full_node/stores/test_coin_store.py +277 -185
- chia/_tests/core/full_node/stores/test_full_node_store.py +11 -4
- chia/_tests/core/full_node/stores/test_hint_store.py +2 -2
- chia/_tests/core/full_node/test_address_manager.py +200 -27
- chia/_tests/core/full_node/test_block_height_map.py +2 -2
- chia/_tests/core/full_node/test_conditions.py +7 -6
- chia/_tests/core/full_node/test_full_node.py +456 -40
- chia/_tests/core/full_node/test_generator_tools.py +32 -2
- chia/_tests/core/full_node/test_hint_management.py +1 -1
- chia/_tests/core/full_node/test_node_load.py +20 -21
- chia/_tests/core/full_node/test_performance.py +3 -4
- chia/_tests/core/full_node/test_prev_tx_block.py +43 -0
- chia/_tests/core/full_node/test_subscriptions.py +1 -2
- chia/_tests/core/full_node/test_transactions.py +9 -5
- chia/_tests/core/full_node/test_tx_processing_queue.py +1 -2
- chia/_tests/core/large_block.py +1 -2
- chia/_tests/core/make_block_generator.py +3 -4
- chia/_tests/core/mempool/test_mempool.py +36 -86
- chia/_tests/core/mempool/test_mempool_fee_estimator.py +1 -1
- chia/_tests/core/mempool/test_mempool_item_queries.py +1 -3
- chia/_tests/core/mempool/test_mempool_manager.py +421 -69
- chia/_tests/core/mempool/test_mempool_performance.py +3 -2
- chia/_tests/core/mempool/test_singleton_fast_forward.py +60 -131
- chia/_tests/core/server/flood.py +1 -1
- chia/_tests/core/server/test_dos.py +1 -1
- chia/_tests/core/server/test_node_discovery.py +41 -27
- chia/_tests/core/server/test_rate_limits.py +1 -1
- chia/_tests/core/server/test_server.py +1 -1
- chia/_tests/core/services/test_services.py +5 -5
- chia/_tests/core/ssl/test_ssl.py +1 -1
- chia/_tests/core/test_cost_calculation.py +6 -6
- chia/_tests/core/test_crawler.py +2 -2
- chia/_tests/core/test_crawler_rpc.py +1 -1
- chia/_tests/core/test_db_conversion.py +3 -1
- chia/_tests/core/test_db_validation.py +5 -3
- chia/_tests/core/test_farmer_harvester_rpc.py +15 -15
- chia/_tests/core/test_filter.py +4 -1
- chia/_tests/core/test_full_node_rpc.py +99 -82
- chia/_tests/core/test_program.py +2 -2
- chia/_tests/core/util/test_block_cache.py +1 -1
- chia/_tests/core/util/test_keychain.py +2 -2
- chia/_tests/core/util/test_lockfile.py +1 -1
- chia/_tests/core/util/test_log_exceptions.py +5 -5
- chia/_tests/core/util/test_streamable.py +81 -22
- chia/_tests/db/test_db_wrapper.py +1 -3
- chia/_tests/environments/wallet.py +5 -5
- chia/_tests/farmer_harvester/test_farmer.py +9 -7
- chia/_tests/farmer_harvester/test_farmer_harvester.py +11 -4
- chia/_tests/farmer_harvester/test_filter_prefix_bits.py +6 -5
- chia/_tests/farmer_harvester/test_third_party_harvesters.py +15 -9
- chia/_tests/fee_estimation/test_fee_estimation_integration.py +1 -2
- chia/_tests/fee_estimation/test_fee_estimation_rpc.py +7 -5
- chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +1 -1
- chia/_tests/generator/test_compression.py +1 -2
- chia/_tests/generator/test_rom.py +8 -4
- chia/_tests/plot_sync/test_plot_sync.py +3 -3
- chia/_tests/plot_sync/test_receiver.py +3 -3
- chia/_tests/plot_sync/test_sender.py +1 -1
- chia/_tests/plot_sync/test_sync_simulated.py +3 -3
- chia/_tests/plot_sync/util.py +2 -2
- chia/_tests/pools/test_pool_cmdline.py +48 -21
- chia/_tests/pools/test_pool_puzzles_lifecycle.py +2 -3
- chia/_tests/pools/test_pool_rpc.py +237 -105
- chia/_tests/pools/test_pool_wallet.py +11 -2
- chia/_tests/pools/test_wallet_pool_store.py +5 -4
- chia/_tests/rpc/test_rpc_client.py +1 -1
- chia/_tests/simulation/test_simulation.py +13 -8
- chia/_tests/simulation/test_simulator.py +2 -2
- chia/_tests/timelord/test_new_peak.py +191 -47
- chia/_tests/timelord/test_timelord.py +1 -1
- chia/_tests/tools/test_full_sync.py +0 -2
- chia/_tests/tools/test_run_block.py +3 -1
- chia/_tests/util/benchmark_cost.py +3 -3
- chia/_tests/util/benchmarks.py +2 -2
- chia/_tests/util/blockchain.py +11 -5
- chia/_tests/util/blockchain_mock.py +1 -4
- chia/_tests/util/coin_store.py +29 -0
- chia/_tests/util/constants.py +2 -18
- chia/_tests/util/full_sync.py +3 -3
- chia/_tests/util/generator_tools_testing.py +2 -3
- chia/_tests/util/key_tool.py +2 -3
- chia/_tests/util/misc.py +33 -31
- chia/_tests/util/network_protocol_data.py +19 -17
- chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
- chia/_tests/util/protocol_messages_json.py +3 -1
- chia/_tests/util/run_block.py +2 -2
- chia/_tests/util/setup_nodes.py +7 -7
- chia/_tests/util/spend_sim.py +47 -55
- chia/_tests/util/test_condition_tools.py +5 -4
- chia/_tests/util/test_config.py +2 -2
- chia/_tests/util/test_dump_keyring.py +1 -1
- chia/_tests/util/test_full_block_utils.py +12 -14
- chia/_tests/util/test_misc.py +2 -2
- chia/_tests/util/test_paginator.py +4 -4
- chia/_tests/util/test_priority_mutex.py +2 -2
- chia/_tests/util/test_replace_str_to_bytes.py +15 -5
- chia/_tests/util/test_ssl_check.py +1 -1
- chia/_tests/util/test_testnet_overrides.py +13 -3
- chia/_tests/util/time_out_assert.py +4 -2
- chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +1 -1
- chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +1 -2
- chia/_tests/wallet/cat_wallet/test_cat_wallet.py +352 -432
- chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +3 -6
- chia/_tests/wallet/cat_wallet/test_trades.py +53 -77
- chia/_tests/wallet/clawback/test_clawback_decorator.py +3 -1
- chia/_tests/wallet/clawback/test_clawback_lifecycle.py +3 -3
- chia/_tests/wallet/clawback/test_clawback_metadata.py +4 -2
- chia/_tests/wallet/conftest.py +11 -12
- chia/_tests/wallet/db_wallet/test_db_graftroot.py +11 -4
- chia/_tests/wallet/db_wallet/test_dl_offers.py +433 -130
- chia/_tests/wallet/db_wallet/test_dl_wallet.py +3 -3
- chia/_tests/wallet/did_wallet/test_did.py +2132 -2000
- chia/_tests/wallet/nft_wallet/config.py +1 -1
- chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1610 -742
- chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +486 -907
- chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +4 -4
- chia/_tests/wallet/nft_wallet/test_nft_wallet.py +517 -294
- chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +133 -62
- chia/_tests/wallet/rpc/test_wallet_rpc.py +305 -184
- chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +10 -6
- chia/_tests/wallet/sync/test_wallet_sync.py +89 -60
- chia/_tests/wallet/test_clvm_casts.py +88 -0
- chia/_tests/wallet/test_coin_management.py +1 -1
- chia/_tests/wallet/test_coin_selection.py +1 -1
- chia/_tests/wallet/test_conditions.py +1 -1
- chia/_tests/wallet/test_new_wallet_protocol.py +13 -11
- chia/_tests/wallet/test_notifications.py +5 -3
- chia/_tests/wallet/test_sign_coin_spends.py +6 -6
- chia/_tests/wallet/test_signer_protocol.py +13 -12
- chia/_tests/wallet/test_singleton.py +1 -1
- chia/_tests/wallet/test_singleton_lifecycle_fast.py +5 -7
- chia/_tests/wallet/test_util.py +2 -2
- chia/_tests/wallet/test_wallet.py +108 -29
- chia/_tests/wallet/test_wallet_action_scope.py +9 -2
- chia/_tests/wallet/test_wallet_blockchain.py +2 -3
- chia/_tests/wallet/test_wallet_key_val_store.py +1 -2
- chia/_tests/wallet/test_wallet_node.py +2 -4
- chia/_tests/wallet/test_wallet_retry.py +4 -2
- chia/_tests/wallet/test_wallet_state_manager.py +191 -5
- chia/_tests/wallet/test_wallet_test_framework.py +1 -1
- chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +8 -8
- chia/_tests/wallet/vc_wallet/test_vc_wallet.py +29 -12
- chia/_tests/wallet/wallet_block_tools.py +6 -6
- chia/_tests/weight_proof/test_weight_proof.py +10 -48
- chia/apis.py +1 -1
- chia/cmds/beta.py +1 -1
- chia/cmds/chia.py +9 -9
- chia/cmds/cmd_classes.py +12 -11
- chia/cmds/cmd_helpers.py +1 -1
- chia/cmds/cmds_util.py +12 -9
- chia/cmds/coin_funcs.py +2 -2
- chia/cmds/configure.py +2 -2
- chia/cmds/data.py +0 -2
- chia/cmds/data_funcs.py +1 -1
- chia/cmds/db_validate_func.py +1 -2
- chia/cmds/dev/__init__.py +0 -0
- chia/cmds/dev/data.py +273 -0
- chia/cmds/{gh.py → dev/gh.py} +5 -5
- chia/cmds/dev/main.py +22 -0
- chia/cmds/dev/mempool.py +78 -0
- chia/cmds/dev/mempool_funcs.py +63 -0
- chia/cmds/farm_funcs.py +5 -4
- chia/cmds/init_funcs.py +11 -11
- chia/cmds/keys.py +2 -2
- chia/cmds/keys_funcs.py +4 -4
- chia/cmds/netspace_funcs.py +1 -1
- chia/cmds/peer_funcs.py +2 -2
- chia/cmds/plotnft_funcs.py +72 -26
- chia/cmds/rpc.py +1 -1
- chia/cmds/show_funcs.py +5 -5
- chia/cmds/signer.py +8 -7
- chia/cmds/sim_funcs.py +8 -9
- chia/cmds/wallet.py +2 -2
- chia/cmds/wallet_funcs.py +165 -131
- chia/{util → consensus}/augmented_chain.py +1 -2
- chia/consensus/block_body_validation.py +54 -40
- chia/consensus/block_creation.py +42 -76
- chia/consensus/block_header_validation.py +32 -26
- chia/consensus/block_record.py +0 -3
- chia/consensus/blockchain.py +23 -32
- chia/consensus/blockchain_interface.py +1 -5
- chia/consensus/check_time_locks.py +57 -0
- chia/consensus/coin_store_protocol.py +151 -0
- chia/consensus/coinbase.py +0 -6
- chia/consensus/condition_costs.py +4 -0
- chia/{util → consensus}/condition_tools.py +4 -5
- chia/consensus/cost_calculator.py +1 -1
- chia/consensus/default_constants.py +32 -9
- chia/consensus/deficit.py +1 -3
- chia/consensus/difficulty_adjustment.py +1 -2
- chia/consensus/find_fork_point.py +1 -3
- chia/consensus/full_block_to_block_record.py +1 -6
- chia/{util → consensus}/generator_tools.py +1 -3
- chia/consensus/get_block_challenge.py +30 -7
- chia/consensus/make_sub_epoch_summary.py +1 -5
- chia/consensus/multiprocess_validation.py +21 -20
- chia/consensus/pot_iterations.py +74 -13
- chia/{util → consensus}/prev_transaction_block.py +1 -1
- chia/consensus/vdf_info_computation.py +1 -3
- chia/daemon/keychain_proxy.py +5 -5
- chia/daemon/server.py +22 -5
- chia/data_layer/data_layer.py +92 -51
- chia/{rpc → data_layer}/data_layer_rpc_api.py +1 -1
- chia/{rpc → data_layer}/data_layer_rpc_util.py +3 -6
- chia/data_layer/data_layer_util.py +4 -6
- chia/data_layer/data_layer_wallet.py +42 -69
- chia/data_layer/dl_wallet_store.py +12 -6
- chia/data_layer/download_data.py +3 -3
- chia/data_layer/s3_plugin_service.py +0 -1
- chia/farmer/farmer.py +3 -4
- chia/farmer/farmer_api.py +11 -7
- chia/{rpc → farmer}/farmer_rpc_client.py +1 -1
- chia/full_node/block_height_map.py +7 -6
- chia/full_node/block_store.py +5 -7
- chia/full_node/bundle_tools.py +1 -2
- chia/full_node/coin_store.py +143 -124
- chia/{types → full_node}/eligible_coin_spends.py +39 -70
- chia/full_node/fee_estimator.py +1 -1
- chia/full_node/fee_estimator_interface.py +0 -8
- chia/full_node/fee_tracker.py +25 -25
- chia/full_node/full_node.py +70 -53
- chia/full_node/full_node_api.py +57 -40
- chia/{rpc → full_node}/full_node_rpc_api.py +87 -8
- chia/{rpc → full_node}/full_node_rpc_client.py +7 -6
- chia/full_node/full_node_store.py +23 -8
- chia/full_node/mempool.py +206 -53
- chia/full_node/mempool_check_conditions.py +20 -63
- chia/full_node/mempool_manager.py +26 -40
- chia/full_node/subscriptions.py +1 -3
- chia/full_node/tx_processing_queue.py +50 -3
- chia/full_node/weight_proof.py +46 -37
- chia/harvester/harvester.py +1 -1
- chia/harvester/harvester_api.py +22 -7
- chia/introducer/introducer.py +1 -1
- chia/introducer/introducer_api.py +1 -1
- chia/plot_sync/exceptions.py +1 -1
- chia/plot_sync/receiver.py +1 -1
- chia/plot_sync/sender.py +2 -2
- chia/pools/pool_puzzles.py +13 -18
- chia/pools/pool_wallet.py +23 -46
- chia/protocols/farmer_protocol.py +11 -3
- chia/protocols/full_node_protocol.py +1 -4
- chia/protocols/harvester_protocol.py +3 -3
- chia/protocols/pool_protocol.py +1 -2
- chia/protocols/shared_protocol.py +3 -3
- chia/protocols/timelord_protocol.py +1 -3
- chia/protocols/wallet_protocol.py +3 -3
- chia/rpc/rpc_client.py +7 -8
- chia/rpc/rpc_server.py +3 -3
- chia/rpc/util.py +3 -1
- chia/seeder/crawler.py +1 -1
- chia/seeder/crawler_api.py +1 -1
- chia/seeder/dns_server.py +2 -0
- chia/seeder/start_crawler.py +3 -3
- chia/server/address_manager.py +286 -38
- chia/server/address_manager_store.py +0 -215
- chia/{types → server}/aliases.py +7 -7
- chia/server/api_protocol.py +1 -1
- chia/server/chia_policy.py +1 -1
- chia/server/node_discovery.py +76 -113
- chia/server/rate_limits.py +1 -1
- chia/server/resolve_peer_info.py +43 -0
- chia/server/server.py +5 -5
- chia/server/start_data_layer.py +4 -4
- chia/server/start_farmer.py +5 -4
- chia/server/start_full_node.py +5 -4
- chia/server/start_harvester.py +7 -5
- chia/server/start_introducer.py +2 -2
- chia/server/start_service.py +1 -1
- chia/server/start_timelord.py +7 -5
- chia/server/start_wallet.py +7 -5
- chia/server/ws_connection.py +1 -1
- chia/simulator/add_blocks_in_batches.py +2 -2
- chia/simulator/block_tools.py +245 -201
- chia/simulator/full_node_simulator.py +38 -10
- chia/simulator/setup_services.py +12 -12
- chia/simulator/simulator_full_node_rpc_api.py +2 -2
- chia/simulator/simulator_full_node_rpc_client.py +2 -2
- chia/simulator/simulator_test_tools.py +2 -2
- chia/simulator/start_simulator.py +1 -1
- chia/simulator/wallet_tools.py +10 -18
- chia/ssl/create_ssl.py +1 -1
- chia/timelord/iters_from_block.py +14 -14
- chia/timelord/timelord.py +15 -11
- chia/timelord/timelord_api.py +14 -2
- chia/timelord/timelord_state.py +20 -14
- chia/types/blockchain_format/program.py +53 -10
- chia/types/blockchain_format/proof_of_space.py +73 -19
- chia/types/coin_spend.py +3 -56
- chia/types/generator_types.py +28 -0
- chia/types/internal_mempool_item.py +1 -2
- chia/types/mempool_item.py +12 -7
- chia/types/unfinished_header_block.py +1 -2
- chia/types/validation_state.py +1 -2
- chia/types/weight_proof.py +1 -3
- chia/util/action_scope.py +3 -3
- chia/util/block_cache.py +1 -2
- chia/util/byte_types.py +1 -1
- chia/util/casts.py +21 -0
- chia/util/config.py +0 -37
- chia/util/db_wrapper.py +8 -1
- chia/util/errors.py +3 -2
- chia/util/initial-config.yaml +21 -5
- chia/util/keychain.py +6 -7
- chia/util/keyring_wrapper.py +5 -5
- chia/util/limited_semaphore.py +1 -1
- chia/util/priority_mutex.py +1 -1
- chia/util/streamable.py +63 -5
- chia/util/task_timing.py +1 -1
- chia/util/virtual_project_analysis.py +1 -1
- chia/wallet/cat_wallet/cat_info.py +7 -3
- chia/wallet/cat_wallet/cat_outer_puzzle.py +9 -5
- chia/wallet/cat_wallet/cat_utils.py +1 -1
- chia/wallet/cat_wallet/cat_wallet.py +44 -36
- chia/wallet/cat_wallet/lineage_store.py +7 -0
- chia/wallet/cat_wallet/r_cat_wallet.py +273 -0
- chia/wallet/conditions.py +5 -10
- chia/wallet/db_wallet/db_wallet_puzzles.py +4 -4
- chia/wallet/derivation_record.py +33 -0
- chia/wallet/derive_keys.py +3 -3
- chia/wallet/did_wallet/did_info.py +12 -3
- chia/wallet/did_wallet/did_wallet.py +132 -101
- chia/wallet/did_wallet/did_wallet_puzzles.py +9 -9
- chia/wallet/driver_protocol.py +3 -1
- chia/{types/spend_bundle.py → wallet/estimate_fees.py} +2 -7
- chia/wallet/nft_wallet/metadata_outer_puzzle.py +5 -3
- chia/wallet/nft_wallet/nft_puzzle_utils.py +1 -1
- chia/wallet/nft_wallet/nft_wallet.py +69 -112
- chia/wallet/nft_wallet/ownership_outer_puzzle.py +5 -3
- chia/wallet/nft_wallet/singleton_outer_puzzle.py +6 -4
- chia/wallet/nft_wallet/transfer_program_puzzle.py +4 -2
- chia/wallet/nft_wallet/uncurry_nft.py +4 -6
- chia/wallet/notification_manager.py +2 -3
- chia/wallet/outer_puzzles.py +7 -2
- chia/wallet/puzzle_drivers.py +1 -1
- chia/wallet/puzzles/clawback/drivers.py +5 -4
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +1 -1
- chia/wallet/puzzles/singleton_top_layer.py +2 -1
- chia/wallet/puzzles/singleton_top_layer_v1_1.py +2 -1
- chia/wallet/puzzles/tails.py +1 -3
- chia/wallet/signer_protocol.py +5 -6
- chia/wallet/singleton.py +5 -4
- chia/wallet/singleton_record.py +1 -1
- chia/wallet/trade_manager.py +18 -20
- chia/wallet/trade_record.py +3 -6
- chia/wallet/trading/offer.py +12 -13
- chia/wallet/uncurried_puzzle.py +2 -2
- chia/wallet/util/compute_additions.py +58 -0
- chia/wallet/util/compute_hints.py +3 -3
- chia/wallet/util/compute_memos.py +4 -4
- chia/wallet/util/curry_and_treehash.py +2 -1
- chia/wallet/util/debug_spend_bundle.py +1 -1
- chia/wallet/util/merkle_tree.py +1 -1
- chia/wallet/util/peer_request_cache.py +1 -2
- chia/wallet/util/tx_config.py +3 -8
- chia/wallet/util/wallet_sync_utils.py +10 -5
- chia/wallet/util/wallet_types.py +1 -0
- chia/wallet/vc_wallet/cr_cat_drivers.py +17 -18
- chia/wallet/vc_wallet/cr_cat_wallet.py +30 -28
- chia/wallet/vc_wallet/cr_outer_puzzle.py +5 -3
- chia/wallet/vc_wallet/vc_drivers.py +50 -8
- chia/wallet/vc_wallet/vc_store.py +3 -5
- chia/wallet/vc_wallet/vc_wallet.py +15 -22
- chia/wallet/wallet.py +36 -46
- chia/wallet/wallet_action_scope.py +73 -4
- chia/wallet/wallet_blockchain.py +1 -3
- chia/wallet/wallet_interested_store.py +1 -1
- chia/wallet/wallet_nft_store.py +3 -3
- chia/wallet/wallet_node.py +17 -16
- chia/wallet/wallet_node_api.py +4 -5
- chia/wallet/wallet_pool_store.py +1 -1
- chia/wallet/wallet_protocol.py +2 -0
- chia/wallet/wallet_puzzle_store.py +1 -1
- chia/{rpc → wallet}/wallet_request_types.py +670 -81
- chia/{rpc → wallet}/wallet_rpc_api.py +735 -766
- chia/{rpc → wallet}/wallet_rpc_client.py +268 -420
- chia/wallet/wallet_singleton_store.py +8 -7
- chia/wallet/wallet_spend_bundle.py +4 -3
- chia/wallet/wallet_state_manager.py +320 -191
- chia/wallet/wallet_weight_proof_handler.py +1 -2
- chia/wallet/wsm_apis.py +98 -0
- {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc1.dist-info}/METADATA +7 -7
- {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc1.dist-info}/RECORD +443 -436
- mozilla-ca/cacert.pem +3 -165
- chia/_tests/fee_estimation/test_mempoolitem_height_added.py +0 -145
- chia/cmds/dev.py +0 -18
- chia/types/blockchain_format/slots.py +0 -9
- chia/types/blockchain_format/sub_epoch_summary.py +0 -5
- chia/types/end_of_slot_bundle.py +0 -5
- chia/types/full_block.py +0 -5
- chia/types/header_block.py +0 -5
- chia/types/spend_bundle_conditions.py +0 -7
- chia/types/transaction_queue_entry.py +0 -56
- chia/types/unfinished_block.py +0 -5
- /chia/cmds/{installers.py → dev/installers.py} +0 -0
- /chia/cmds/{sim.py → dev/sim.py} +0 -0
- /chia/{util → cmds}/dump_keyring.py +0 -0
- /chia/{full_node → consensus}/signage_point.py +0 -0
- /chia/{rpc → data_layer}/data_layer_rpc_client.py +0 -0
- /chia/{rpc → farmer}/farmer_rpc_api.py +0 -0
- /chia/{util → full_node}/full_block_utils.py +0 -0
- /chia/{rpc → harvester}/harvester_rpc_api.py +0 -0
- /chia/{rpc → harvester}/harvester_rpc_client.py +0 -0
- /chia/{full_node → protocols}/fee_estimate.py +0 -0
- /chia/{server → protocols}/outbound_message.py +0 -0
- /chia/{rpc → seeder}/crawler_rpc_api.py +0 -0
- /chia/{util → simulator}/vdf_prover.py +0 -0
- /chia/{util → ssl}/ssl_check.py +0 -0
- /chia/{rpc → timelord}/timelord_rpc_api.py +0 -0
- {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc1.dist-info}/LICENSE +0 -0
- {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc1.dist-info}/WHEEL +0 -0
- {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc1.dist-info}/entry_points.txt +0 -0
|
@@ -6,80 +6,24 @@ import logging
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union, cast
|
|
8
8
|
|
|
9
|
-
from chia_rs import AugSchemeMPL, Coin, G1Element, G2Element, PrivateKey
|
|
9
|
+
from chia_rs import AugSchemeMPL, Coin, CoinSpend, CoinState, G1Element, G2Element, PrivateKey
|
|
10
10
|
from chia_rs.sized_bytes import bytes32
|
|
11
11
|
from chia_rs.sized_ints import uint8, uint16, uint32, uint64
|
|
12
12
|
from clvm_tools.binutils import assemble
|
|
13
13
|
|
|
14
14
|
from chia.consensus.block_rewards import calculate_base_farmer_reward
|
|
15
15
|
from chia.data_layer.data_layer_errors import LauncherCoinNotFoundError
|
|
16
|
-
from chia.data_layer.data_layer_util import dl_verify_proof
|
|
17
|
-
from chia.data_layer.data_layer_wallet import DataLayerWallet
|
|
16
|
+
from chia.data_layer.data_layer_util import DLProof, VerifyProofResponse, dl_verify_proof
|
|
17
|
+
from chia.data_layer.data_layer_wallet import DataLayerWallet, Mirror
|
|
18
18
|
from chia.pools.pool_wallet import PoolWallet
|
|
19
19
|
from chia.pools.pool_wallet_info import FARMING_TO_POOL, PoolState, PoolWalletInfo, create_pool_state
|
|
20
|
-
from chia.protocols.
|
|
20
|
+
from chia.protocols.outbound_message import NodeType
|
|
21
21
|
from chia.rpc.rpc_server import Endpoint, EndpointResult, default_get_connections
|
|
22
22
|
from chia.rpc.util import ALL_TRANSLATION_LAYERS, RpcEndpoint, marshal
|
|
23
|
-
from chia.rpc.wallet_request_types import (
|
|
24
|
-
AddKey,
|
|
25
|
-
AddKeyResponse,
|
|
26
|
-
ApplySignatures,
|
|
27
|
-
ApplySignaturesResponse,
|
|
28
|
-
CheckDeleteKey,
|
|
29
|
-
CheckDeleteKeyResponse,
|
|
30
|
-
CombineCoins,
|
|
31
|
-
CombineCoinsResponse,
|
|
32
|
-
DeleteKey,
|
|
33
|
-
Empty,
|
|
34
|
-
ExecuteSigningInstructions,
|
|
35
|
-
ExecuteSigningInstructionsResponse,
|
|
36
|
-
GatherSigningInfo,
|
|
37
|
-
GatherSigningInfoResponse,
|
|
38
|
-
GenerateMnemonicResponse,
|
|
39
|
-
GetHeightInfoResponse,
|
|
40
|
-
GetLoggedInFingerprintResponse,
|
|
41
|
-
GetNotifications,
|
|
42
|
-
GetNotificationsResponse,
|
|
43
|
-
GetPrivateKey,
|
|
44
|
-
GetPrivateKeyFormat,
|
|
45
|
-
GetPrivateKeyResponse,
|
|
46
|
-
GetPublicKeysResponse,
|
|
47
|
-
GetSyncStatusResponse,
|
|
48
|
-
GetTimestampForHeight,
|
|
49
|
-
GetTimestampForHeightResponse,
|
|
50
|
-
LogIn,
|
|
51
|
-
LogInResponse,
|
|
52
|
-
PushTransactions,
|
|
53
|
-
PushTransactionsResponse,
|
|
54
|
-
PushTX,
|
|
55
|
-
SetWalletResyncOnStartup,
|
|
56
|
-
SplitCoins,
|
|
57
|
-
SplitCoinsResponse,
|
|
58
|
-
SubmitTransactions,
|
|
59
|
-
SubmitTransactionsResponse,
|
|
60
|
-
VCAddProofs,
|
|
61
|
-
VCGet,
|
|
62
|
-
VCGetList,
|
|
63
|
-
VCGetListResponse,
|
|
64
|
-
VCGetProofsForRoot,
|
|
65
|
-
VCGetProofsForRootResponse,
|
|
66
|
-
VCGetResponse,
|
|
67
|
-
VCMint,
|
|
68
|
-
VCMintResponse,
|
|
69
|
-
VCProofsRPC,
|
|
70
|
-
VCProofWithHash,
|
|
71
|
-
VCRecordWithCoinID,
|
|
72
|
-
VCRevoke,
|
|
73
|
-
VCRevokeResponse,
|
|
74
|
-
VCSpend,
|
|
75
|
-
VCSpendResponse,
|
|
76
|
-
)
|
|
77
|
-
from chia.server.outbound_message import NodeType
|
|
78
23
|
from chia.server.ws_connection import WSChiaConnection
|
|
79
24
|
from chia.types.blockchain_format.coin import coin_as_list
|
|
80
|
-
from chia.types.blockchain_format.program import INFINITE_COST, Program
|
|
25
|
+
from chia.types.blockchain_format.program import INFINITE_COST, Program, run_with_cost
|
|
81
26
|
from chia.types.coin_record import CoinRecord
|
|
82
|
-
from chia.types.coin_spend import CoinSpend
|
|
83
27
|
from chia.types.signing_mode import CHIP_0002_SIGN_MESSAGE_PREFIX, SigningMode
|
|
84
28
|
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
|
|
85
29
|
from chia.util.byte_types import hexstr_to_bytes
|
|
@@ -109,11 +53,10 @@ from chia.wallet.derive_keys import (
|
|
|
109
53
|
MAX_POOL_WALLETS,
|
|
110
54
|
master_sk_to_farmer_sk,
|
|
111
55
|
master_sk_to_pool_sk,
|
|
112
|
-
master_sk_to_singleton_owner_sk,
|
|
113
56
|
match_address_to_sk,
|
|
114
57
|
)
|
|
115
58
|
from chia.wallet.did_wallet import did_wallet_puzzles
|
|
116
|
-
from chia.wallet.did_wallet.did_info import DIDCoinData, DIDInfo
|
|
59
|
+
from chia.wallet.did_wallet.did_info import DIDCoinData, DIDInfo, did_recovery_is_nil
|
|
117
60
|
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
|
118
61
|
from chia.wallet.did_wallet.did_wallet_puzzles import (
|
|
119
62
|
DID_INNERPUZ_MOD,
|
|
@@ -163,6 +106,148 @@ from chia.wallet.wallet_coin_store import CoinRecordOrder, GetCoinRecords, unspe
|
|
|
163
106
|
from chia.wallet.wallet_info import WalletInfo
|
|
164
107
|
from chia.wallet.wallet_node import WalletNode
|
|
165
108
|
from chia.wallet.wallet_protocol import WalletProtocol
|
|
109
|
+
from chia.wallet.wallet_request_types import (
|
|
110
|
+
AddKey,
|
|
111
|
+
AddKeyResponse,
|
|
112
|
+
ApplySignatures,
|
|
113
|
+
ApplySignaturesResponse,
|
|
114
|
+
CheckDeleteKey,
|
|
115
|
+
CheckDeleteKeyResponse,
|
|
116
|
+
CombineCoins,
|
|
117
|
+
CombineCoinsResponse,
|
|
118
|
+
CreateNewDL,
|
|
119
|
+
CreateNewDLResponse,
|
|
120
|
+
DeleteKey,
|
|
121
|
+
DIDCreateBackupFile,
|
|
122
|
+
DIDCreateBackupFileResponse,
|
|
123
|
+
DIDFindLostDID,
|
|
124
|
+
DIDFindLostDIDResponse,
|
|
125
|
+
DIDGetCurrentCoinInfo,
|
|
126
|
+
DIDGetCurrentCoinInfoResponse,
|
|
127
|
+
DIDGetDID,
|
|
128
|
+
DIDGetDIDResponse,
|
|
129
|
+
DIDGetInfo,
|
|
130
|
+
DIDGetInfoResponse,
|
|
131
|
+
DIDGetMetadata,
|
|
132
|
+
DIDGetMetadataResponse,
|
|
133
|
+
DIDGetPubkey,
|
|
134
|
+
DIDGetPubkeyResponse,
|
|
135
|
+
DIDGetRecoveryInfo,
|
|
136
|
+
DIDGetRecoveryInfoResponse,
|
|
137
|
+
DIDGetRecoveryList,
|
|
138
|
+
DIDGetRecoveryListResponse,
|
|
139
|
+
DIDGetWalletName,
|
|
140
|
+
DIDGetWalletNameResponse,
|
|
141
|
+
DIDMessageSpend,
|
|
142
|
+
DIDMessageSpendResponse,
|
|
143
|
+
DIDSetWalletName,
|
|
144
|
+
DIDSetWalletNameResponse,
|
|
145
|
+
DIDTransferDID,
|
|
146
|
+
DIDTransferDIDResponse,
|
|
147
|
+
DIDUpdateMetadata,
|
|
148
|
+
DIDUpdateMetadataResponse,
|
|
149
|
+
DIDUpdateRecoveryIDs,
|
|
150
|
+
DIDUpdateRecoveryIDsResponse,
|
|
151
|
+
DLDeleteMirror,
|
|
152
|
+
DLDeleteMirrorResponse,
|
|
153
|
+
DLGetMirrors,
|
|
154
|
+
DLGetMirrorsResponse,
|
|
155
|
+
DLHistory,
|
|
156
|
+
DLHistoryResponse,
|
|
157
|
+
DLLatestSingleton,
|
|
158
|
+
DLLatestSingletonResponse,
|
|
159
|
+
DLNewMirror,
|
|
160
|
+
DLNewMirrorResponse,
|
|
161
|
+
DLOwnedSingletonsResponse,
|
|
162
|
+
DLSingletonsByRoot,
|
|
163
|
+
DLSingletonsByRootResponse,
|
|
164
|
+
DLStopTracking,
|
|
165
|
+
DLTrackNew,
|
|
166
|
+
DLUpdateMultiple,
|
|
167
|
+
DLUpdateMultipleResponse,
|
|
168
|
+
DLUpdateRoot,
|
|
169
|
+
DLUpdateRootResponse,
|
|
170
|
+
Empty,
|
|
171
|
+
ExecuteSigningInstructions,
|
|
172
|
+
ExecuteSigningInstructionsResponse,
|
|
173
|
+
GatherSigningInfo,
|
|
174
|
+
GatherSigningInfoResponse,
|
|
175
|
+
GenerateMnemonicResponse,
|
|
176
|
+
GetHeightInfoResponse,
|
|
177
|
+
GetLoggedInFingerprintResponse,
|
|
178
|
+
GetNotifications,
|
|
179
|
+
GetNotificationsResponse,
|
|
180
|
+
GetPrivateKey,
|
|
181
|
+
GetPrivateKeyFormat,
|
|
182
|
+
GetPrivateKeyResponse,
|
|
183
|
+
GetPublicKeysResponse,
|
|
184
|
+
GetSyncStatusResponse,
|
|
185
|
+
GetTimestampForHeight,
|
|
186
|
+
GetTimestampForHeightResponse,
|
|
187
|
+
LogIn,
|
|
188
|
+
LogInResponse,
|
|
189
|
+
NFTAddURI,
|
|
190
|
+
NFTAddURIResponse,
|
|
191
|
+
NFTCalculateRoyalties,
|
|
192
|
+
NFTCalculateRoyaltiesResponse,
|
|
193
|
+
NFTCountNFTs,
|
|
194
|
+
NFTCountNFTsResponse,
|
|
195
|
+
NFTGetByDID,
|
|
196
|
+
NFTGetByDIDResponse,
|
|
197
|
+
NFTGetInfo,
|
|
198
|
+
NFTGetInfoResponse,
|
|
199
|
+
NFTGetNFTs,
|
|
200
|
+
NFTGetNFTsResponse,
|
|
201
|
+
NFTGetWalletDID,
|
|
202
|
+
NFTGetWalletDIDResponse,
|
|
203
|
+
NFTGetWalletsWithDIDsResponse,
|
|
204
|
+
NFTMintBulk,
|
|
205
|
+
NFTMintBulkResponse,
|
|
206
|
+
NFTMintNFTRequest,
|
|
207
|
+
NFTMintNFTResponse,
|
|
208
|
+
NFTSetDIDBulk,
|
|
209
|
+
NFTSetDIDBulkResponse,
|
|
210
|
+
NFTSetNFTDID,
|
|
211
|
+
NFTSetNFTDIDResponse,
|
|
212
|
+
NFTSetNFTStatus,
|
|
213
|
+
NFTTransferBulk,
|
|
214
|
+
NFTTransferBulkResponse,
|
|
215
|
+
NFTTransferNFT,
|
|
216
|
+
NFTTransferNFTResponse,
|
|
217
|
+
NFTWalletWithDID,
|
|
218
|
+
PushTransactions,
|
|
219
|
+
PushTransactionsResponse,
|
|
220
|
+
PushTX,
|
|
221
|
+
PWAbsorbRewards,
|
|
222
|
+
PWAbsorbRewardsResponse,
|
|
223
|
+
PWJoinPool,
|
|
224
|
+
PWJoinPoolResponse,
|
|
225
|
+
PWSelfPool,
|
|
226
|
+
PWSelfPoolResponse,
|
|
227
|
+
PWStatus,
|
|
228
|
+
PWStatusResponse,
|
|
229
|
+
SetWalletResyncOnStartup,
|
|
230
|
+
SplitCoins,
|
|
231
|
+
SplitCoinsResponse,
|
|
232
|
+
SubmitTransactions,
|
|
233
|
+
SubmitTransactionsResponse,
|
|
234
|
+
VCAddProofs,
|
|
235
|
+
VCGet,
|
|
236
|
+
VCGetList,
|
|
237
|
+
VCGetListResponse,
|
|
238
|
+
VCGetProofsForRoot,
|
|
239
|
+
VCGetProofsForRootResponse,
|
|
240
|
+
VCGetResponse,
|
|
241
|
+
VCMint,
|
|
242
|
+
VCMintResponse,
|
|
243
|
+
VCProofsRPC,
|
|
244
|
+
VCProofWithHash,
|
|
245
|
+
VCRecordWithCoinID,
|
|
246
|
+
VCRevoke,
|
|
247
|
+
VCRevokeResponse,
|
|
248
|
+
VCSpend,
|
|
249
|
+
VCSpendResponse,
|
|
250
|
+
)
|
|
166
251
|
from chia.wallet.wallet_spend_bundle import WalletSpendBundle
|
|
167
252
|
|
|
168
253
|
# Timeout for response from wallet/full node for sending a transaction
|
|
@@ -361,6 +446,27 @@ def tx_endpoint(
|
|
|
361
446
|
return _inner
|
|
362
447
|
|
|
363
448
|
|
|
449
|
+
REPLACEABLE_TRANSACTION_RECORD = TransactionRecord(
|
|
450
|
+
confirmed_at_height=uint32(0),
|
|
451
|
+
created_at_time=uint64(0),
|
|
452
|
+
to_puzzle_hash=bytes32.zeros,
|
|
453
|
+
amount=uint64(0),
|
|
454
|
+
fee_amount=uint64(0),
|
|
455
|
+
confirmed=False,
|
|
456
|
+
sent=uint32(0),
|
|
457
|
+
spend_bundle=WalletSpendBundle([], G2Element()),
|
|
458
|
+
additions=[],
|
|
459
|
+
removals=[],
|
|
460
|
+
wallet_id=uint32(0),
|
|
461
|
+
sent_to=[],
|
|
462
|
+
trade_id=None,
|
|
463
|
+
type=uint32(0),
|
|
464
|
+
name=bytes32.zeros,
|
|
465
|
+
memos=[],
|
|
466
|
+
valid_times=ConditionValidTimes(),
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
|
|
364
470
|
class WalletRpcApi:
|
|
365
471
|
if TYPE_CHECKING:
|
|
366
472
|
from chia.rpc.rpc_server import RpcApiProtocol
|
|
@@ -843,7 +949,7 @@ class WalletRpcApi:
|
|
|
843
949
|
for tx in interface.side_effects.transactions
|
|
844
950
|
if tx.spend_bundle is not None
|
|
845
951
|
for cs in tx.spend_bundle.coin_spends
|
|
846
|
-
for condition in cs.puzzle_reveal
|
|
952
|
+
for condition in run_with_cost(cs.puzzle_reveal, INFINITE_COST, cs.solution)[1].as_iter()
|
|
847
953
|
]
|
|
848
954
|
create_coin_announcement = next(
|
|
849
955
|
condition
|
|
@@ -870,7 +976,7 @@ class WalletRpcApi:
|
|
|
870
976
|
await self.service.wallet_state_manager.main_wallet.create_tandem_xch_tx(
|
|
871
977
|
request.fee,
|
|
872
978
|
inner_action_scope,
|
|
873
|
-
(
|
|
979
|
+
extra_conditions=(
|
|
874
980
|
*extra_conditions,
|
|
875
981
|
CreateCoinAnnouncement(
|
|
876
982
|
create_coin_announcement.msg, announcement_origin
|
|
@@ -1106,9 +1212,7 @@ class WalletRpcApi:
|
|
|
1106
1212
|
if "initial_target_state" not in request:
|
|
1107
1213
|
raise AttributeError("Daemon didn't send `initial_target_state`. Try updating the daemon.")
|
|
1108
1214
|
|
|
1109
|
-
owner_puzzle_hash: bytes32 = await self.service.wallet_state_manager
|
|
1110
|
-
new=not action_scope.config.tx_config.reuse_puzhash
|
|
1111
|
-
)
|
|
1215
|
+
owner_puzzle_hash: bytes32 = await action_scope.get_puzzle_hash(self.service.wallet_state_manager)
|
|
1112
1216
|
|
|
1113
1217
|
from chia.pools.pool_wallet_info import initial_pool_state_from_dict
|
|
1114
1218
|
|
|
@@ -1121,17 +1225,15 @@ class WalletRpcApi:
|
|
|
1121
1225
|
max_pwi = 1
|
|
1122
1226
|
for _, wallet in self.service.wallet_state_manager.wallets.items():
|
|
1123
1227
|
if wallet.type() == WalletType.POOLING_WALLET:
|
|
1124
|
-
|
|
1125
|
-
pool_wallet_index = await wallet.get_pool_wallet_index()
|
|
1126
|
-
max_pwi = max(max_pwi, pool_wallet_index)
|
|
1228
|
+
max_pwi += 1
|
|
1127
1229
|
|
|
1128
1230
|
if max_pwi + 1 >= (MAX_POOL_WALLETS - 1):
|
|
1129
1231
|
raise ValueError(f"Too many pool wallets ({max_pwi}), cannot create any more on this key.")
|
|
1130
1232
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1233
|
+
owner_pk: G1Element = self.service.wallet_state_manager.main_wallet.hardened_pubkey_for_path(
|
|
1234
|
+
# copied from chia.wallet.derive_keys. Could maybe be an exported constant in the future.
|
|
1235
|
+
[12381, 8444, 5, max_pwi]
|
|
1133
1236
|
)
|
|
1134
|
-
owner_pk: G1Element = owner_sk.get_g1()
|
|
1135
1237
|
|
|
1136
1238
|
initial_target_state = initial_pool_state_from_dict(
|
|
1137
1239
|
request["initial_target_state"], owner_pk, owner_puzzle_hash
|
|
@@ -1184,7 +1286,7 @@ class WalletRpcApi:
|
|
|
1184
1286
|
wallet_balance["wallet_type"] = wallet.type()
|
|
1185
1287
|
if self.service.logged_in_fingerprint is not None:
|
|
1186
1288
|
wallet_balance["fingerprint"] = self.service.logged_in_fingerprint
|
|
1187
|
-
if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
|
|
1289
|
+
if wallet.type() in {WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}:
|
|
1188
1290
|
assert isinstance(wallet, CATWallet)
|
|
1189
1291
|
wallet_balance["asset_id"] = wallet.get_asset_id()
|
|
1190
1292
|
if wallet.type() == WalletType.CRCAT:
|
|
@@ -1274,9 +1376,9 @@ class WalletRpcApi:
|
|
|
1274
1376
|
|
|
1275
1377
|
outputs = [
|
|
1276
1378
|
CreateCoin(
|
|
1277
|
-
await
|
|
1278
|
-
|
|
1279
|
-
|
|
1379
|
+
await action_scope.get_puzzle_hash(
|
|
1380
|
+
self.service.wallet_state_manager, override_reuse_puzhash_with=False
|
|
1381
|
+
),
|
|
1280
1382
|
request.amount_per_coin,
|
|
1281
1383
|
)
|
|
1282
1384
|
for _ in range(request.number_of_coins)
|
|
@@ -1388,13 +1490,10 @@ class WalletRpcApi:
|
|
|
1388
1490
|
)
|
|
1389
1491
|
if isinstance(wallet, Wallet):
|
|
1390
1492
|
primary_output_amount = uint64(primary_output_amount - request.fee)
|
|
1391
|
-
main_wallet = wallet
|
|
1392
|
-
else:
|
|
1393
|
-
main_wallet = wallet.standard_wallet
|
|
1394
1493
|
|
|
1395
1494
|
await wallet.generate_signed_transaction(
|
|
1396
1495
|
[primary_output_amount],
|
|
1397
|
-
[await
|
|
1496
|
+
[await action_scope.get_puzzle_hash(self.service.wallet_state_manager)],
|
|
1398
1497
|
action_scope,
|
|
1399
1498
|
request.fee,
|
|
1400
1499
|
coins=set(coins),
|
|
@@ -1480,13 +1579,13 @@ class WalletRpcApi:
|
|
|
1480
1579
|
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
|
1481
1580
|
selected = self.service.config["selected_network"]
|
|
1482
1581
|
prefix = self.service.config["network_overrides"]["config"][selected]["address_prefix"]
|
|
1483
|
-
if wallet.type()
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1582
|
+
if wallet.type() in {WalletType.STANDARD_WALLET, WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}:
|
|
1583
|
+
async with self.service.wallet_state_manager.new_action_scope(
|
|
1584
|
+
DEFAULT_TX_CONFIG, push=request.get("save_derivations", True)
|
|
1585
|
+
) as action_scope:
|
|
1586
|
+
raw_puzzle_hash = await action_scope.get_puzzle_hash(
|
|
1587
|
+
self.service.wallet_state_manager, override_reuse_puzhash_with=not create_new
|
|
1588
|
+
)
|
|
1490
1589
|
address = encode_puzzle_hash(raw_puzzle_hash, prefix)
|
|
1491
1590
|
else:
|
|
1492
1591
|
raise ValueError(f"Wallet type {wallet.type()} cannot create puzzle hashes")
|
|
@@ -1555,7 +1654,7 @@ class WalletRpcApi:
|
|
|
1555
1654
|
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
|
1556
1655
|
|
|
1557
1656
|
async with self.service.wallet_state_manager.lock:
|
|
1558
|
-
if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
|
|
1657
|
+
if wallet.type() in {WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}:
|
|
1559
1658
|
assert isinstance(wallet, CATWallet)
|
|
1560
1659
|
response = await self.cat_spend(request, hold_lock=False)
|
|
1561
1660
|
transaction = response["transaction"]
|
|
@@ -1708,7 +1807,7 @@ class WalletRpcApi:
|
|
|
1708
1807
|
wallet = state_mgr.wallets[wallet_id]
|
|
1709
1808
|
async with state_mgr.lock:
|
|
1710
1809
|
all_coin_records = await state_mgr.coin_store.get_unspent_coins_for_wallet(wallet_id)
|
|
1711
|
-
if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
|
|
1810
|
+
if wallet.type() in {WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}:
|
|
1712
1811
|
assert isinstance(wallet, CATWallet)
|
|
1713
1812
|
spendable_coins: list[WalletCoinRecord] = await wallet.get_cat_spendable_coins(all_coin_records)
|
|
1714
1813
|
else:
|
|
@@ -1828,9 +1927,10 @@ class WalletRpcApi:
|
|
|
1828
1927
|
# Since we've bumping the derivation index without having found any new puzzles, we want
|
|
1829
1928
|
# to preserve the current last used index, so we call create_more_puzzle_hashes with
|
|
1830
1929
|
# mark_existing_as_used=False
|
|
1831
|
-
await self.service.wallet_state_manager.create_more_puzzle_hashes(
|
|
1930
|
+
result = await self.service.wallet_state_manager.create_more_puzzle_hashes(
|
|
1832
1931
|
from_zero=False, mark_existing_as_used=False, up_to_index=index, num_additional_phs=0
|
|
1833
1932
|
)
|
|
1933
|
+
await result.commit(self.service.wallet_state_manager)
|
|
1834
1934
|
|
|
1835
1935
|
updated: Optional[uint32] = await self.service.wallet_state_manager.puzzle_store.get_last_derivation_path()
|
|
1836
1936
|
updated_index = updated if updated is not None else None
|
|
@@ -2476,88 +2576,79 @@ class WalletRpcApi:
|
|
|
2476
2576
|
# Distributed Identities
|
|
2477
2577
|
##########################################################################################
|
|
2478
2578
|
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2482
|
-
await wallet.set_name(
|
|
2483
|
-
return
|
|
2579
|
+
@marshal
|
|
2580
|
+
async def did_set_wallet_name(self, request: DIDSetWalletName) -> DIDSetWalletNameResponse:
|
|
2581
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2582
|
+
await wallet.set_name(request.name)
|
|
2583
|
+
return DIDSetWalletNameResponse(request.wallet_id)
|
|
2484
2584
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2488
|
-
|
|
2489
|
-
return {"success": True, "wallet_id": wallet_id, "name": name}
|
|
2585
|
+
@marshal
|
|
2586
|
+
async def did_get_wallet_name(self, request: DIDGetWalletName) -> DIDGetWalletNameResponse:
|
|
2587
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2588
|
+
return DIDGetWalletNameResponse(request.wallet_id, wallet.get_name())
|
|
2490
2589
|
|
|
2491
2590
|
@tx_endpoint(push=True)
|
|
2591
|
+
@marshal
|
|
2492
2592
|
async def did_update_recovery_ids(
|
|
2493
2593
|
self,
|
|
2494
|
-
request:
|
|
2594
|
+
request: DIDUpdateRecoveryIDs,
|
|
2495
2595
|
action_scope: WalletActionScope,
|
|
2496
2596
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
2497
|
-
) ->
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
else:
|
|
2506
|
-
new_amount_verifications_required = uint64(len(recovery_list))
|
|
2597
|
+
) -> DIDUpdateRecoveryIDsResponse:
|
|
2598
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2599
|
+
recovery_list = [decode_puzzle_hash(puzzle_hash) for puzzle_hash in request.new_list]
|
|
2600
|
+
new_amount_verifications_required = (
|
|
2601
|
+
request.num_verifications_required
|
|
2602
|
+
if request.num_verifications_required is not None
|
|
2603
|
+
else uint64(len(recovery_list))
|
|
2604
|
+
)
|
|
2507
2605
|
async with self.service.wallet_state_manager.lock:
|
|
2508
2606
|
update_success = await wallet.update_recovery_list(recovery_list, new_amount_verifications_required)
|
|
2509
2607
|
# Update coin with new ID info
|
|
2510
2608
|
if update_success:
|
|
2511
|
-
await wallet.create_update_spend(
|
|
2512
|
-
|
|
2513
|
-
)
|
|
2514
|
-
return {
|
|
2515
|
-
"success": True,
|
|
2516
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
2517
|
-
}
|
|
2609
|
+
await wallet.create_update_spend(action_scope, fee=request.fee, extra_conditions=extra_conditions)
|
|
2610
|
+
# tx_endpoint will take care of default values here
|
|
2611
|
+
return DIDUpdateRecoveryIDsResponse([], [])
|
|
2518
2612
|
else:
|
|
2519
|
-
|
|
2613
|
+
raise RuntimeError("updating recovery list failed")
|
|
2520
2614
|
|
|
2521
2615
|
@tx_endpoint(push=False)
|
|
2616
|
+
@marshal
|
|
2522
2617
|
async def did_message_spend(
|
|
2523
2618
|
self,
|
|
2524
|
-
request:
|
|
2619
|
+
request: DIDMessageSpend,
|
|
2525
2620
|
action_scope: WalletActionScope,
|
|
2526
2621
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
2527
|
-
) ->
|
|
2528
|
-
|
|
2529
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2622
|
+
) -> DIDMessageSpendResponse:
|
|
2623
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2530
2624
|
|
|
2531
2625
|
await wallet.create_message_spend(
|
|
2532
2626
|
action_scope,
|
|
2533
2627
|
extra_conditions=(
|
|
2534
2628
|
*extra_conditions,
|
|
2535
|
-
*(CreateCoinAnnouncement(
|
|
2536
|
-
*(CreatePuzzleAnnouncement(
|
|
2629
|
+
*(CreateCoinAnnouncement(ca) for ca in request.coin_announcements),
|
|
2630
|
+
*(CreatePuzzleAnnouncement(pa) for pa in request.puzzle_announcements),
|
|
2537
2631
|
),
|
|
2538
2632
|
)
|
|
2539
|
-
return {
|
|
2540
|
-
"success": True,
|
|
2541
|
-
"spend_bundle": None, # tx_endpoint wrapper will take care of this
|
|
2542
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
2543
|
-
}
|
|
2544
2633
|
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2634
|
+
# tx_endpoint will take care of the default values here
|
|
2635
|
+
return DIDMessageSpendResponse([], [], WalletSpendBundle([], G2Element()))
|
|
2636
|
+
|
|
2637
|
+
@marshal
|
|
2638
|
+
async def did_get_info(self, request: DIDGetInfo) -> DIDGetInfoResponse:
|
|
2639
|
+
if request.coin_id.startswith(AddressType.DID.hrp(self.service.config)):
|
|
2640
|
+
coin_id = decode_puzzle_hash(request.coin_id)
|
|
2551
2641
|
else:
|
|
2552
|
-
coin_id = bytes32.from_hexstr(coin_id)
|
|
2642
|
+
coin_id = bytes32.from_hexstr(request.coin_id)
|
|
2553
2643
|
# Get coin state
|
|
2554
2644
|
peer = self.service.get_full_node_peer()
|
|
2555
|
-
coin_spend, coin_state = await self.get_latest_singleton_coin_spend(peer, coin_id, request.
|
|
2645
|
+
coin_spend, coin_state = await self.get_latest_singleton_coin_spend(peer, coin_id, request.latest)
|
|
2556
2646
|
uncurried = uncurry_puzzle(coin_spend.puzzle_reveal)
|
|
2557
2647
|
curried_args = match_did_puzzle(uncurried.mod, uncurried.args)
|
|
2558
2648
|
if curried_args is None:
|
|
2559
|
-
|
|
2649
|
+
raise ValueError("The coin is not a DID.")
|
|
2560
2650
|
p2_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata = curried_args
|
|
2651
|
+
recovery_list_hash_bytes = recovery_list_hash.as_atom()
|
|
2561
2652
|
launcher_id = bytes32(singleton_struct.rest().first().as_atom())
|
|
2562
2653
|
uncurried_p2 = uncurry_puzzle(p2_puzzle)
|
|
2563
2654
|
(public_key,) = uncurried_p2.args.as_iter()
|
|
@@ -2566,49 +2657,48 @@ class WalletRpcApi:
|
|
|
2566
2657
|
coin_memos = memos.get(coin_state.coin.name())
|
|
2567
2658
|
if coin_memos is not None:
|
|
2568
2659
|
for memo in coin_memos:
|
|
2569
|
-
hints.append(memo
|
|
2570
|
-
return
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
"
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
}
|
|
2660
|
+
hints.append(memo)
|
|
2661
|
+
return DIDGetInfoResponse(
|
|
2662
|
+
did_id=encode_puzzle_hash(launcher_id, AddressType.DID.hrp(self.service.config)),
|
|
2663
|
+
latest_coin=coin_state.coin.name(),
|
|
2664
|
+
p2_address=encode_puzzle_hash(p2_puzzle.get_tree_hash(), AddressType.XCH.hrp(self.service.config)),
|
|
2665
|
+
public_key=public_key.as_atom(),
|
|
2666
|
+
recovery_list_hash=bytes32(recovery_list_hash_bytes) if recovery_list_hash_bytes != b"" else None,
|
|
2667
|
+
num_verification=uint16(num_verification.as_int()),
|
|
2668
|
+
metadata=did_program_to_metadata(metadata),
|
|
2669
|
+
launcher_id=launcher_id,
|
|
2670
|
+
full_puzzle=Program.from_serialized(coin_spend.puzzle_reveal),
|
|
2671
|
+
solution=Program.from_serialized(coin_spend.solution),
|
|
2672
|
+
hints=hints,
|
|
2673
|
+
)
|
|
2584
2674
|
|
|
2585
|
-
|
|
2675
|
+
@marshal
|
|
2676
|
+
async def did_find_lost_did(self, request: DIDFindLostDID) -> DIDFindLostDIDResponse:
|
|
2586
2677
|
"""
|
|
2587
2678
|
Recover a missing or unspendable DID wallet by a coin id of the DID
|
|
2588
2679
|
:param coin_id: It can be DID ID, launcher coin ID or any coin ID of the DID you want to find.
|
|
2589
2680
|
The latest coin ID will take less time.
|
|
2590
2681
|
:return:
|
|
2591
2682
|
"""
|
|
2592
|
-
if "coin_id" not in request:
|
|
2593
|
-
return {"success": False, "error": "DID coin ID is required."}
|
|
2594
|
-
coin_id = request["coin_id"]
|
|
2595
2683
|
# Check if we have a DID wallet for this
|
|
2596
|
-
if coin_id.startswith(AddressType.DID.hrp(self.service.config)):
|
|
2597
|
-
coin_id = decode_puzzle_hash(coin_id)
|
|
2684
|
+
if request.coin_id.startswith(AddressType.DID.hrp(self.service.config)):
|
|
2685
|
+
coin_id = decode_puzzle_hash(request.coin_id)
|
|
2598
2686
|
else:
|
|
2599
|
-
coin_id = bytes32.from_hexstr(coin_id)
|
|
2687
|
+
coin_id = bytes32.from_hexstr(request.coin_id)
|
|
2600
2688
|
# Get coin state
|
|
2601
2689
|
peer = self.service.get_full_node_peer()
|
|
2602
2690
|
coin_spend, coin_state = await self.get_latest_singleton_coin_spend(peer, coin_id)
|
|
2603
2691
|
uncurried = uncurry_puzzle(coin_spend.puzzle_reveal)
|
|
2604
2692
|
curried_args = match_did_puzzle(uncurried.mod, uncurried.args)
|
|
2605
2693
|
if curried_args is None:
|
|
2606
|
-
|
|
2694
|
+
raise ValueError("The coin is not a DID.")
|
|
2607
2695
|
p2_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata = curried_args
|
|
2696
|
+
num_verification_int: Optional[uint16] = uint16(num_verification.as_int())
|
|
2697
|
+
assert num_verification_int is not None
|
|
2608
2698
|
did_data: DIDCoinData = DIDCoinData(
|
|
2609
2699
|
p2_puzzle,
|
|
2610
|
-
bytes32(recovery_list_hash.as_atom()),
|
|
2611
|
-
|
|
2700
|
+
bytes32(recovery_list_hash.as_atom()) if recovery_list_hash != Program.to(None) else None,
|
|
2701
|
+
num_verification_int,
|
|
2612
2702
|
singleton_struct,
|
|
2613
2703
|
metadata,
|
|
2614
2704
|
get_inner_puzzle_from_singleton(coin_spend.puzzle_reveal),
|
|
@@ -2636,7 +2726,7 @@ class WalletRpcApi:
|
|
|
2636
2726
|
|
|
2637
2727
|
launcher_id = bytes32(singleton_struct.rest().first().as_atom())
|
|
2638
2728
|
if derivation_record is None:
|
|
2639
|
-
|
|
2729
|
+
raise ValueError(f"This DID {launcher_id} does not belong to the connected wallet")
|
|
2640
2730
|
else:
|
|
2641
2731
|
our_inner_puzzle: Program = self.service.wallet_state_manager.main_wallet.puzzle_for_pk(
|
|
2642
2732
|
derivation_record.pubkey
|
|
@@ -2672,11 +2762,12 @@ class WalletRpcApi:
|
|
|
2672
2762
|
did_puzzle = did_wallet.did_info.current_inner
|
|
2673
2763
|
else:
|
|
2674
2764
|
# Try override
|
|
2675
|
-
if
|
|
2676
|
-
recovery_list_hash = Program.from_bytes(
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2765
|
+
if request.recovery_list_hash is not None:
|
|
2766
|
+
recovery_list_hash = Program.from_bytes(request.recovery_list_hash)
|
|
2767
|
+
if request.num_verification is not None:
|
|
2768
|
+
num_verification_int = request.num_verification
|
|
2769
|
+
if request.metadata is not None:
|
|
2770
|
+
metadata = metadata_to_program(request.metadata)
|
|
2680
2771
|
did_puzzle = DID_INNERPUZ_MOD.curry(
|
|
2681
2772
|
our_inner_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata
|
|
2682
2773
|
)
|
|
@@ -2708,17 +2799,16 @@ class WalletRpcApi:
|
|
|
2708
2799
|
)
|
|
2709
2800
|
|
|
2710
2801
|
if not matched:
|
|
2711
|
-
|
|
2712
|
-
"
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
}
|
|
2802
|
+
raise RuntimeError(
|
|
2803
|
+
f"Cannot recover DID {launcher_id} "
|
|
2804
|
+
f"because the last spend updated recovery_list_hash/num_verification/metadata."
|
|
2805
|
+
)
|
|
2716
2806
|
|
|
2717
2807
|
if did_wallet is None:
|
|
2718
2808
|
# Create DID wallet
|
|
2719
2809
|
response: list[CoinState] = await self.service.get_coin_state([launcher_id], peer=peer)
|
|
2720
2810
|
if len(response) == 0:
|
|
2721
|
-
|
|
2811
|
+
raise ValueError(f"Could not find the launch coin with ID: {launcher_id}")
|
|
2722
2812
|
launcher_coin: CoinState = response[0]
|
|
2723
2813
|
did_wallet = await DIDWallet.create_new_did_wallet_from_coin_spend(
|
|
2724
2814
|
self.service.wallet_state_manager,
|
|
@@ -2736,7 +2826,7 @@ class WalletRpcApi:
|
|
|
2736
2826
|
inner_solution: Program = full_solution.rest().rest().first()
|
|
2737
2827
|
recovery_list: list[bytes32] = []
|
|
2738
2828
|
backup_required: int = num_verification.as_int()
|
|
2739
|
-
if recovery_list_hash
|
|
2829
|
+
if not did_recovery_is_nil(recovery_list_hash):
|
|
2740
2830
|
try:
|
|
2741
2831
|
for did in inner_solution.rest().rest().rest().rest().rest().as_python():
|
|
2742
2832
|
recovery_list.append(did[0])
|
|
@@ -2761,7 +2851,7 @@ class WalletRpcApi:
|
|
|
2761
2851
|
try:
|
|
2762
2852
|
coin = await did_wallet.get_coin()
|
|
2763
2853
|
if coin.name() == coin_state.coin.name():
|
|
2764
|
-
return
|
|
2854
|
+
return DIDFindLostDIDResponse(coin.name())
|
|
2765
2855
|
except RuntimeError:
|
|
2766
2856
|
# We don't have any coin for this wallet, add the coin
|
|
2767
2857
|
pass
|
|
@@ -2779,70 +2869,61 @@ class WalletRpcApi:
|
|
|
2779
2869
|
peer,
|
|
2780
2870
|
did_data,
|
|
2781
2871
|
)
|
|
2782
|
-
return
|
|
2872
|
+
return DIDFindLostDIDResponse(coin_state.coin.name())
|
|
2783
2873
|
|
|
2784
2874
|
@tx_endpoint(push=True)
|
|
2875
|
+
@marshal
|
|
2785
2876
|
async def did_update_metadata(
|
|
2786
2877
|
self,
|
|
2787
|
-
request:
|
|
2878
|
+
request: DIDUpdateMetadata,
|
|
2788
2879
|
action_scope: WalletActionScope,
|
|
2789
2880
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
2790
|
-
) ->
|
|
2791
|
-
|
|
2792
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2793
|
-
metadata: dict[str, str] = {}
|
|
2794
|
-
if "metadata" in request and type(request["metadata"]) is dict:
|
|
2795
|
-
metadata = request["metadata"]
|
|
2881
|
+
) -> DIDUpdateMetadataResponse:
|
|
2882
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2796
2883
|
async with self.service.wallet_state_manager.lock:
|
|
2797
|
-
update_success = await wallet.update_metadata(metadata)
|
|
2884
|
+
update_success = await wallet.update_metadata(request.metadata)
|
|
2798
2885
|
# Update coin with new ID info
|
|
2799
2886
|
if update_success:
|
|
2800
|
-
await wallet.create_update_spend(
|
|
2801
|
-
|
|
2887
|
+
await wallet.create_update_spend(action_scope, request.fee, extra_conditions=extra_conditions)
|
|
2888
|
+
# tx_endpoint wrapper will take care of these default values
|
|
2889
|
+
return DIDUpdateMetadataResponse(
|
|
2890
|
+
[],
|
|
2891
|
+
[],
|
|
2892
|
+
wallet_id=request.wallet_id,
|
|
2893
|
+
spend_bundle=WalletSpendBundle([], G2Element()),
|
|
2802
2894
|
)
|
|
2803
|
-
return {
|
|
2804
|
-
"wallet_id": wallet_id,
|
|
2805
|
-
"success": True,
|
|
2806
|
-
"spend_bundle": None, # tx_endpoint wrapper will take care of this
|
|
2807
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
2808
|
-
}
|
|
2809
2895
|
else:
|
|
2810
|
-
|
|
2896
|
+
raise ValueError(f"Couldn't update metadata with input: {request.metadata}")
|
|
2811
2897
|
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2898
|
+
@marshal
|
|
2899
|
+
async def did_get_did(self, request: DIDGetDID) -> DIDGetDIDResponse:
|
|
2900
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2815
2901
|
my_did: str = encode_puzzle_hash(bytes32.fromhex(wallet.get_my_DID()), AddressType.DID.hrp(self.service.config))
|
|
2816
2902
|
async with self.service.wallet_state_manager.lock:
|
|
2817
2903
|
try:
|
|
2818
2904
|
coin = await wallet.get_coin()
|
|
2819
|
-
return
|
|
2905
|
+
return DIDGetDIDResponse(wallet_id=request.wallet_id, my_did=my_did, coin_id=coin.name())
|
|
2820
2906
|
except RuntimeError:
|
|
2821
|
-
return
|
|
2907
|
+
return DIDGetDIDResponse(wallet_id=request.wallet_id, my_did=my_did)
|
|
2822
2908
|
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2909
|
+
@marshal
|
|
2910
|
+
async def did_get_recovery_list(self, request: DIDGetRecoveryList) -> DIDGetRecoveryListResponse:
|
|
2911
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2826
2912
|
recovery_list = wallet.did_info.backup_ids
|
|
2827
2913
|
recovery_dids = []
|
|
2828
2914
|
for backup_id in recovery_list:
|
|
2829
2915
|
recovery_dids.append(encode_puzzle_hash(backup_id, AddressType.DID.hrp(self.service.config)))
|
|
2830
|
-
return
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
}
|
|
2916
|
+
return DIDGetRecoveryListResponse(
|
|
2917
|
+
wallet_id=request.wallet_id,
|
|
2918
|
+
recovery_list=recovery_dids,
|
|
2919
|
+
num_required=uint16(wallet.did_info.num_of_backup_ids_needed),
|
|
2920
|
+
)
|
|
2836
2921
|
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2922
|
+
@marshal
|
|
2923
|
+
async def did_get_metadata(self, request: DIDGetMetadata) -> DIDGetMetadataResponse:
|
|
2924
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2840
2925
|
metadata = json.loads(wallet.did_info.metadata)
|
|
2841
|
-
return
|
|
2842
|
-
"success": True,
|
|
2843
|
-
"wallet_id": wallet_id,
|
|
2844
|
-
"metadata": metadata,
|
|
2845
|
-
}
|
|
2926
|
+
return DIDGetMetadataResponse(wallet_id=request.wallet_id, metadata=metadata)
|
|
2846
2927
|
|
|
2847
2928
|
# TODO: this needs a test
|
|
2848
2929
|
# Don't need full @tx_endpoint decorator here, but "push" is still a valid option
|
|
@@ -2888,11 +2969,12 @@ class WalletRpcApi:
|
|
|
2888
2969
|
"transactions": [tx.to_json_dict_convenience(self.service.config)],
|
|
2889
2970
|
}
|
|
2890
2971
|
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2894
|
-
|
|
2895
|
-
|
|
2972
|
+
@marshal
|
|
2973
|
+
async def did_get_pubkey(self, request: DIDGetPubkey) -> DIDGetPubkeyResponse:
|
|
2974
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2975
|
+
return DIDGetPubkeyResponse(
|
|
2976
|
+
(await wallet.wallet_state_manager.get_unused_derivation_record(request.wallet_id)).pubkey
|
|
2977
|
+
)
|
|
2896
2978
|
|
|
2897
2979
|
# TODO: this needs a test
|
|
2898
2980
|
@tx_endpoint(push=True)
|
|
@@ -2926,27 +3008,28 @@ class WalletRpcApi:
|
|
|
2926
3008
|
else:
|
|
2927
3009
|
return {"success": False}
|
|
2928
3010
|
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
3011
|
+
@marshal
|
|
3012
|
+
async def did_get_information_needed_for_recovery(self, request: DIDGetRecoveryInfo) -> DIDGetRecoveryInfoResponse:
|
|
3013
|
+
did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2932
3014
|
my_did = encode_puzzle_hash(
|
|
2933
3015
|
bytes32.from_hexstr(did_wallet.get_my_DID()), AddressType.DID.hrp(self.service.config)
|
|
2934
3016
|
)
|
|
2935
3017
|
assert did_wallet.did_info.temp_coin is not None
|
|
2936
|
-
coin_name = did_wallet.did_info.temp_coin.name()
|
|
2937
|
-
return
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3018
|
+
coin_name = did_wallet.did_info.temp_coin.name()
|
|
3019
|
+
return DIDGetRecoveryInfoResponse(
|
|
3020
|
+
wallet_id=request.wallet_id,
|
|
3021
|
+
my_did=my_did,
|
|
3022
|
+
coin_name=coin_name,
|
|
3023
|
+
newpuzhash=did_wallet.did_info.temp_puzhash,
|
|
3024
|
+
pubkey=G1Element.from_bytes(did_wallet.did_info.temp_pubkey)
|
|
3025
|
+
if did_wallet.did_info.temp_pubkey is not None
|
|
3026
|
+
else None,
|
|
3027
|
+
backup_dids=did_wallet.did_info.backup_ids,
|
|
3028
|
+
)
|
|
2946
3029
|
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
3030
|
+
@marshal
|
|
3031
|
+
async def did_get_current_coin_info(self, request: DIDGetCurrentCoinInfo) -> DIDGetCurrentCoinInfoResponse:
|
|
3032
|
+
did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
2950
3033
|
my_did = encode_puzzle_hash(
|
|
2951
3034
|
bytes32.from_hexstr(did_wallet.get_my_DID()), AddressType.DID.hrp(self.service.config)
|
|
2952
3035
|
)
|
|
@@ -2954,210 +3037,177 @@ class WalletRpcApi:
|
|
|
2954
3037
|
did_coin_threeple = await did_wallet.get_info_for_recovery()
|
|
2955
3038
|
assert my_did is not None
|
|
2956
3039
|
assert did_coin_threeple is not None
|
|
2957
|
-
return
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
}
|
|
3040
|
+
return DIDGetCurrentCoinInfoResponse(
|
|
3041
|
+
wallet_id=request.wallet_id,
|
|
3042
|
+
my_did=my_did,
|
|
3043
|
+
did_parent=did_coin_threeple[0],
|
|
3044
|
+
did_innerpuz=did_coin_threeple[1],
|
|
3045
|
+
did_amount=did_coin_threeple[2],
|
|
3046
|
+
)
|
|
2965
3047
|
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
did_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet)
|
|
2969
|
-
return
|
|
3048
|
+
@marshal
|
|
3049
|
+
async def did_create_backup_file(self, request: DIDCreateBackupFile) -> DIDCreateBackupFileResponse:
|
|
3050
|
+
did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
3051
|
+
return DIDCreateBackupFileResponse(wallet_id=request.wallet_id, backup_data=did_wallet.create_backup())
|
|
2970
3052
|
|
|
2971
3053
|
@tx_endpoint(push=True)
|
|
3054
|
+
@marshal
|
|
2972
3055
|
async def did_transfer_did(
|
|
2973
3056
|
self,
|
|
2974
|
-
request:
|
|
3057
|
+
request: DIDTransferDID,
|
|
2975
3058
|
action_scope: WalletActionScope,
|
|
2976
3059
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
2977
|
-
) ->
|
|
3060
|
+
) -> DIDTransferDIDResponse:
|
|
2978
3061
|
if await self.service.wallet_state_manager.synced() is False:
|
|
2979
3062
|
raise ValueError("Wallet needs to be fully synced.")
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
puzzle_hash: bytes32 = decode_puzzle_hash(request["inner_address"])
|
|
3063
|
+
did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet)
|
|
3064
|
+
puzzle_hash: bytes32 = decode_puzzle_hash(request.inner_address)
|
|
2983
3065
|
async with self.service.wallet_state_manager.lock:
|
|
2984
3066
|
await did_wallet.transfer_did(
|
|
2985
3067
|
puzzle_hash,
|
|
2986
|
-
|
|
2987
|
-
request.
|
|
3068
|
+
request.fee,
|
|
3069
|
+
request.with_recovery_info,
|
|
2988
3070
|
action_scope,
|
|
2989
3071
|
extra_conditions=extra_conditions,
|
|
2990
3072
|
)
|
|
2991
3073
|
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
"transaction": None, # tx_endpoint wrapper will take care of this
|
|
2995
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
2996
|
-
"transaction_id": None, # tx_endpoint wrapper will take care of this
|
|
2997
|
-
}
|
|
3074
|
+
# The tx_endpoint wrapper will take care of these default values
|
|
3075
|
+
return DIDTransferDIDResponse([], [], transaction=REPLACEABLE_TRANSACTION_RECORD, transaction_id=bytes32.zeros)
|
|
2998
3076
|
|
|
2999
3077
|
##########################################################################################
|
|
3000
3078
|
# NFT Wallet
|
|
3001
3079
|
##########################################################################################
|
|
3002
3080
|
@tx_endpoint(push=True)
|
|
3081
|
+
@marshal
|
|
3003
3082
|
async def nft_mint_nft(
|
|
3004
3083
|
self,
|
|
3005
|
-
request:
|
|
3084
|
+
request: NFTMintNFTRequest,
|
|
3006
3085
|
action_scope: WalletActionScope,
|
|
3007
3086
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3008
|
-
) ->
|
|
3087
|
+
) -> NFTMintNFTResponse:
|
|
3009
3088
|
log.debug("Got minting RPC request: %s", request)
|
|
3010
|
-
wallet_id = uint32(request["wallet_id"])
|
|
3011
3089
|
assert self.service.wallet_state_manager
|
|
3012
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3013
|
-
|
|
3014
|
-
royalty_amount = uint16(request.get("royalty_percentage", 0))
|
|
3015
|
-
if royalty_amount == 10000:
|
|
3090
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3091
|
+
if request.royalty_amount == 10000:
|
|
3016
3092
|
raise ValueError("Royalty percentage cannot be 100%")
|
|
3017
|
-
if
|
|
3018
|
-
royalty_puzhash = decode_puzzle_hash(royalty_address)
|
|
3019
|
-
elif royalty_address is None:
|
|
3020
|
-
royalty_puzhash = await nft_wallet.standard_wallet.get_puzzle_hash(
|
|
3021
|
-
new=not action_scope.config.tx_config.reuse_puzhash
|
|
3022
|
-
)
|
|
3093
|
+
if request.royalty_address is not None:
|
|
3094
|
+
royalty_puzhash = decode_puzzle_hash(request.royalty_address)
|
|
3023
3095
|
else:
|
|
3024
|
-
royalty_puzhash =
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
target_puzhash = decode_puzzle_hash(target_address)
|
|
3028
|
-
elif target_address is None:
|
|
3029
|
-
target_puzhash = await nft_wallet.standard_wallet.get_puzzle_hash(
|
|
3030
|
-
new=not action_scope.config.tx_config.reuse_puzhash
|
|
3031
|
-
)
|
|
3096
|
+
royalty_puzhash = await action_scope.get_puzzle_hash(self.service.wallet_state_manager)
|
|
3097
|
+
if request.target_address is not None:
|
|
3098
|
+
target_puzhash = decode_puzzle_hash(request.target_address)
|
|
3032
3099
|
else:
|
|
3033
|
-
target_puzhash =
|
|
3034
|
-
if "uris" not in request:
|
|
3035
|
-
return {"success": False, "error": "Data URIs is required"}
|
|
3036
|
-
if not isinstance(request["uris"], list):
|
|
3037
|
-
return {"success": False, "error": "Data URIs must be a list"}
|
|
3038
|
-
if not isinstance(request.get("meta_uris", []), list):
|
|
3039
|
-
return {"success": False, "error": "Metadata URIs must be a list"}
|
|
3040
|
-
if not isinstance(request.get("license_uris", []), list):
|
|
3041
|
-
return {"success": False, "error": "License URIs must be a list"}
|
|
3100
|
+
target_puzhash = await action_scope.get_puzzle_hash(self.service.wallet_state_manager)
|
|
3042
3101
|
metadata_list = [
|
|
3043
|
-
("u", request
|
|
3044
|
-
("h",
|
|
3045
|
-
("mu", request.
|
|
3046
|
-
("lu", request.
|
|
3047
|
-
("sn",
|
|
3048
|
-
("st",
|
|
3102
|
+
("u", request.uris),
|
|
3103
|
+
("h", request.hash),
|
|
3104
|
+
("mu", request.meta_uris),
|
|
3105
|
+
("lu", request.license_uris),
|
|
3106
|
+
("sn", request.edition_number),
|
|
3107
|
+
("st", request.edition_total),
|
|
3049
3108
|
]
|
|
3050
|
-
if
|
|
3051
|
-
metadata_list.append(("mh",
|
|
3052
|
-
if
|
|
3053
|
-
metadata_list.append(("lh",
|
|
3109
|
+
if request.meta_hash is not None:
|
|
3110
|
+
metadata_list.append(("mh", request.meta_hash))
|
|
3111
|
+
if request.license_hash is not None:
|
|
3112
|
+
metadata_list.append(("lh", request.license_hash))
|
|
3054
3113
|
metadata = Program.to(metadata_list)
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
if did_id == "":
|
|
3059
|
-
did_id = b""
|
|
3114
|
+
if request.did_id is not None:
|
|
3115
|
+
if request.did_id == "":
|
|
3116
|
+
did_id: Optional[bytes] = b""
|
|
3060
3117
|
else:
|
|
3061
|
-
did_id = decode_puzzle_hash(did_id)
|
|
3118
|
+
did_id = decode_puzzle_hash(request.did_id)
|
|
3119
|
+
else:
|
|
3120
|
+
did_id = request.did_id
|
|
3062
3121
|
|
|
3063
3122
|
nft_id = await nft_wallet.generate_new_nft(
|
|
3064
3123
|
metadata,
|
|
3065
3124
|
action_scope,
|
|
3066
3125
|
target_puzhash,
|
|
3067
3126
|
royalty_puzhash,
|
|
3068
|
-
royalty_amount,
|
|
3127
|
+
request.royalty_amount,
|
|
3069
3128
|
did_id,
|
|
3070
|
-
fee,
|
|
3129
|
+
request.fee,
|
|
3071
3130
|
extra_conditions=extra_conditions,
|
|
3072
3131
|
)
|
|
3073
3132
|
nft_id_bech32 = encode_puzzle_hash(nft_id, AddressType.NFT.hrp(self.service.config))
|
|
3074
|
-
return
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3133
|
+
return NFTMintNFTResponse(
|
|
3134
|
+
[],
|
|
3135
|
+
[],
|
|
3136
|
+
wallet_id=request.wallet_id,
|
|
3137
|
+
spend_bundle=WalletSpendBundle([], G2Element()), # tx_endpoint wrapper will take care of this
|
|
3138
|
+
nft_id=nft_id_bech32,
|
|
3139
|
+
)
|
|
3081
3140
|
|
|
3082
|
-
|
|
3083
|
-
|
|
3141
|
+
@marshal
|
|
3142
|
+
async def nft_count_nfts(self, request: NFTCountNFTs) -> NFTCountNFTsResponse:
|
|
3084
3143
|
count = 0
|
|
3085
|
-
if wallet_id is not None:
|
|
3144
|
+
if request.wallet_id is not None:
|
|
3086
3145
|
try:
|
|
3087
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3146
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3088
3147
|
except KeyError:
|
|
3089
3148
|
# wallet not found
|
|
3090
|
-
|
|
3149
|
+
raise ValueError(f"Wallet {request.wallet_id} not found.")
|
|
3091
3150
|
count = await nft_wallet.get_nft_count()
|
|
3092
3151
|
else:
|
|
3093
3152
|
count = await self.service.wallet_state_manager.nft_store.count()
|
|
3094
|
-
return
|
|
3153
|
+
return NFTCountNFTsResponse(request.wallet_id, uint64(count))
|
|
3095
3154
|
|
|
3096
|
-
|
|
3097
|
-
|
|
3155
|
+
@marshal
|
|
3156
|
+
async def nft_get_nfts(self, request: NFTGetNFTs) -> NFTGetNFTsResponse:
|
|
3098
3157
|
nfts: list[NFTCoinInfo] = []
|
|
3099
|
-
if wallet_id is not None:
|
|
3100
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3158
|
+
if request.wallet_id is not None:
|
|
3159
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3101
3160
|
else:
|
|
3102
3161
|
nft_wallet = None
|
|
3103
|
-
try:
|
|
3104
|
-
start_index = int(request.get("start_index", 0))
|
|
3105
|
-
except (TypeError, ValueError):
|
|
3106
|
-
start_index = 0
|
|
3107
|
-
try:
|
|
3108
|
-
count = int(request.get("num", 50))
|
|
3109
|
-
except (TypeError, ValueError):
|
|
3110
|
-
count = 50
|
|
3111
3162
|
nft_info_list = []
|
|
3112
3163
|
if nft_wallet is not None:
|
|
3113
|
-
nfts = await nft_wallet.get_current_nfts(start_index=start_index, count=
|
|
3164
|
+
nfts = await nft_wallet.get_current_nfts(start_index=request.start_index, count=request.num)
|
|
3114
3165
|
else:
|
|
3115
|
-
nfts = await self.service.wallet_state_manager.nft_store.get_nft_list(
|
|
3166
|
+
nfts = await self.service.wallet_state_manager.nft_store.get_nft_list(
|
|
3167
|
+
start_index=request.start_index, count=request.num
|
|
3168
|
+
)
|
|
3116
3169
|
for nft in nfts:
|
|
3117
3170
|
nft_info = await nft_puzzle_utils.get_nft_info_from_puzzle(nft, self.service.wallet_state_manager.config)
|
|
3118
3171
|
nft_info_list.append(nft_info)
|
|
3119
|
-
return
|
|
3172
|
+
return NFTGetNFTsResponse(request.wallet_id, nft_info_list)
|
|
3120
3173
|
|
|
3121
3174
|
@tx_endpoint(push=True)
|
|
3175
|
+
@marshal
|
|
3122
3176
|
async def nft_set_nft_did(
|
|
3123
3177
|
self,
|
|
3124
|
-
request:
|
|
3178
|
+
request: NFTSetNFTDID,
|
|
3125
3179
|
action_scope: WalletActionScope,
|
|
3126
3180
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3127
|
-
) ->
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
did_id =
|
|
3133
|
-
nft_coin_info = await nft_wallet.get_nft_coin_by_id(
|
|
3181
|
+
) -> NFTSetNFTDIDResponse:
|
|
3182
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3183
|
+
if request.did_id is not None:
|
|
3184
|
+
did_id: bytes = decode_puzzle_hash(request.did_id)
|
|
3185
|
+
else:
|
|
3186
|
+
did_id = b""
|
|
3187
|
+
nft_coin_info = await nft_wallet.get_nft_coin_by_id(request.nft_coin_id)
|
|
3134
3188
|
if not (
|
|
3135
3189
|
await nft_puzzle_utils.get_nft_info_from_puzzle(nft_coin_info, self.service.wallet_state_manager.config)
|
|
3136
3190
|
).supports_did:
|
|
3137
|
-
|
|
3191
|
+
raise ValueError("The NFT doesn't support setting a DID.")
|
|
3138
3192
|
|
|
3139
|
-
fee = uint64(request.get("fee", 0))
|
|
3140
3193
|
await nft_wallet.set_nft_did(
|
|
3141
3194
|
nft_coin_info,
|
|
3142
3195
|
did_id,
|
|
3143
3196
|
action_scope,
|
|
3144
|
-
fee=fee,
|
|
3197
|
+
fee=request.fee,
|
|
3145
3198
|
extra_conditions=extra_conditions,
|
|
3146
3199
|
)
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
"success": True,
|
|
3150
|
-
"spend_bundle": None, # tx_endpoint wrapper will take care of this
|
|
3151
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
3152
|
-
}
|
|
3200
|
+
# tx_endpoint wrapper takes care of setting most of these default values
|
|
3201
|
+
return NFTSetNFTDIDResponse([], [], request.wallet_id, WalletSpendBundle([], G2Element()))
|
|
3153
3202
|
|
|
3154
3203
|
@tx_endpoint(push=True)
|
|
3204
|
+
@marshal
|
|
3155
3205
|
async def nft_set_did_bulk(
|
|
3156
3206
|
self,
|
|
3157
|
-
request:
|
|
3207
|
+
request: NFTSetDIDBulk,
|
|
3158
3208
|
action_scope: WalletActionScope,
|
|
3159
3209
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3160
|
-
) ->
|
|
3210
|
+
) -> NFTSetDIDBulkResponse:
|
|
3161
3211
|
"""
|
|
3162
3212
|
Bulk set DID for NFTs across different wallets.
|
|
3163
3213
|
accepted `request` dict keys:
|
|
@@ -3167,40 +3217,34 @@ class WalletRpcApi:
|
|
|
3167
3217
|
:param request:
|
|
3168
3218
|
:return:
|
|
3169
3219
|
"""
|
|
3170
|
-
if len(request
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
if did_id
|
|
3174
|
-
did_id = decode_puzzle_hash(did_id)
|
|
3220
|
+
if len(request.nft_coin_list) > MAX_NFT_CHUNK_SIZE:
|
|
3221
|
+
raise ValueError(f"You can only set {MAX_NFT_CHUNK_SIZE} NFTs at once")
|
|
3222
|
+
|
|
3223
|
+
if request.did_id is not None:
|
|
3224
|
+
did_id: bytes = decode_puzzle_hash(request.did_id)
|
|
3225
|
+
else:
|
|
3226
|
+
did_id = b""
|
|
3175
3227
|
nft_dict: dict[uint32, list[NFTCoinInfo]] = {}
|
|
3176
3228
|
coin_ids = []
|
|
3177
3229
|
nft_ids = []
|
|
3178
|
-
fee = uint64(request.get("fee", 0))
|
|
3179
3230
|
|
|
3180
3231
|
nft_wallet: NFTWallet
|
|
3181
|
-
for nft_coin in request
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
wallet_id = uint32(nft_coin["wallet_id"])
|
|
3186
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3187
|
-
nft_coin_id = nft_coin["nft_coin_id"]
|
|
3188
|
-
if nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3189
|
-
nft_id = decode_puzzle_hash(nft_coin_id)
|
|
3190
|
-
nft_coin_info = await nft_wallet.get_nft(nft_id)
|
|
3232
|
+
for nft_coin in request.nft_coin_list:
|
|
3233
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=nft_coin.wallet_id, required_type=NFTWallet)
|
|
3234
|
+
if nft_coin.nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3235
|
+
nft_coin_info = await nft_wallet.get_nft(decode_puzzle_hash(nft_coin.nft_coin_id))
|
|
3191
3236
|
else:
|
|
3192
|
-
|
|
3193
|
-
nft_coin_info = await nft_wallet.get_nft_coin_by_id(nft_coin_id)
|
|
3237
|
+
nft_coin_info = await nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(nft_coin.nft_coin_id))
|
|
3194
3238
|
assert nft_coin_info is not None
|
|
3195
3239
|
if not (
|
|
3196
3240
|
await nft_puzzle_utils.get_nft_info_from_puzzle(nft_coin_info, self.service.wallet_state_manager.config)
|
|
3197
3241
|
).supports_did:
|
|
3198
3242
|
log.warning(f"Skipping NFT {nft_coin_info.nft_id.hex()}, doesn't support setting a DID.")
|
|
3199
3243
|
continue
|
|
3200
|
-
if wallet_id in nft_dict:
|
|
3201
|
-
nft_dict[wallet_id].append(nft_coin_info)
|
|
3244
|
+
if nft_coin.wallet_id in nft_dict:
|
|
3245
|
+
nft_dict[nft_coin.wallet_id].append(nft_coin_info)
|
|
3202
3246
|
else:
|
|
3203
|
-
nft_dict[wallet_id] = [nft_coin_info]
|
|
3247
|
+
nft_dict[nft_coin.wallet_id] = [nft_coin_info]
|
|
3204
3248
|
nft_ids.append(nft_coin_info.nft_id)
|
|
3205
3249
|
first = True
|
|
3206
3250
|
for wallet_id, nft_list in nft_dict.items():
|
|
@@ -3209,7 +3253,7 @@ class WalletRpcApi:
|
|
|
3209
3253
|
await nft_wallet.set_bulk_nft_did(nft_list, did_id, action_scope, extra_conditions=extra_conditions)
|
|
3210
3254
|
else:
|
|
3211
3255
|
await nft_wallet.set_bulk_nft_did(
|
|
3212
|
-
nft_list, did_id, action_scope, fee, nft_ids, extra_conditions=extra_conditions
|
|
3256
|
+
nft_list, did_id, action_scope, request.fee, nft_ids, extra_conditions=extra_conditions
|
|
3213
3257
|
)
|
|
3214
3258
|
for coin in nft_list:
|
|
3215
3259
|
coin_ids.append(coin.coin.name())
|
|
@@ -3221,21 +3265,22 @@ class WalletRpcApi:
|
|
|
3221
3265
|
self.service.wallet_state_manager.state_changed("nft_coin_did_set", wallet_id)
|
|
3222
3266
|
|
|
3223
3267
|
async with action_scope.use() as interface:
|
|
3224
|
-
return
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3268
|
+
return NFTSetDIDBulkResponse(
|
|
3269
|
+
[],
|
|
3270
|
+
[],
|
|
3271
|
+
wallet_id=list(nft_dict.keys()),
|
|
3272
|
+
spend_bundle=WalletSpendBundle([], G2Element()),
|
|
3273
|
+
tx_num=uint16(len(interface.side_effects.transactions)),
|
|
3274
|
+
)
|
|
3231
3275
|
|
|
3232
3276
|
@tx_endpoint(push=True)
|
|
3277
|
+
@marshal
|
|
3233
3278
|
async def nft_transfer_bulk(
|
|
3234
3279
|
self,
|
|
3235
|
-
request:
|
|
3280
|
+
request: NFTTransferBulk,
|
|
3236
3281
|
action_scope: WalletActionScope,
|
|
3237
3282
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3238
|
-
) ->
|
|
3283
|
+
) -> NFTTransferBulkResponse:
|
|
3239
3284
|
"""
|
|
3240
3285
|
Bulk transfer NFTs to an address.
|
|
3241
3286
|
accepted `request` dict keys:
|
|
@@ -3245,36 +3290,26 @@ class WalletRpcApi:
|
|
|
3245
3290
|
:param request:
|
|
3246
3291
|
:return:
|
|
3247
3292
|
"""
|
|
3248
|
-
if len(request
|
|
3249
|
-
|
|
3250
|
-
address = request
|
|
3251
|
-
|
|
3252
|
-
puzzle_hash = decode_puzzle_hash(address)
|
|
3253
|
-
else:
|
|
3254
|
-
return dict(success=False, error="target_address parameter missing")
|
|
3293
|
+
if len(request.nft_coin_list) > MAX_NFT_CHUNK_SIZE:
|
|
3294
|
+
raise ValueError(f"You can only transfer {MAX_NFT_CHUNK_SIZE} NFTs at once")
|
|
3295
|
+
address = request.target_address
|
|
3296
|
+
puzzle_hash = decode_puzzle_hash(address)
|
|
3255
3297
|
nft_dict: dict[uint32, list[NFTCoinInfo]] = {}
|
|
3256
3298
|
coin_ids = []
|
|
3257
|
-
fee = uint64(request.get("fee", 0))
|
|
3258
3299
|
|
|
3259
3300
|
nft_wallet: NFTWallet
|
|
3260
|
-
for nft_coin in request
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
continue
|
|
3264
|
-
wallet_id = uint32(nft_coin["wallet_id"])
|
|
3265
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3266
|
-
nft_coin_id = nft_coin["nft_coin_id"]
|
|
3301
|
+
for nft_coin in request.nft_coin_list:
|
|
3302
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=nft_coin.wallet_id, required_type=NFTWallet)
|
|
3303
|
+
nft_coin_id = nft_coin.nft_coin_id
|
|
3267
3304
|
if nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3268
|
-
|
|
3269
|
-
nft_coin_info = await nft_wallet.get_nft(nft_id)
|
|
3305
|
+
nft_coin_info = await nft_wallet.get_nft(decode_puzzle_hash(nft_coin_id))
|
|
3270
3306
|
else:
|
|
3271
|
-
|
|
3272
|
-
nft_coin_info = await nft_wallet.get_nft_coin_by_id(nft_coin_id)
|
|
3307
|
+
nft_coin_info = await nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(nft_coin_id))
|
|
3273
3308
|
assert nft_coin_info is not None
|
|
3274
|
-
if wallet_id in nft_dict:
|
|
3275
|
-
nft_dict[wallet_id].append(nft_coin_info)
|
|
3309
|
+
if nft_coin.wallet_id in nft_dict:
|
|
3310
|
+
nft_dict[nft_coin.wallet_id].append(nft_coin_info)
|
|
3276
3311
|
else:
|
|
3277
|
-
nft_dict[wallet_id] = [nft_coin_info]
|
|
3312
|
+
nft_dict[nft_coin.wallet_id] = [nft_coin_info]
|
|
3278
3313
|
first = True
|
|
3279
3314
|
for wallet_id, nft_list in nft_dict.items():
|
|
3280
3315
|
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
@@ -3284,7 +3319,7 @@ class WalletRpcApi:
|
|
|
3284
3319
|
)
|
|
3285
3320
|
else:
|
|
3286
3321
|
await nft_wallet.bulk_transfer_nft(
|
|
3287
|
-
nft_list, puzzle_hash, action_scope, fee, extra_conditions=extra_conditions
|
|
3322
|
+
nft_list, puzzle_hash, action_scope, request.fee, extra_conditions=extra_conditions
|
|
3288
3323
|
)
|
|
3289
3324
|
for coin in nft_list:
|
|
3290
3325
|
coin_ids.append(coin.coin.name())
|
|
@@ -3295,33 +3330,35 @@ class WalletRpcApi:
|
|
|
3295
3330
|
for wallet_id in nft_dict.keys():
|
|
3296
3331
|
self.service.wallet_state_manager.state_changed("nft_coin_did_set", wallet_id)
|
|
3297
3332
|
async with action_scope.use() as interface:
|
|
3298
|
-
return
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3333
|
+
return NFTTransferBulkResponse(
|
|
3334
|
+
[],
|
|
3335
|
+
[],
|
|
3336
|
+
wallet_id=list(nft_dict.keys()),
|
|
3337
|
+
spend_bundle=WalletSpendBundle([], G2Element()),
|
|
3338
|
+
tx_num=uint16(len(interface.side_effects.transactions)),
|
|
3339
|
+
)
|
|
3305
3340
|
|
|
3306
|
-
|
|
3341
|
+
@marshal
|
|
3342
|
+
async def nft_get_by_did(self, request: NFTGetByDID) -> NFTGetByDIDResponse:
|
|
3307
3343
|
did_id: Optional[bytes32] = None
|
|
3308
|
-
if request.
|
|
3309
|
-
did_id = decode_puzzle_hash(request
|
|
3344
|
+
if request.did_id is not None:
|
|
3345
|
+
did_id = decode_puzzle_hash(request.did_id)
|
|
3310
3346
|
for wallet in self.service.wallet_state_manager.wallets.values():
|
|
3311
3347
|
if isinstance(wallet, NFTWallet) and wallet.get_did() == did_id:
|
|
3312
|
-
return
|
|
3313
|
-
|
|
3348
|
+
return NFTGetByDIDResponse(uint32(wallet.wallet_id))
|
|
3349
|
+
raise ValueError(f"Cannot find a NFT wallet DID = {did_id}")
|
|
3314
3350
|
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3351
|
+
@marshal
|
|
3352
|
+
async def nft_get_wallet_did(self, request: NFTGetWalletDID) -> NFTGetWalletDIDResponse:
|
|
3353
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3318
3354
|
did_bytes: Optional[bytes32] = nft_wallet.get_did()
|
|
3319
3355
|
did_id = ""
|
|
3320
3356
|
if did_bytes is not None:
|
|
3321
3357
|
did_id = encode_puzzle_hash(did_bytes, AddressType.DID.hrp(self.service.config))
|
|
3322
|
-
return
|
|
3358
|
+
return NFTGetWalletDIDResponse(None if len(did_id) == 0 else did_id)
|
|
3323
3359
|
|
|
3324
|
-
|
|
3360
|
+
@marshal
|
|
3361
|
+
async def nft_get_wallets_with_dids(self, request: Empty) -> NFTGetWalletsWithDIDsResponse:
|
|
3325
3362
|
all_wallets = self.service.wallet_state_manager.wallets.values()
|
|
3326
3363
|
did_wallets_by_did_id: dict[bytes32, uint32] = {}
|
|
3327
3364
|
|
|
@@ -3331,7 +3368,7 @@ class WalletRpcApi:
|
|
|
3331
3368
|
if wallet.did_info.origin_coin is not None:
|
|
3332
3369
|
did_wallets_by_did_id[wallet.did_info.origin_coin.name()] = wallet.id()
|
|
3333
3370
|
|
|
3334
|
-
did_nft_wallets: list[
|
|
3371
|
+
did_nft_wallets: list[NFTWalletWithDID] = []
|
|
3335
3372
|
for wallet in all_wallets:
|
|
3336
3373
|
if isinstance(wallet, NFTWallet):
|
|
3337
3374
|
nft_wallet_did: Optional[bytes32] = wallet.get_did()
|
|
@@ -3341,97 +3378,78 @@ class WalletRpcApi:
|
|
|
3341
3378
|
log.warning(f"NFT wallet {wallet.id()} has DID {nft_wallet_did.hex()} but no DID wallet")
|
|
3342
3379
|
else:
|
|
3343
3380
|
did_nft_wallets.append(
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3381
|
+
NFTWalletWithDID(
|
|
3382
|
+
wallet_id=wallet.id(),
|
|
3383
|
+
did_id=encode_puzzle_hash(nft_wallet_did, AddressType.DID.hrp(self.service.config)),
|
|
3384
|
+
did_wallet_id=did_wallet_id,
|
|
3385
|
+
)
|
|
3349
3386
|
)
|
|
3350
|
-
return
|
|
3387
|
+
return NFTGetWalletsWithDIDsResponse(did_nft_wallets)
|
|
3351
3388
|
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
coin_id: bytes32 = bytes32.from_hexstr(request["coin_id"])
|
|
3355
|
-
status: bool = request["in_transaction"]
|
|
3389
|
+
@marshal
|
|
3390
|
+
async def nft_set_nft_status(self, request: NFTSetNFTStatus) -> Empty:
|
|
3356
3391
|
assert self.service.wallet_state_manager is not None
|
|
3357
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3358
|
-
await nft_wallet.update_coin_status(coin_id,
|
|
3359
|
-
return
|
|
3392
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3393
|
+
await nft_wallet.update_coin_status(request.coin_id, request.in_transaction)
|
|
3394
|
+
return Empty()
|
|
3360
3395
|
|
|
3361
3396
|
@tx_endpoint(push=True)
|
|
3397
|
+
@marshal
|
|
3362
3398
|
async def nft_transfer_nft(
|
|
3363
3399
|
self,
|
|
3364
|
-
request:
|
|
3400
|
+
request: NFTTransferNFT,
|
|
3365
3401
|
action_scope: WalletActionScope,
|
|
3366
3402
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3367
|
-
) ->
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3403
|
+
) -> NFTTransferNFTResponse:
|
|
3404
|
+
puzzle_hash = decode_puzzle_hash(request.target_address)
|
|
3405
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3406
|
+
nft_coin_id = request.nft_coin_id
|
|
3407
|
+
if nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3408
|
+
nft_coin_info = await nft_wallet.get_nft(decode_puzzle_hash(nft_coin_id))
|
|
3372
3409
|
else:
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
try:
|
|
3376
|
-
nft_coin_id = request["nft_coin_id"]
|
|
3377
|
-
if nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3378
|
-
nft_id = decode_puzzle_hash(nft_coin_id)
|
|
3379
|
-
nft_coin_info = await nft_wallet.get_nft(nft_id)
|
|
3380
|
-
else:
|
|
3381
|
-
nft_coin_id = bytes32.from_hexstr(nft_coin_id)
|
|
3382
|
-
nft_coin_info = await nft_wallet.get_nft_coin_by_id(nft_coin_id)
|
|
3383
|
-
assert nft_coin_info is not None
|
|
3410
|
+
nft_coin_info = await nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(nft_coin_id))
|
|
3411
|
+
assert nft_coin_info is not None
|
|
3384
3412
|
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
except Exception as e:
|
|
3404
|
-
log.exception(f"Failed to transfer NFT: {e}")
|
|
3405
|
-
return {"success": False, "error": str(e)}
|
|
3406
|
-
|
|
3407
|
-
async def nft_get_info(self, request: dict[str, Any]) -> EndpointResult:
|
|
3408
|
-
if "coin_id" not in request:
|
|
3409
|
-
return {"success": False, "error": "Coin ID is required."}
|
|
3410
|
-
coin_id = request["coin_id"]
|
|
3411
|
-
if coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3412
|
-
coin_id = decode_puzzle_hash(coin_id)
|
|
3413
|
+
await nft_wallet.generate_signed_transaction(
|
|
3414
|
+
[uint64(nft_coin_info.coin.amount)],
|
|
3415
|
+
[puzzle_hash],
|
|
3416
|
+
action_scope,
|
|
3417
|
+
coins={nft_coin_info.coin},
|
|
3418
|
+
fee=request.fee,
|
|
3419
|
+
new_owner=b"",
|
|
3420
|
+
new_did_inner_hash=b"",
|
|
3421
|
+
extra_conditions=extra_conditions,
|
|
3422
|
+
)
|
|
3423
|
+
await nft_wallet.update_coin_status(nft_coin_info.coin.name(), True)
|
|
3424
|
+
# tx_endpoint takes care of filling in default values here
|
|
3425
|
+
return NFTTransferNFTResponse([], [], request.wallet_id, WalletSpendBundle([], G2Element()))
|
|
3426
|
+
|
|
3427
|
+
@marshal
|
|
3428
|
+
async def nft_get_info(self, request: NFTGetInfo) -> NFTGetInfoResponse:
|
|
3429
|
+
if request.coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3430
|
+
coin_id = decode_puzzle_hash(request.coin_id)
|
|
3413
3431
|
else:
|
|
3414
3432
|
try:
|
|
3415
|
-
coin_id = bytes32.from_hexstr(coin_id)
|
|
3433
|
+
coin_id = bytes32.from_hexstr(request.coin_id)
|
|
3416
3434
|
except ValueError:
|
|
3417
|
-
|
|
3435
|
+
raise ValueError(f"Invalid Coin ID format for 'coin_id': {request.coin_id!r}")
|
|
3418
3436
|
# Get coin state
|
|
3419
3437
|
peer = self.service.get_full_node_peer()
|
|
3420
|
-
coin_spend, coin_state = await self.get_latest_singleton_coin_spend(peer, coin_id, request.
|
|
3438
|
+
coin_spend, coin_state = await self.get_latest_singleton_coin_spend(peer, coin_id, request.latest)
|
|
3421
3439
|
# convert to NFTInfo
|
|
3422
3440
|
# Check if the metadata is updated
|
|
3423
3441
|
full_puzzle: Program = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
|
|
3424
3442
|
|
|
3425
3443
|
uncurried_nft: Optional[UncurriedNFT] = UncurriedNFT.uncurry(*full_puzzle.uncurry())
|
|
3426
3444
|
if uncurried_nft is None:
|
|
3427
|
-
|
|
3445
|
+
raise ValueError("The coin is not a NFT.")
|
|
3428
3446
|
metadata, p2_puzzle_hash = get_metadata_and_phs(uncurried_nft, coin_spend.solution)
|
|
3429
3447
|
# Note: This is not the actual unspent NFT full puzzle.
|
|
3430
3448
|
# There is no way to rebuild the full puzzle in a different wallet.
|
|
3431
3449
|
# But it shouldn't have impact on generating the NFTInfo, since inner_puzzle is not used there.
|
|
3432
3450
|
if uncurried_nft.supports_did:
|
|
3433
3451
|
inner_puzzle = nft_puzzle_utils.recurry_nft_puzzle(
|
|
3434
|
-
uncurried_nft, coin_spend.solution
|
|
3452
|
+
uncurried_nft, Program.from_serialized(coin_spend.solution), uncurried_nft.p2_puzzle
|
|
3435
3453
|
)
|
|
3436
3454
|
else:
|
|
3437
3455
|
inner_puzzle = uncurried_nft.p2_puzzle
|
|
@@ -3448,10 +3466,7 @@ class WalletRpcApi:
|
|
|
3448
3466
|
[uncurried_nft.singleton_launcher_id], peer=peer
|
|
3449
3467
|
)
|
|
3450
3468
|
if launcher_coin is None or len(launcher_coin) < 1 or launcher_coin[0].spent_height is None:
|
|
3451
|
-
|
|
3452
|
-
"success": False,
|
|
3453
|
-
"error": f"Launcher coin record 0x{uncurried_nft.singleton_launcher_id.hex()} not found",
|
|
3454
|
-
}
|
|
3469
|
+
raise ValueError(f"Launcher coin record 0x{uncurried_nft.singleton_launcher_id.hex()} not found")
|
|
3455
3470
|
minter_did = await self.service.wallet_state_manager.get_minter_did(launcher_coin[0].coin, peer)
|
|
3456
3471
|
|
|
3457
3472
|
nft_info: NFTInfo = await nft_puzzle_utils.get_nft_info_from_puzzle(
|
|
@@ -3468,179 +3483,136 @@ class WalletRpcApi:
|
|
|
3468
3483
|
)
|
|
3469
3484
|
# This is a bit hacky, it should just come out like this, but this works for this RPC
|
|
3470
3485
|
nft_info = dataclasses.replace(nft_info, p2_address=p2_puzzle_hash)
|
|
3471
|
-
return
|
|
3486
|
+
return NFTGetInfoResponse(nft_info)
|
|
3472
3487
|
|
|
3473
3488
|
@tx_endpoint(push=True)
|
|
3489
|
+
@marshal
|
|
3474
3490
|
async def nft_add_uri(
|
|
3475
3491
|
self,
|
|
3476
|
-
request:
|
|
3492
|
+
request: NFTAddURI,
|
|
3477
3493
|
action_scope: WalletActionScope,
|
|
3478
3494
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3479
|
-
) ->
|
|
3480
|
-
wallet_id = uint32(request["wallet_id"])
|
|
3495
|
+
) -> NFTAddURIResponse:
|
|
3481
3496
|
# Note metadata updater can only add one uri for one field per spend.
|
|
3482
3497
|
# If you want to add multiple uris for one field, you need to spend multiple times.
|
|
3483
|
-
nft_wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=NFTWallet)
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
nft_coin_id = request["nft_coin_id"]
|
|
3487
|
-
if nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3488
|
-
nft_coin_id = decode_puzzle_hash(nft_coin_id)
|
|
3498
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3499
|
+
if request.nft_coin_id.startswith(AddressType.NFT.hrp(self.service.config)):
|
|
3500
|
+
nft_coin_id = decode_puzzle_hash(request.nft_coin_id)
|
|
3489
3501
|
else:
|
|
3490
|
-
nft_coin_id = bytes32.from_hexstr(nft_coin_id)
|
|
3502
|
+
nft_coin_id = bytes32.from_hexstr(request.nft_coin_id)
|
|
3491
3503
|
nft_coin_info = await nft_wallet.get_nft_coin_by_id(nft_coin_id)
|
|
3492
3504
|
|
|
3493
|
-
fee = uint64(request.get("fee", 0))
|
|
3494
3505
|
await nft_wallet.update_metadata(
|
|
3495
|
-
nft_coin_info, key, uri, action_scope, fee=fee, extra_conditions=extra_conditions
|
|
3506
|
+
nft_coin_info, request.key, request.uri, action_scope, fee=request.fee, extra_conditions=extra_conditions
|
|
3496
3507
|
)
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
"success": True,
|
|
3500
|
-
"spend_bundle": None, # tx_endpoint wrapper will take care of this
|
|
3501
|
-
"transactions": None, # tx_endpoint wrapper will take care of this
|
|
3502
|
-
}
|
|
3508
|
+
# tx_endpoint takes care of setting the default values here
|
|
3509
|
+
return NFTAddURIResponse([], [], request.wallet_id, WalletSpendBundle([], G2Element()))
|
|
3503
3510
|
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
+
@marshal
|
|
3512
|
+
async def nft_calculate_royalties(self, request: NFTCalculateRoyalties) -> NFTCalculateRoyaltiesResponse:
|
|
3513
|
+
return NFTCalculateRoyaltiesResponse.from_json_dict(
|
|
3514
|
+
NFTWallet.royalty_calculation(
|
|
3515
|
+
{
|
|
3516
|
+
asset.asset: (asset.royalty_address, uint16(asset.royalty_percentage))
|
|
3517
|
+
for asset in request.royalty_assets
|
|
3518
|
+
},
|
|
3519
|
+
{asset.asset: asset.amount for asset in request.fungible_assets},
|
|
3520
|
+
)
|
|
3511
3521
|
)
|
|
3512
3522
|
|
|
3513
3523
|
@tx_endpoint(push=False)
|
|
3524
|
+
@marshal
|
|
3514
3525
|
async def nft_mint_bulk(
|
|
3515
3526
|
self,
|
|
3516
|
-
request:
|
|
3527
|
+
request: NFTMintBulk,
|
|
3517
3528
|
action_scope: WalletActionScope,
|
|
3518
3529
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3519
|
-
) ->
|
|
3530
|
+
) -> NFTMintBulkResponse:
|
|
3520
3531
|
if action_scope.config.push:
|
|
3521
3532
|
raise ValueError("Automatic pushing of nft minting transactions not yet available") # pragma: no cover
|
|
3522
3533
|
if await self.service.wallet_state_manager.synced() is False:
|
|
3523
3534
|
raise ValueError("Wallet needs to be fully synced.")
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
if isinstance(royalty_address, str) and royalty_address != "":
|
|
3528
|
-
royalty_puzhash = decode_puzzle_hash(royalty_address)
|
|
3529
|
-
elif royalty_address in {None, ""}:
|
|
3530
|
-
royalty_puzhash = await nft_wallet.standard_wallet.get_new_puzzlehash()
|
|
3535
|
+
nft_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=NFTWallet)
|
|
3536
|
+
if request.royalty_address in {None, ""}:
|
|
3537
|
+
royalty_puzhash = await action_scope.get_puzzle_hash(self.service.wallet_state_manager)
|
|
3531
3538
|
else:
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
if royalty_percentage is None:
|
|
3535
|
-
royalty_percentage = uint16(0)
|
|
3536
|
-
else:
|
|
3537
|
-
royalty_percentage = uint16(int(royalty_percentage))
|
|
3539
|
+
assert request.royalty_address is not None # hello mypy
|
|
3540
|
+
royalty_puzhash = decode_puzzle_hash(request.royalty_address)
|
|
3538
3541
|
metadata_list = []
|
|
3539
|
-
for meta in request
|
|
3540
|
-
if "uris" not in meta.keys():
|
|
3541
|
-
return {"success": False, "error": "Data URIs is required"}
|
|
3542
|
-
if not isinstance(meta["uris"], list):
|
|
3543
|
-
return {"success": False, "error": "Data URIs must be a list"}
|
|
3544
|
-
if not isinstance(meta.get("meta_uris", []), list):
|
|
3545
|
-
return {"success": False, "error": "Metadata URIs must be a list"}
|
|
3546
|
-
if not isinstance(meta.get("license_uris", []), list):
|
|
3547
|
-
return {"success": False, "error": "License URIs must be a list"}
|
|
3542
|
+
for meta in request.metadata_list:
|
|
3548
3543
|
nft_metadata = [
|
|
3549
|
-
("u", meta
|
|
3550
|
-
("h",
|
|
3551
|
-
("mu", meta.
|
|
3552
|
-
("lu", meta.
|
|
3553
|
-
("sn",
|
|
3554
|
-
("st",
|
|
3544
|
+
("u", meta.uris),
|
|
3545
|
+
("h", meta.hash),
|
|
3546
|
+
("mu", meta.meta_uris),
|
|
3547
|
+
("lu", meta.license_uris),
|
|
3548
|
+
("sn", meta.edition_number),
|
|
3549
|
+
("st", meta.edition_total),
|
|
3555
3550
|
]
|
|
3556
|
-
if
|
|
3557
|
-
nft_metadata.append(("mh",
|
|
3558
|
-
if
|
|
3559
|
-
nft_metadata.append(("lh",
|
|
3551
|
+
if meta.meta_hash is not None:
|
|
3552
|
+
nft_metadata.append(("mh", meta.meta_hash))
|
|
3553
|
+
if meta.license_hash is not None:
|
|
3554
|
+
nft_metadata.append(("lh", meta.license_hash))
|
|
3560
3555
|
metadata_program = Program.to(nft_metadata)
|
|
3561
3556
|
metadata_dict = {
|
|
3562
3557
|
"program": metadata_program,
|
|
3563
|
-
"royalty_pc": royalty_percentage,
|
|
3558
|
+
"royalty_pc": request.royalty_percentage,
|
|
3564
3559
|
"royalty_ph": royalty_puzhash,
|
|
3565
3560
|
}
|
|
3566
3561
|
metadata_list.append(metadata_dict)
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
target_list.append(decode_puzzle_hash(target))
|
|
3572
|
-
mint_number_start = request.get("mint_number_start", 1)
|
|
3573
|
-
mint_total = request.get("mint_total", None)
|
|
3574
|
-
xch_coin_list = request.get("xch_coins", None)
|
|
3575
|
-
xch_coins = None
|
|
3576
|
-
if xch_coin_list:
|
|
3577
|
-
xch_coins = {Coin.from_json_dict(xch_coin) for xch_coin in xch_coin_list}
|
|
3578
|
-
xch_change_target = request.get("xch_change_target", None)
|
|
3579
|
-
if xch_change_target is not None:
|
|
3580
|
-
if xch_change_target[:2] == "xch":
|
|
3581
|
-
xch_change_ph = decode_puzzle_hash(xch_change_target)
|
|
3562
|
+
target_list = [decode_puzzle_hash(target) for target in request.target_list]
|
|
3563
|
+
if request.xch_change_target is not None:
|
|
3564
|
+
if request.xch_change_target.startswith("xch"):
|
|
3565
|
+
xch_change_ph = decode_puzzle_hash(request.xch_change_target)
|
|
3582
3566
|
else:
|
|
3583
|
-
xch_change_ph = bytes32.from_hexstr(xch_change_target)
|
|
3567
|
+
xch_change_ph = bytes32.from_hexstr(request.xch_change_target)
|
|
3584
3568
|
else:
|
|
3585
3569
|
xch_change_ph = None
|
|
3586
|
-
new_innerpuzhash = request.get("new_innerpuzhash", None)
|
|
3587
|
-
new_p2_puzhash = request.get("new_p2_puzhash", None)
|
|
3588
|
-
did_coin_dict = request.get("did_coin", None)
|
|
3589
|
-
if did_coin_dict:
|
|
3590
|
-
did_coin = Coin.from_json_dict(did_coin_dict)
|
|
3591
|
-
else:
|
|
3592
|
-
did_coin = None
|
|
3593
|
-
did_lineage_parent_hex = request.get("did_lineage_parent", None)
|
|
3594
|
-
if did_lineage_parent_hex:
|
|
3595
|
-
did_lineage_parent = bytes32.from_hexstr(did_lineage_parent_hex)
|
|
3596
|
-
else:
|
|
3597
|
-
did_lineage_parent = None
|
|
3598
|
-
mint_from_did = request.get("mint_from_did", False)
|
|
3599
|
-
fee = uint64(request.get("fee", 0))
|
|
3600
3570
|
|
|
3601
|
-
if mint_from_did:
|
|
3571
|
+
if request.mint_from_did:
|
|
3602
3572
|
await nft_wallet.mint_from_did(
|
|
3603
3573
|
metadata_list,
|
|
3604
|
-
mint_number_start=mint_number_start,
|
|
3605
|
-
mint_total=mint_total,
|
|
3574
|
+
mint_number_start=request.mint_number_start,
|
|
3575
|
+
mint_total=request.mint_total,
|
|
3606
3576
|
target_list=target_list,
|
|
3607
|
-
xch_coins=xch_coins,
|
|
3577
|
+
xch_coins=set(request.xch_coins) if request.xch_coins is not None else None,
|
|
3608
3578
|
xch_change_ph=xch_change_ph,
|
|
3609
|
-
new_innerpuzhash=new_innerpuzhash,
|
|
3610
|
-
new_p2_puzhash=new_p2_puzhash,
|
|
3611
|
-
did_coin=did_coin,
|
|
3612
|
-
did_lineage_parent=did_lineage_parent,
|
|
3613
|
-
fee=fee,
|
|
3579
|
+
new_innerpuzhash=request.new_innerpuzhash,
|
|
3580
|
+
new_p2_puzhash=request.new_p2_puzhash,
|
|
3581
|
+
did_coin=request.did_coin,
|
|
3582
|
+
did_lineage_parent=request.did_lineage_parent,
|
|
3583
|
+
fee=request.fee,
|
|
3614
3584
|
action_scope=action_scope,
|
|
3615
3585
|
extra_conditions=extra_conditions,
|
|
3616
3586
|
)
|
|
3617
3587
|
else:
|
|
3618
3588
|
await nft_wallet.mint_from_xch(
|
|
3619
3589
|
metadata_list,
|
|
3620
|
-
mint_number_start=mint_number_start,
|
|
3621
|
-
mint_total=mint_total,
|
|
3590
|
+
mint_number_start=request.mint_number_start,
|
|
3591
|
+
mint_total=request.mint_total,
|
|
3622
3592
|
target_list=target_list,
|
|
3623
|
-
xch_coins=xch_coins,
|
|
3593
|
+
xch_coins=set(request.xch_coins) if request.xch_coins is not None else None,
|
|
3624
3594
|
xch_change_ph=xch_change_ph,
|
|
3625
|
-
fee=fee,
|
|
3595
|
+
fee=request.fee,
|
|
3626
3596
|
action_scope=action_scope,
|
|
3627
3597
|
extra_conditions=extra_conditions,
|
|
3628
3598
|
)
|
|
3629
3599
|
async with action_scope.use() as interface:
|
|
3630
3600
|
sb = WalletSpendBundle.aggregate(
|
|
3631
3601
|
[tx.spend_bundle for tx in interface.side_effects.transactions if tx.spend_bundle is not None]
|
|
3602
|
+
+ [sb for sb in interface.side_effects.extra_spends]
|
|
3632
3603
|
)
|
|
3633
3604
|
nft_id_list = []
|
|
3634
3605
|
for cs in sb.coin_spends:
|
|
3635
3606
|
if cs.coin.puzzle_hash == SINGLETON_LAUNCHER_PUZZLE_HASH:
|
|
3636
3607
|
nft_id_list.append(encode_puzzle_hash(cs.coin.name(), AddressType.NFT.hrp(self.service.config)))
|
|
3637
3608
|
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3609
|
+
# tx_endpoint will take care of the default values here
|
|
3610
|
+
return NFTMintBulkResponse(
|
|
3611
|
+
[],
|
|
3612
|
+
[],
|
|
3613
|
+
WalletSpendBundle([], G2Element()),
|
|
3614
|
+
nft_id_list,
|
|
3615
|
+
)
|
|
3644
3616
|
|
|
3645
3617
|
async def get_coin_records(self, request: dict[str, Any]) -> EndpointResult:
|
|
3646
3618
|
parsed_request = GetCoinRecords.from_json_dict(request)
|
|
@@ -3822,118 +3794,119 @@ class WalletRpcApi:
|
|
|
3822
3794
|
# Pool Wallet
|
|
3823
3795
|
##########################################################################################
|
|
3824
3796
|
@tx_endpoint(push=True)
|
|
3797
|
+
@marshal
|
|
3825
3798
|
async def pw_join_pool(
|
|
3826
3799
|
self,
|
|
3827
|
-
request:
|
|
3800
|
+
request: PWJoinPool,
|
|
3828
3801
|
action_scope: WalletActionScope,
|
|
3829
3802
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3830
|
-
) ->
|
|
3831
|
-
|
|
3832
|
-
wallet_id = uint32(request["wallet_id"])
|
|
3833
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
|
|
3834
|
-
|
|
3835
|
-
pool_wallet_info: PoolWalletInfo = await wallet.get_current_state()
|
|
3836
|
-
owner_pubkey = pool_wallet_info.current.owner_pubkey
|
|
3837
|
-
target_puzzlehash = None
|
|
3803
|
+
) -> PWJoinPoolResponse:
|
|
3804
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=PoolWallet)
|
|
3838
3805
|
|
|
3839
3806
|
if await self.service.wallet_state_manager.synced() is False:
|
|
3840
3807
|
raise ValueError("Wallet needs to be fully synced.")
|
|
3841
3808
|
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3809
|
+
pool_wallet_info: PoolWalletInfo = await wallet.get_current_state()
|
|
3810
|
+
if (
|
|
3811
|
+
pool_wallet_info.current.state == FARMING_TO_POOL.value
|
|
3812
|
+
and pool_wallet_info.current.pool_url == request.pool_url
|
|
3813
|
+
):
|
|
3814
|
+
raise ValueError(f"Already farming to pool {pool_wallet_info.current.pool_url}")
|
|
3815
|
+
|
|
3816
|
+
owner_pubkey = pool_wallet_info.current.owner_pubkey
|
|
3845
3817
|
new_target_state: PoolState = create_pool_state(
|
|
3846
3818
|
FARMING_TO_POOL,
|
|
3847
|
-
target_puzzlehash,
|
|
3819
|
+
request.target_puzzlehash,
|
|
3848
3820
|
owner_pubkey,
|
|
3849
|
-
request
|
|
3850
|
-
|
|
3821
|
+
request.pool_url,
|
|
3822
|
+
request.relative_lock_height,
|
|
3851
3823
|
)
|
|
3852
3824
|
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3825
|
+
total_fee = await wallet.join_pool(new_target_state, request.fee, action_scope)
|
|
3826
|
+
# tx_endpoint will take care of filling in these default values
|
|
3827
|
+
return PWJoinPoolResponse(
|
|
3828
|
+
[],
|
|
3829
|
+
[],
|
|
3830
|
+
total_fee=total_fee,
|
|
3831
|
+
transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3832
|
+
fee_transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3833
|
+
)
|
|
3861
3834
|
|
|
3862
3835
|
@tx_endpoint(push=True)
|
|
3836
|
+
@marshal
|
|
3863
3837
|
async def pw_self_pool(
|
|
3864
3838
|
self,
|
|
3865
|
-
request:
|
|
3839
|
+
request: PWSelfPool,
|
|
3866
3840
|
action_scope: WalletActionScope,
|
|
3867
3841
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3868
|
-
) ->
|
|
3842
|
+
) -> PWSelfPoolResponse:
|
|
3869
3843
|
# Leaving a pool requires two state transitions.
|
|
3870
3844
|
# First we transition to PoolSingletonState.LEAVING_POOL
|
|
3871
3845
|
# Then we transition to FARMING_TO_POOL or SELF_POOLING
|
|
3872
|
-
|
|
3873
|
-
wallet_id = uint32(request["wallet_id"])
|
|
3874
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
|
|
3846
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=PoolWallet)
|
|
3875
3847
|
|
|
3876
3848
|
if await self.service.wallet_state_manager.synced() is False:
|
|
3877
3849
|
raise ValueError("Wallet needs to be fully synced.")
|
|
3878
3850
|
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3851
|
+
total_fee = await wallet.self_pool(request.fee, action_scope)
|
|
3852
|
+
# tx_endpoint will take care of filling in these default values
|
|
3853
|
+
return PWSelfPoolResponse(
|
|
3854
|
+
[],
|
|
3855
|
+
[],
|
|
3856
|
+
total_fee=total_fee,
|
|
3857
|
+
transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3858
|
+
fee_transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3859
|
+
)
|
|
3887
3860
|
|
|
3888
3861
|
@tx_endpoint(push=True)
|
|
3862
|
+
@marshal
|
|
3889
3863
|
async def pw_absorb_rewards(
|
|
3890
3864
|
self,
|
|
3891
|
-
request:
|
|
3865
|
+
request: PWAbsorbRewards,
|
|
3892
3866
|
action_scope: WalletActionScope,
|
|
3893
3867
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3894
|
-
) ->
|
|
3868
|
+
) -> PWAbsorbRewardsResponse:
|
|
3895
3869
|
"""Perform a sweep of the p2_singleton rewards controlled by the pool wallet singleton"""
|
|
3896
3870
|
if await self.service.wallet_state_manager.synced() is False:
|
|
3897
3871
|
raise ValueError("Wallet needs to be fully synced before collecting rewards")
|
|
3898
|
-
|
|
3899
|
-
max_spends_in_tx = request.get("max_spends_in_tx", None)
|
|
3900
|
-
wallet_id = uint32(request["wallet_id"])
|
|
3901
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
|
|
3872
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=PoolWallet)
|
|
3902
3873
|
|
|
3903
3874
|
assert isinstance(wallet, PoolWallet)
|
|
3904
3875
|
async with self.service.wallet_state_manager.lock:
|
|
3905
|
-
await wallet.claim_pool_rewards(fee, max_spends_in_tx, action_scope)
|
|
3876
|
+
await wallet.claim_pool_rewards(request.fee, request.max_spends_in_tx, action_scope)
|
|
3906
3877
|
state: PoolWalletInfo = await wallet.get_current_state()
|
|
3907
|
-
return
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3878
|
+
return PWAbsorbRewardsResponse(
|
|
3879
|
+
[],
|
|
3880
|
+
[],
|
|
3881
|
+
state=state,
|
|
3882
|
+
transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3883
|
+
fee_transaction=REPLACEABLE_TRANSACTION_RECORD,
|
|
3884
|
+
)
|
|
3913
3885
|
|
|
3914
|
-
|
|
3886
|
+
@marshal
|
|
3887
|
+
async def pw_status(self, request: PWStatus) -> PWStatusResponse:
|
|
3915
3888
|
"""Return the complete state of the Pool wallet with id `request["wallet_id"]`"""
|
|
3916
|
-
|
|
3917
|
-
wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=PoolWallet)
|
|
3889
|
+
wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=PoolWallet)
|
|
3918
3890
|
|
|
3919
3891
|
assert isinstance(wallet, PoolWallet)
|
|
3920
3892
|
state: PoolWalletInfo = await wallet.get_current_state()
|
|
3921
3893
|
unconfirmed_transactions: list[TransactionRecord] = await wallet.get_unconfirmed_transactions()
|
|
3922
|
-
return
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3894
|
+
return PWStatusResponse(
|
|
3895
|
+
state=state,
|
|
3896
|
+
unconfirmed_transactions=unconfirmed_transactions,
|
|
3897
|
+
)
|
|
3926
3898
|
|
|
3927
3899
|
##########################################################################################
|
|
3928
3900
|
# DataLayer Wallet
|
|
3929
3901
|
##########################################################################################
|
|
3930
3902
|
@tx_endpoint(push=True)
|
|
3903
|
+
@marshal
|
|
3931
3904
|
async def create_new_dl(
|
|
3932
3905
|
self,
|
|
3933
|
-
request:
|
|
3906
|
+
request: CreateNewDL,
|
|
3934
3907
|
action_scope: WalletActionScope,
|
|
3935
3908
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
3936
|
-
) ->
|
|
3909
|
+
) -> CreateNewDLResponse:
|
|
3937
3910
|
"""Initialize the DataLayer Wallet (only one can exist)"""
|
|
3938
3911
|
if self.service.wallet_state_manager is None:
|
|
3939
3912
|
raise ValueError("The wallet service is not currently initialized")
|
|
@@ -3944,24 +3917,19 @@ class WalletRpcApi:
|
|
|
3944
3917
|
async with self.service.wallet_state_manager.lock:
|
|
3945
3918
|
dl_wallet = await DataLayerWallet.create_new_dl_wallet(self.service.wallet_state_manager)
|
|
3946
3919
|
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
)
|
|
3955
|
-
except ValueError as e:
|
|
3956
|
-
log.error(f"Error while generating new reporter {e}")
|
|
3957
|
-
return {"success": False, "error": str(e)}
|
|
3920
|
+
async with self.service.wallet_state_manager.lock:
|
|
3921
|
+
launcher_id = await dl_wallet.generate_new_reporter(
|
|
3922
|
+
request.root,
|
|
3923
|
+
action_scope,
|
|
3924
|
+
fee=request.fee,
|
|
3925
|
+
extra_conditions=extra_conditions,
|
|
3926
|
+
)
|
|
3958
3927
|
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
"launcher_id": launcher_id,
|
|
3962
|
-
}
|
|
3928
|
+
# tx_endpoint will take care of these default values
|
|
3929
|
+
return CreateNewDLResponse([], [], launcher_id=launcher_id)
|
|
3963
3930
|
|
|
3964
|
-
|
|
3931
|
+
@marshal
|
|
3932
|
+
async def dl_track_new(self, request: DLTrackNew) -> Empty:
|
|
3965
3933
|
"""Initialize the DataLayer Wallet (only one can exist)"""
|
|
3966
3934
|
if self.service.wallet_state_manager is None:
|
|
3967
3935
|
raise ValueError("The wallet service is not currently initialized")
|
|
@@ -3977,55 +3945,53 @@ class WalletRpcApi:
|
|
|
3977
3945
|
for i, peer in enumerate(peer_list):
|
|
3978
3946
|
try:
|
|
3979
3947
|
await dl_wallet.track_new_launcher_id(
|
|
3980
|
-
|
|
3948
|
+
request.launcher_id,
|
|
3981
3949
|
peer,
|
|
3982
3950
|
)
|
|
3983
3951
|
except LauncherCoinNotFoundError as e:
|
|
3984
3952
|
if i == peer_length - 1:
|
|
3985
3953
|
raise e # raise the error if we've tried all peers
|
|
3986
3954
|
continue # try some other peers, maybe someone has it
|
|
3987
|
-
return
|
|
3955
|
+
return Empty()
|
|
3988
3956
|
|
|
3989
|
-
|
|
3957
|
+
@marshal
|
|
3958
|
+
async def dl_stop_tracking(self, request: DLStopTracking) -> Empty:
|
|
3990
3959
|
"""Initialize the DataLayer Wallet (only one can exist)"""
|
|
3991
3960
|
if self.service.wallet_state_manager is None:
|
|
3992
3961
|
raise ValueError("The wallet service is not currently initialized")
|
|
3993
3962
|
|
|
3994
3963
|
dl_wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
3995
|
-
await dl_wallet.stop_tracking_singleton(
|
|
3996
|
-
return
|
|
3964
|
+
await dl_wallet.stop_tracking_singleton(request.launcher_id)
|
|
3965
|
+
return Empty()
|
|
3997
3966
|
|
|
3998
|
-
|
|
3967
|
+
@marshal
|
|
3968
|
+
async def dl_latest_singleton(self, request: DLLatestSingleton) -> DLLatestSingletonResponse:
|
|
3999
3969
|
"""Get the singleton record for the latest singleton of a launcher ID"""
|
|
4000
3970
|
if self.service.wallet_state_manager is None:
|
|
4001
3971
|
raise ValueError("The wallet service is not currently initialized")
|
|
4002
3972
|
|
|
4003
|
-
only_confirmed = request.get("only_confirmed")
|
|
4004
|
-
if only_confirmed is None:
|
|
4005
|
-
only_confirmed = False
|
|
4006
3973
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4007
|
-
record = await wallet.get_latest_singleton(
|
|
4008
|
-
return
|
|
3974
|
+
record = await wallet.get_latest_singleton(request.launcher_id, request.only_confirmed)
|
|
3975
|
+
return DLLatestSingletonResponse(record)
|
|
4009
3976
|
|
|
4010
|
-
|
|
3977
|
+
@marshal
|
|
3978
|
+
async def dl_singletons_by_root(self, request: DLSingletonsByRoot) -> DLSingletonsByRootResponse:
|
|
4011
3979
|
"""Get the singleton records that contain the specified root"""
|
|
4012
3980
|
if self.service.wallet_state_manager is None:
|
|
4013
3981
|
raise ValueError("The wallet service is not currently initialized")
|
|
4014
3982
|
|
|
4015
3983
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4016
|
-
records = await wallet.get_singletons_by_root(
|
|
4017
|
-
|
|
4018
|
-
)
|
|
4019
|
-
records_json = [rec.to_json_dict() for rec in records]
|
|
4020
|
-
return {"singletons": records_json}
|
|
3984
|
+
records = await wallet.get_singletons_by_root(request.launcher_id, request.root)
|
|
3985
|
+
return DLSingletonsByRootResponse(records)
|
|
4021
3986
|
|
|
4022
3987
|
@tx_endpoint(push=True)
|
|
3988
|
+
@marshal
|
|
4023
3989
|
async def dl_update_root(
|
|
4024
3990
|
self,
|
|
4025
|
-
request:
|
|
3991
|
+
request: DLUpdateRoot,
|
|
4026
3992
|
action_scope: WalletActionScope,
|
|
4027
3993
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
4028
|
-
) ->
|
|
3994
|
+
) -> DLUpdateRootResponse:
|
|
4029
3995
|
"""Get the singleton record for the latest singleton of a launcher ID"""
|
|
4030
3996
|
if self.service.wallet_state_manager is None:
|
|
4031
3997
|
raise ValueError("The wallet service is not currently initialized")
|
|
@@ -4033,48 +3999,55 @@ class WalletRpcApi:
|
|
|
4033
3999
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4034
4000
|
async with self.service.wallet_state_manager.lock:
|
|
4035
4001
|
await wallet.create_update_state_spend(
|
|
4036
|
-
|
|
4037
|
-
|
|
4002
|
+
request.launcher_id,
|
|
4003
|
+
request.new_root,
|
|
4038
4004
|
action_scope,
|
|
4039
|
-
fee=
|
|
4005
|
+
fee=request.fee,
|
|
4040
4006
|
extra_conditions=extra_conditions,
|
|
4041
4007
|
)
|
|
4042
4008
|
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4009
|
+
# tx_endpoint will take care of default values here
|
|
4010
|
+
return DLUpdateRootResponse(
|
|
4011
|
+
[],
|
|
4012
|
+
[],
|
|
4013
|
+
REPLACEABLE_TRANSACTION_RECORD,
|
|
4014
|
+
)
|
|
4047
4015
|
|
|
4048
4016
|
@tx_endpoint(push=True)
|
|
4017
|
+
@marshal
|
|
4049
4018
|
async def dl_update_multiple(
|
|
4050
4019
|
self,
|
|
4051
|
-
request:
|
|
4020
|
+
request: DLUpdateMultiple,
|
|
4052
4021
|
action_scope: WalletActionScope,
|
|
4053
4022
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
4054
|
-
) ->
|
|
4023
|
+
) -> DLUpdateMultipleResponse:
|
|
4055
4024
|
"""Update multiple singletons with new merkle roots"""
|
|
4056
4025
|
if self.service.wallet_state_manager is None:
|
|
4057
|
-
|
|
4026
|
+
raise RuntimeError("not initialized")
|
|
4058
4027
|
|
|
4059
4028
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4060
4029
|
async with self.service.wallet_state_manager.lock:
|
|
4061
4030
|
# TODO: This method should optionally link the singletons with announcements.
|
|
4062
4031
|
# Otherwise spends are vulnerable to signature subtraction.
|
|
4063
|
-
|
|
4064
|
-
|
|
4032
|
+
# TODO: This method should natively support spending many and attaching one fee
|
|
4033
|
+
fee_per_launcher = uint64(request.fee // len(request.updates.launcher_root_pairs))
|
|
4034
|
+
for launcher_root_pair in request.updates.launcher_root_pairs:
|
|
4065
4035
|
await wallet.create_update_state_spend(
|
|
4066
|
-
|
|
4067
|
-
|
|
4036
|
+
launcher_root_pair.launcher_id,
|
|
4037
|
+
launcher_root_pair.new_root,
|
|
4068
4038
|
action_scope,
|
|
4069
4039
|
fee=fee_per_launcher,
|
|
4070
4040
|
extra_conditions=extra_conditions,
|
|
4071
4041
|
)
|
|
4072
4042
|
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4043
|
+
# tx_endpoint will take care of default values here
|
|
4044
|
+
return DLUpdateMultipleResponse(
|
|
4045
|
+
[],
|
|
4046
|
+
[],
|
|
4047
|
+
)
|
|
4076
4048
|
|
|
4077
|
-
|
|
4049
|
+
@marshal
|
|
4050
|
+
async def dl_history(self, request: DLHistory) -> DLHistoryResponse:
|
|
4078
4051
|
"""Get the singleton record for the latest singleton of a launcher ID"""
|
|
4079
4052
|
if self.service.wallet_state_manager is None:
|
|
4080
4053
|
raise ValueError("The wallet service is not currently initialized")
|
|
@@ -4082,51 +4055,44 @@ class WalletRpcApi:
|
|
|
4082
4055
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4083
4056
|
additional_kwargs = {}
|
|
4084
4057
|
|
|
4085
|
-
if
|
|
4086
|
-
additional_kwargs["min_generation"] = uint32(request
|
|
4087
|
-
if
|
|
4088
|
-
additional_kwargs["max_generation"] = uint32(request
|
|
4089
|
-
if
|
|
4090
|
-
additional_kwargs["num_results"] = uint32(request
|
|
4058
|
+
if request.min_generation is not None:
|
|
4059
|
+
additional_kwargs["min_generation"] = uint32(request.min_generation)
|
|
4060
|
+
if request.max_generation is not None:
|
|
4061
|
+
additional_kwargs["max_generation"] = uint32(request.max_generation)
|
|
4062
|
+
if request.num_results is not None:
|
|
4063
|
+
additional_kwargs["num_results"] = uint32(request.num_results)
|
|
4091
4064
|
|
|
4092
|
-
history = await wallet.get_history(
|
|
4093
|
-
|
|
4094
|
-
return {"history": history_json, "count": len(history_json)}
|
|
4065
|
+
history = await wallet.get_history(request.launcher_id, **additional_kwargs)
|
|
4066
|
+
return DLHistoryResponse(history, uint32(len(history)))
|
|
4095
4067
|
|
|
4096
|
-
|
|
4068
|
+
@marshal
|
|
4069
|
+
async def dl_owned_singletons(self, request: Empty) -> DLOwnedSingletonsResponse:
|
|
4097
4070
|
"""Get all owned singleton records"""
|
|
4098
4071
|
if self.service.wallet_state_manager is None:
|
|
4099
4072
|
raise ValueError("The wallet service is not currently initialized")
|
|
4100
4073
|
|
|
4101
|
-
|
|
4102
|
-
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4103
|
-
except ValueError:
|
|
4104
|
-
return {"success": False, "error": "no DataLayer wallet available"}
|
|
4105
|
-
|
|
4074
|
+
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4106
4075
|
singletons = await wallet.get_owned_singletons()
|
|
4107
|
-
singletons_json = [singleton.to_json_dict() for singleton in singletons]
|
|
4108
4076
|
|
|
4109
|
-
return
|
|
4077
|
+
return DLOwnedSingletonsResponse(singletons, uint32(len(singletons)))
|
|
4110
4078
|
|
|
4111
|
-
|
|
4079
|
+
@marshal
|
|
4080
|
+
async def dl_get_mirrors(self, request: DLGetMirrors) -> DLGetMirrorsResponse:
|
|
4112
4081
|
"""Get all of the mirrors for a specific singleton"""
|
|
4113
4082
|
if self.service.wallet_state_manager is None:
|
|
4114
4083
|
raise ValueError("The wallet service is not currently initialized")
|
|
4115
4084
|
|
|
4116
4085
|
wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4117
|
-
|
|
4118
|
-
for mirror in await wallet.get_mirrors_for_launcher(bytes32.from_hexstr(request["launcher_id"])):
|
|
4119
|
-
mirrors_json.append(mirror.to_json_dict())
|
|
4120
|
-
|
|
4121
|
-
return {"mirrors": mirrors_json}
|
|
4086
|
+
return DLGetMirrorsResponse(await wallet.get_mirrors_for_launcher(request.launcher_id))
|
|
4122
4087
|
|
|
4123
4088
|
@tx_endpoint(push=True)
|
|
4089
|
+
@marshal
|
|
4124
4090
|
async def dl_new_mirror(
|
|
4125
4091
|
self,
|
|
4126
|
-
request:
|
|
4092
|
+
request: DLNewMirror,
|
|
4127
4093
|
action_scope: WalletActionScope,
|
|
4128
4094
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
4129
|
-
) ->
|
|
4095
|
+
) -> DLNewMirrorResponse:
|
|
4130
4096
|
"""Add a new on chain message for a specific singleton"""
|
|
4131
4097
|
if self.service.wallet_state_manager is None:
|
|
4132
4098
|
raise ValueError("The wallet service is not currently initialized")
|
|
@@ -4134,48 +4100,53 @@ class WalletRpcApi:
|
|
|
4134
4100
|
dl_wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4135
4101
|
async with self.service.wallet_state_manager.lock:
|
|
4136
4102
|
await dl_wallet.create_new_mirror(
|
|
4137
|
-
|
|
4138
|
-
request
|
|
4139
|
-
|
|
4103
|
+
request.launcher_id,
|
|
4104
|
+
request.amount,
|
|
4105
|
+
Mirror.encode_urls(request.urls),
|
|
4140
4106
|
action_scope,
|
|
4141
|
-
fee=request.
|
|
4107
|
+
fee=request.fee,
|
|
4142
4108
|
extra_conditions=extra_conditions,
|
|
4143
4109
|
)
|
|
4144
4110
|
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4111
|
+
# tx_endpoint will take care of default values here
|
|
4112
|
+
return DLNewMirrorResponse(
|
|
4113
|
+
[],
|
|
4114
|
+
[],
|
|
4115
|
+
)
|
|
4148
4116
|
|
|
4149
4117
|
@tx_endpoint(push=True)
|
|
4118
|
+
@marshal
|
|
4150
4119
|
async def dl_delete_mirror(
|
|
4151
4120
|
self,
|
|
4152
|
-
request:
|
|
4121
|
+
request: DLDeleteMirror,
|
|
4153
4122
|
action_scope: WalletActionScope,
|
|
4154
4123
|
extra_conditions: tuple[Condition, ...] = tuple(),
|
|
4155
|
-
) ->
|
|
4124
|
+
) -> DLDeleteMirrorResponse:
|
|
4156
4125
|
"""Remove an existing mirror for a specific singleton"""
|
|
4157
4126
|
if self.service.wallet_state_manager is None:
|
|
4158
4127
|
raise ValueError("The wallet service is not currently initialized")
|
|
4159
4128
|
|
|
4160
4129
|
dl_wallet = self.service.wallet_state_manager.get_dl_wallet()
|
|
4161
|
-
|
|
4162
4130
|
async with self.service.wallet_state_manager.lock:
|
|
4163
4131
|
await dl_wallet.delete_mirror(
|
|
4164
|
-
|
|
4132
|
+
request.coin_id,
|
|
4165
4133
|
self.service.get_full_node_peer(),
|
|
4166
4134
|
action_scope,
|
|
4167
|
-
fee=request.
|
|
4135
|
+
fee=request.fee,
|
|
4168
4136
|
extra_conditions=extra_conditions,
|
|
4169
4137
|
)
|
|
4170
4138
|
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4139
|
+
# tx_endpoint will take care of default values here
|
|
4140
|
+
return DLDeleteMirrorResponse(
|
|
4141
|
+
[],
|
|
4142
|
+
[],
|
|
4143
|
+
)
|
|
4174
4144
|
|
|
4145
|
+
@marshal
|
|
4175
4146
|
async def dl_verify_proof(
|
|
4176
4147
|
self,
|
|
4177
|
-
request:
|
|
4178
|
-
) ->
|
|
4148
|
+
request: DLProof,
|
|
4149
|
+
) -> VerifyProofResponse:
|
|
4179
4150
|
"""Verify a proof of inclusion for a DL singleton"""
|
|
4180
4151
|
res = await dl_verify_proof(
|
|
4181
4152
|
request,
|
|
@@ -4270,9 +4241,7 @@ class WalletRpcApi:
|
|
|
4270
4241
|
[
|
|
4271
4242
|
request.new_puzhash
|
|
4272
4243
|
if request.new_puzhash is not None
|
|
4273
|
-
else await
|
|
4274
|
-
new=not action_scope.config.tx_config.reuse_puzhash
|
|
4275
|
-
)
|
|
4244
|
+
else await action_scope.get_puzzle_hash(self.service.wallet_state_manager)
|
|
4276
4245
|
],
|
|
4277
4246
|
action_scope,
|
|
4278
4247
|
request.fee,
|