chia-blockchain 2.5.1rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chia/__init__.py +10 -0
- chia/__main__.py +5 -0
- chia/_tests/README.md +53 -0
- chia/_tests/__init__.py +0 -0
- chia/_tests/blockchain/__init__.py +0 -0
- chia/_tests/blockchain/blockchain_test_utils.py +195 -0
- chia/_tests/blockchain/config.py +4 -0
- chia/_tests/blockchain/test_augmented_chain.py +145 -0
- chia/_tests/blockchain/test_blockchain.py +4202 -0
- chia/_tests/blockchain/test_blockchain_transactions.py +1031 -0
- chia/_tests/blockchain/test_build_chains.py +59 -0
- chia/_tests/blockchain/test_get_block_generator.py +72 -0
- chia/_tests/blockchain/test_lookup_fork_chain.py +194 -0
- chia/_tests/build-init-files.py +92 -0
- chia/_tests/build-job-matrix.py +204 -0
- chia/_tests/check_pytest_monitor_output.py +34 -0
- chia/_tests/check_sql_statements.py +72 -0
- chia/_tests/chia-start-sim +42 -0
- chia/_tests/clvm/__init__.py +0 -0
- chia/_tests/clvm/benchmark_costs.py +23 -0
- chia/_tests/clvm/coin_store.py +149 -0
- chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
- chia/_tests/clvm/test_clvm_step.py +37 -0
- chia/_tests/clvm/test_condition_codes.py +13 -0
- chia/_tests/clvm/test_curry_and_treehash.py +55 -0
- chia/_tests/clvm/test_message_conditions.py +184 -0
- chia/_tests/clvm/test_program.py +150 -0
- chia/_tests/clvm/test_puzzle_compression.py +143 -0
- chia/_tests/clvm/test_puzzle_drivers.py +45 -0
- chia/_tests/clvm/test_puzzles.py +242 -0
- chia/_tests/clvm/test_singletons.py +540 -0
- chia/_tests/clvm/test_spend_sim.py +181 -0
- chia/_tests/cmds/__init__.py +0 -0
- chia/_tests/cmds/cmd_test_utils.py +469 -0
- chia/_tests/cmds/config.py +3 -0
- chia/_tests/cmds/conftest.py +23 -0
- chia/_tests/cmds/test_click_types.py +200 -0
- chia/_tests/cmds/test_cmd_framework.py +620 -0
- chia/_tests/cmds/test_cmds_util.py +97 -0
- chia/_tests/cmds/test_daemon.py +92 -0
- chia/_tests/cmds/test_dev_gh.py +131 -0
- chia/_tests/cmds/test_farm_cmd.py +66 -0
- chia/_tests/cmds/test_show.py +116 -0
- chia/_tests/cmds/test_sim.py +207 -0
- chia/_tests/cmds/test_timelock_args.py +75 -0
- chia/_tests/cmds/test_tx_config_args.py +154 -0
- chia/_tests/cmds/testing_classes.py +59 -0
- chia/_tests/cmds/wallet/__init__.py +0 -0
- chia/_tests/cmds/wallet/test_consts.py +47 -0
- chia/_tests/cmds/wallet/test_dao.py +565 -0
- chia/_tests/cmds/wallet/test_did.py +403 -0
- chia/_tests/cmds/wallet/test_nft.py +471 -0
- chia/_tests/cmds/wallet/test_notifications.py +124 -0
- chia/_tests/cmds/wallet/test_offer.toffer +1 -0
- chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
- chia/_tests/cmds/wallet/test_vcs.py +400 -0
- chia/_tests/cmds/wallet/test_wallet.py +1125 -0
- chia/_tests/cmds/wallet/test_wallet_check.py +109 -0
- chia/_tests/conftest.py +1419 -0
- chia/_tests/connection_utils.py +125 -0
- chia/_tests/core/__init__.py +0 -0
- chia/_tests/core/cmds/__init__.py +0 -0
- chia/_tests/core/cmds/test_beta.py +382 -0
- chia/_tests/core/cmds/test_keys.py +1734 -0
- chia/_tests/core/cmds/test_wallet.py +126 -0
- chia/_tests/core/config.py +3 -0
- chia/_tests/core/consensus/__init__.py +0 -0
- chia/_tests/core/consensus/test_block_creation.py +54 -0
- chia/_tests/core/consensus/test_pot_iterations.py +117 -0
- chia/_tests/core/custom_types/__init__.py +0 -0
- chia/_tests/core/custom_types/test_coin.py +107 -0
- chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
- chia/_tests/core/custom_types/test_spend_bundle.py +70 -0
- chia/_tests/core/daemon/__init__.py +0 -0
- chia/_tests/core/daemon/config.py +4 -0
- chia/_tests/core/daemon/test_daemon.py +2128 -0
- chia/_tests/core/daemon/test_daemon_register.py +109 -0
- chia/_tests/core/daemon/test_keychain_proxy.py +101 -0
- chia/_tests/core/data_layer/__init__.py +0 -0
- chia/_tests/core/data_layer/config.py +5 -0
- chia/_tests/core/data_layer/conftest.py +106 -0
- chia/_tests/core/data_layer/test_data_cli.py +56 -0
- chia/_tests/core/data_layer/test_data_layer.py +83 -0
- chia/_tests/core/data_layer/test_data_layer_util.py +218 -0
- chia/_tests/core/data_layer/test_data_rpc.py +3847 -0
- chia/_tests/core/data_layer/test_data_store.py +2424 -0
- chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
- chia/_tests/core/data_layer/test_plugin.py +91 -0
- chia/_tests/core/data_layer/util.py +233 -0
- chia/_tests/core/farmer/__init__.py +0 -0
- chia/_tests/core/farmer/config.py +3 -0
- chia/_tests/core/farmer/test_farmer_api.py +103 -0
- chia/_tests/core/full_node/__init__.py +0 -0
- chia/_tests/core/full_node/config.py +4 -0
- chia/_tests/core/full_node/dos/__init__.py +0 -0
- chia/_tests/core/full_node/dos/config.py +3 -0
- chia/_tests/core/full_node/full_sync/__init__.py +0 -0
- chia/_tests/core/full_node/full_sync/config.py +4 -0
- chia/_tests/core/full_node/full_sync/test_full_sync.py +443 -0
- chia/_tests/core/full_node/ram_db.py +27 -0
- chia/_tests/core/full_node/stores/__init__.py +0 -0
- chia/_tests/core/full_node/stores/config.py +4 -0
- chia/_tests/core/full_node/stores/test_block_store.py +590 -0
- chia/_tests/core/full_node/stores/test_coin_store.py +897 -0
- chia/_tests/core/full_node/stores/test_full_node_store.py +1219 -0
- chia/_tests/core/full_node/stores/test_hint_store.py +229 -0
- chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
- chia/_tests/core/full_node/test_address_manager.py +588 -0
- chia/_tests/core/full_node/test_block_height_map.py +556 -0
- chia/_tests/core/full_node/test_conditions.py +556 -0
- chia/_tests/core/full_node/test_full_node.py +2700 -0
- chia/_tests/core/full_node/test_generator_tools.py +82 -0
- chia/_tests/core/full_node/test_hint_management.py +104 -0
- chia/_tests/core/full_node/test_node_load.py +34 -0
- chia/_tests/core/full_node/test_performance.py +179 -0
- chia/_tests/core/full_node/test_subscriptions.py +492 -0
- chia/_tests/core/full_node/test_transactions.py +203 -0
- chia/_tests/core/full_node/test_tx_processing_queue.py +155 -0
- chia/_tests/core/large_block.py +2388 -0
- chia/_tests/core/make_block_generator.py +70 -0
- chia/_tests/core/mempool/__init__.py +0 -0
- chia/_tests/core/mempool/config.py +4 -0
- chia/_tests/core/mempool/test_mempool.py +3255 -0
- chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
- chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
- chia/_tests/core/mempool/test_mempool_item_queries.py +190 -0
- chia/_tests/core/mempool/test_mempool_manager.py +2084 -0
- chia/_tests/core/mempool/test_mempool_performance.py +64 -0
- chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
- chia/_tests/core/node_height.py +28 -0
- chia/_tests/core/server/__init__.py +0 -0
- chia/_tests/core/server/config.py +3 -0
- chia/_tests/core/server/flood.py +84 -0
- chia/_tests/core/server/serve.py +135 -0
- chia/_tests/core/server/test_api_protocol.py +21 -0
- chia/_tests/core/server/test_capabilities.py +66 -0
- chia/_tests/core/server/test_dos.py +319 -0
- chia/_tests/core/server/test_event_loop.py +109 -0
- chia/_tests/core/server/test_loop.py +294 -0
- chia/_tests/core/server/test_node_discovery.py +73 -0
- chia/_tests/core/server/test_rate_limits.py +482 -0
- chia/_tests/core/server/test_server.py +226 -0
- chia/_tests/core/server/test_upnp.py +8 -0
- chia/_tests/core/services/__init__.py +0 -0
- chia/_tests/core/services/config.py +3 -0
- chia/_tests/core/services/test_services.py +188 -0
- chia/_tests/core/ssl/__init__.py +0 -0
- chia/_tests/core/ssl/config.py +3 -0
- chia/_tests/core/ssl/test_ssl.py +202 -0
- chia/_tests/core/test_coins.py +33 -0
- chia/_tests/core/test_cost_calculation.py +313 -0
- chia/_tests/core/test_crawler.py +175 -0
- chia/_tests/core/test_crawler_rpc.py +53 -0
- chia/_tests/core/test_daemon_rpc.py +24 -0
- chia/_tests/core/test_db_conversion.py +130 -0
- chia/_tests/core/test_db_validation.py +162 -0
- chia/_tests/core/test_farmer_harvester_rpc.py +505 -0
- chia/_tests/core/test_filter.py +35 -0
- chia/_tests/core/test_full_node_rpc.py +768 -0
- chia/_tests/core/test_merkle_set.py +343 -0
- chia/_tests/core/test_program.py +47 -0
- chia/_tests/core/test_rpc_util.py +86 -0
- chia/_tests/core/test_seeder.py +420 -0
- chia/_tests/core/test_setproctitle.py +13 -0
- chia/_tests/core/util/__init__.py +0 -0
- chia/_tests/core/util/config.py +4 -0
- chia/_tests/core/util/test_block_cache.py +44 -0
- chia/_tests/core/util/test_cached_bls.py +57 -0
- chia/_tests/core/util/test_config.py +337 -0
- chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
- chia/_tests/core/util/test_files.py +391 -0
- chia/_tests/core/util/test_jsonify.py +146 -0
- chia/_tests/core/util/test_keychain.py +522 -0
- chia/_tests/core/util/test_keyring_wrapper.py +491 -0
- chia/_tests/core/util/test_lockfile.py +380 -0
- chia/_tests/core/util/test_log_exceptions.py +187 -0
- chia/_tests/core/util/test_lru_cache.py +56 -0
- chia/_tests/core/util/test_significant_bits.py +40 -0
- chia/_tests/core/util/test_streamable.py +883 -0
- chia/_tests/db/__init__.py +0 -0
- chia/_tests/db/test_db_wrapper.py +566 -0
- chia/_tests/environments/__init__.py +0 -0
- chia/_tests/environments/common.py +35 -0
- chia/_tests/environments/full_node.py +47 -0
- chia/_tests/environments/wallet.py +429 -0
- chia/_tests/ether.py +19 -0
- chia/_tests/farmer_harvester/__init__.py +0 -0
- chia/_tests/farmer_harvester/config.py +3 -0
- chia/_tests/farmer_harvester/test_farmer.py +1264 -0
- chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
- chia/_tests/farmer_harvester/test_filter_prefix_bits.py +131 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters.py +528 -0
- chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
- chia/_tests/fee_estimation/__init__.py +0 -0
- chia/_tests/fee_estimation/config.py +3 -0
- chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
- chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
- chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +144 -0
- chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
- chia/_tests/generator/__init__.py +0 -0
- chia/_tests/generator/puzzles/__init__.py +0 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
- chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
- chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
- chia/_tests/generator/test_compression.py +201 -0
- chia/_tests/generator/test_generator_types.py +44 -0
- chia/_tests/generator/test_rom.py +180 -0
- chia/_tests/plot_sync/__init__.py +0 -0
- chia/_tests/plot_sync/config.py +3 -0
- chia/_tests/plot_sync/test_delta.py +101 -0
- chia/_tests/plot_sync/test_plot_sync.py +618 -0
- chia/_tests/plot_sync/test_receiver.py +451 -0
- chia/_tests/plot_sync/test_sender.py +116 -0
- chia/_tests/plot_sync/test_sync_simulated.py +451 -0
- chia/_tests/plot_sync/util.py +68 -0
- chia/_tests/plotting/__init__.py +0 -0
- chia/_tests/plotting/config.py +3 -0
- chia/_tests/plotting/test_plot_manager.py +781 -0
- chia/_tests/plotting/util.py +12 -0
- chia/_tests/pools/__init__.py +0 -0
- chia/_tests/pools/config.py +5 -0
- chia/_tests/pools/test_pool_cli_parsing.py +128 -0
- chia/_tests/pools/test_pool_cmdline.py +1001 -0
- chia/_tests/pools/test_pool_config.py +42 -0
- chia/_tests/pools/test_pool_puzzles_lifecycle.py +397 -0
- chia/_tests/pools/test_pool_rpc.py +1123 -0
- chia/_tests/pools/test_pool_wallet.py +205 -0
- chia/_tests/pools/test_wallet_pool_store.py +161 -0
- chia/_tests/process_junit.py +348 -0
- chia/_tests/rpc/__init__.py +0 -0
- chia/_tests/rpc/test_rpc_client.py +138 -0
- chia/_tests/rpc/test_rpc_server.py +183 -0
- chia/_tests/simulation/__init__.py +0 -0
- chia/_tests/simulation/config.py +6 -0
- chia/_tests/simulation/test_simulation.py +501 -0
- chia/_tests/simulation/test_simulator.py +232 -0
- chia/_tests/simulation/test_start_simulator.py +107 -0
- chia/_tests/testconfig.py +13 -0
- chia/_tests/timelord/__init__.py +0 -0
- chia/_tests/timelord/config.py +3 -0
- chia/_tests/timelord/test_new_peak.py +437 -0
- chia/_tests/timelord/test_timelord.py +11 -0
- chia/_tests/tools/1315537.json +170 -0
- chia/_tests/tools/1315544.json +160 -0
- chia/_tests/tools/1315630.json +150 -0
- chia/_tests/tools/300000.json +105 -0
- chia/_tests/tools/442734.json +140 -0
- chia/_tests/tools/466212.json +130 -0
- chia/_tests/tools/__init__.py +0 -0
- chia/_tests/tools/config.py +5 -0
- chia/_tests/tools/test-blockchain-db.sqlite +0 -0
- chia/_tests/tools/test_full_sync.py +30 -0
- chia/_tests/tools/test_legacy_keyring.py +82 -0
- chia/_tests/tools/test_run_block.py +128 -0
- chia/_tests/tools/test_virtual_project.py +591 -0
- chia/_tests/util/__init__.py +0 -0
- chia/_tests/util/benchmark_cost.py +170 -0
- chia/_tests/util/benchmarks.py +153 -0
- chia/_tests/util/bip39_test_vectors.json +148 -0
- chia/_tests/util/blockchain.py +134 -0
- chia/_tests/util/blockchain_mock.py +132 -0
- chia/_tests/util/build_network_protocol_files.py +302 -0
- chia/_tests/util/clvm_generator.bin +0 -0
- chia/_tests/util/config.py +3 -0
- chia/_tests/util/constants.py +20 -0
- chia/_tests/util/db_connection.py +37 -0
- chia/_tests/util/full_sync.py +253 -0
- chia/_tests/util/gen_ssl_certs.py +114 -0
- chia/_tests/util/generator_tools_testing.py +45 -0
- chia/_tests/util/get_name_puzzle_conditions.py +52 -0
- chia/_tests/util/key_tool.py +36 -0
- chia/_tests/util/misc.py +675 -0
- chia/_tests/util/network_protocol_data.py +1072 -0
- chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
- chia/_tests/util/protocol_messages_json.py +2701 -0
- chia/_tests/util/rpc.py +26 -0
- chia/_tests/util/run_block.py +163 -0
- chia/_tests/util/setup_nodes.py +481 -0
- chia/_tests/util/spend_sim.py +492 -0
- chia/_tests/util/split_managers.py +102 -0
- chia/_tests/util/temp_file.py +14 -0
- chia/_tests/util/test_action_scope.py +144 -0
- chia/_tests/util/test_async_pool.py +366 -0
- chia/_tests/util/test_build_job_matrix.py +42 -0
- chia/_tests/util/test_build_network_protocol_files.py +7 -0
- chia/_tests/util/test_chia_version.py +50 -0
- chia/_tests/util/test_collection.py +11 -0
- chia/_tests/util/test_condition_tools.py +229 -0
- chia/_tests/util/test_config.py +426 -0
- chia/_tests/util/test_dump_keyring.py +60 -0
- chia/_tests/util/test_errors.py +10 -0
- chia/_tests/util/test_full_block_utils.py +279 -0
- chia/_tests/util/test_installed.py +20 -0
- chia/_tests/util/test_limited_semaphore.py +53 -0
- chia/_tests/util/test_logging_filter.py +42 -0
- chia/_tests/util/test_misc.py +445 -0
- chia/_tests/util/test_network.py +73 -0
- chia/_tests/util/test_network_protocol_files.py +578 -0
- chia/_tests/util/test_network_protocol_json.py +267 -0
- chia/_tests/util/test_network_protocol_test.py +256 -0
- chia/_tests/util/test_paginator.py +71 -0
- chia/_tests/util/test_pprint.py +17 -0
- chia/_tests/util/test_priority_mutex.py +488 -0
- chia/_tests/util/test_recursive_replace.py +116 -0
- chia/_tests/util/test_replace_str_to_bytes.py +137 -0
- chia/_tests/util/test_service_groups.py +15 -0
- chia/_tests/util/test_ssl_check.py +31 -0
- chia/_tests/util/test_testnet_overrides.py +19 -0
- chia/_tests/util/test_tests_misc.py +38 -0
- chia/_tests/util/test_timing.py +37 -0
- chia/_tests/util/test_trusted_peer.py +51 -0
- chia/_tests/util/time_out_assert.py +191 -0
- chia/_tests/wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/__init__.py +0 -0
- chia/_tests/wallet/cat_wallet/config.py +4 -0
- chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
- chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
- chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1826 -0
- chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
- chia/_tests/wallet/cat_wallet/test_trades.py +2600 -0
- chia/_tests/wallet/clawback/__init__.py +0 -0
- chia/_tests/wallet/clawback/config.py +3 -0
- chia/_tests/wallet/clawback/test_clawback_decorator.py +78 -0
- chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
- chia/_tests/wallet/clawback/test_clawback_metadata.py +50 -0
- chia/_tests/wallet/config.py +4 -0
- chia/_tests/wallet/conftest.py +278 -0
- chia/_tests/wallet/dao_wallet/__init__.py +0 -0
- chia/_tests/wallet/dao_wallet/config.py +3 -0
- chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1330 -0
- chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
- chia/_tests/wallet/db_wallet/__init__.py +0 -0
- chia/_tests/wallet/db_wallet/config.py +3 -0
- chia/_tests/wallet/db_wallet/test_db_graftroot.py +141 -0
- chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
- chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
- chia/_tests/wallet/did_wallet/__init__.py +0 -0
- chia/_tests/wallet/did_wallet/config.py +4 -0
- chia/_tests/wallet/did_wallet/test_did.py +2284 -0
- chia/_tests/wallet/nft_wallet/__init__.py +0 -0
- chia/_tests/wallet/nft_wallet/config.py +4 -0
- chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1493 -0
- chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1024 -0
- chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +375 -0
- chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
- chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
- chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2584 -0
- chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
- chia/_tests/wallet/rpc/__init__.py +0 -0
- chia/_tests/wallet/rpc/config.py +4 -0
- chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +285 -0
- chia/_tests/wallet/rpc/test_wallet_rpc.py +3153 -0
- chia/_tests/wallet/simple_sync/__init__.py +0 -0
- chia/_tests/wallet/simple_sync/config.py +3 -0
- chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +718 -0
- chia/_tests/wallet/sync/__init__.py +0 -0
- chia/_tests/wallet/sync/config.py +4 -0
- chia/_tests/wallet/sync/test_wallet_sync.py +1692 -0
- chia/_tests/wallet/test_address_type.py +189 -0
- chia/_tests/wallet/test_bech32m.py +45 -0
- chia/_tests/wallet/test_clvm_streamable.py +244 -0
- chia/_tests/wallet/test_coin_management.py +354 -0
- chia/_tests/wallet/test_coin_selection.py +588 -0
- chia/_tests/wallet/test_conditions.py +400 -0
- chia/_tests/wallet/test_debug_spend_bundle.py +218 -0
- chia/_tests/wallet/test_new_wallet_protocol.py +1174 -0
- chia/_tests/wallet/test_nft_store.py +192 -0
- chia/_tests/wallet/test_notifications.py +196 -0
- chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
- chia/_tests/wallet/test_puzzle_store.py +132 -0
- chia/_tests/wallet/test_sign_coin_spends.py +159 -0
- chia/_tests/wallet/test_signer_protocol.py +947 -0
- chia/_tests/wallet/test_singleton.py +122 -0
- chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
- chia/_tests/wallet/test_singleton_store.py +152 -0
- chia/_tests/wallet/test_taproot.py +19 -0
- chia/_tests/wallet/test_transaction_store.py +945 -0
- chia/_tests/wallet/test_util.py +185 -0
- chia/_tests/wallet/test_wallet.py +2139 -0
- chia/_tests/wallet/test_wallet_action_scope.py +85 -0
- chia/_tests/wallet/test_wallet_blockchain.py +111 -0
- chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
- chia/_tests/wallet/test_wallet_interested_store.py +43 -0
- chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
- chia/_tests/wallet/test_wallet_node.py +780 -0
- chia/_tests/wallet/test_wallet_retry.py +95 -0
- chia/_tests/wallet/test_wallet_state_manager.py +259 -0
- chia/_tests/wallet/test_wallet_test_framework.py +275 -0
- chia/_tests/wallet/test_wallet_trade_store.py +218 -0
- chia/_tests/wallet/test_wallet_user_store.py +34 -0
- chia/_tests/wallet/test_wallet_utils.py +156 -0
- chia/_tests/wallet/vc_wallet/__init__.py +0 -0
- chia/_tests/wallet/vc_wallet/config.py +3 -0
- chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
- chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
- chia/_tests/wallet/vc_wallet/test_vc_wallet.py +830 -0
- chia/_tests/wallet/wallet_block_tools.py +327 -0
- chia/_tests/weight_proof/__init__.py +0 -0
- chia/_tests/weight_proof/config.py +3 -0
- chia/_tests/weight_proof/test_weight_proof.py +528 -0
- chia/apis.py +19 -0
- chia/clvm/__init__.py +0 -0
- chia/cmds/__init__.py +0 -0
- chia/cmds/beta.py +184 -0
- chia/cmds/beta_funcs.py +137 -0
- chia/cmds/check_wallet_db.py +420 -0
- chia/cmds/chia.py +151 -0
- chia/cmds/cmd_classes.py +323 -0
- chia/cmds/cmd_helpers.py +242 -0
- chia/cmds/cmds_util.py +488 -0
- chia/cmds/coin_funcs.py +275 -0
- chia/cmds/coins.py +182 -0
- chia/cmds/completion.py +49 -0
- chia/cmds/configure.py +332 -0
- chia/cmds/dao.py +1064 -0
- chia/cmds/dao_funcs.py +598 -0
- chia/cmds/data.py +708 -0
- chia/cmds/data_funcs.py +385 -0
- chia/cmds/db.py +87 -0
- chia/cmds/db_backup_func.py +77 -0
- chia/cmds/db_upgrade_func.py +452 -0
- chia/cmds/db_validate_func.py +184 -0
- chia/cmds/dev.py +18 -0
- chia/cmds/farm.py +100 -0
- chia/cmds/farm_funcs.py +200 -0
- chia/cmds/gh.py +275 -0
- chia/cmds/init.py +63 -0
- chia/cmds/init_funcs.py +367 -0
- chia/cmds/installers.py +131 -0
- chia/cmds/keys.py +527 -0
- chia/cmds/keys_funcs.py +863 -0
- chia/cmds/netspace.py +50 -0
- chia/cmds/netspace_funcs.py +54 -0
- chia/cmds/options.py +32 -0
- chia/cmds/param_types.py +238 -0
- chia/cmds/passphrase.py +131 -0
- chia/cmds/passphrase_funcs.py +292 -0
- chia/cmds/peer.py +51 -0
- chia/cmds/peer_funcs.py +129 -0
- chia/cmds/plotnft.py +260 -0
- chia/cmds/plotnft_funcs.py +405 -0
- chia/cmds/plots.py +230 -0
- chia/cmds/plotters.py +18 -0
- chia/cmds/rpc.py +208 -0
- chia/cmds/show.py +72 -0
- chia/cmds/show_funcs.py +215 -0
- chia/cmds/signer.py +296 -0
- chia/cmds/sim.py +225 -0
- chia/cmds/sim_funcs.py +509 -0
- chia/cmds/start.py +24 -0
- chia/cmds/start_funcs.py +109 -0
- chia/cmds/stop.py +62 -0
- chia/cmds/units.py +9 -0
- chia/cmds/wallet.py +1901 -0
- chia/cmds/wallet_funcs.py +1874 -0
- chia/consensus/__init__.py +0 -0
- chia/consensus/block_body_validation.py +562 -0
- chia/consensus/block_creation.py +546 -0
- chia/consensus/block_header_validation.py +1059 -0
- chia/consensus/block_record.py +31 -0
- chia/consensus/block_rewards.py +53 -0
- chia/consensus/blockchain.py +1087 -0
- chia/consensus/blockchain_interface.py +56 -0
- chia/consensus/coinbase.py +30 -0
- chia/consensus/condition_costs.py +9 -0
- chia/consensus/constants.py +49 -0
- chia/consensus/cost_calculator.py +15 -0
- chia/consensus/default_constants.py +89 -0
- chia/consensus/deficit.py +55 -0
- chia/consensus/difficulty_adjustment.py +412 -0
- chia/consensus/find_fork_point.py +111 -0
- chia/consensus/full_block_to_block_record.py +167 -0
- chia/consensus/get_block_challenge.py +106 -0
- chia/consensus/get_block_generator.py +27 -0
- chia/consensus/make_sub_epoch_summary.py +210 -0
- chia/consensus/multiprocess_validation.py +268 -0
- chia/consensus/pos_quality.py +19 -0
- chia/consensus/pot_iterations.py +67 -0
- chia/consensus/puzzles/__init__.py +0 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
- chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
- chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
- chia/consensus/vdf_info_computation.py +156 -0
- chia/daemon/__init__.py +0 -0
- chia/daemon/client.py +252 -0
- chia/daemon/keychain_proxy.py +502 -0
- chia/daemon/keychain_server.py +365 -0
- chia/daemon/server.py +1606 -0
- chia/daemon/windows_signal.py +56 -0
- chia/data_layer/__init__.py +0 -0
- chia/data_layer/data_layer.py +1291 -0
- chia/data_layer/data_layer_api.py +33 -0
- chia/data_layer/data_layer_errors.py +50 -0
- chia/data_layer/data_layer_server.py +170 -0
- chia/data_layer/data_layer_util.py +985 -0
- chia/data_layer/data_layer_wallet.py +1311 -0
- chia/data_layer/data_store.py +2267 -0
- chia/data_layer/dl_wallet_store.py +407 -0
- chia/data_layer/download_data.py +389 -0
- chia/data_layer/puzzles/__init__.py +0 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
- chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
- chia/data_layer/s3_plugin_config.yml +33 -0
- chia/data_layer/s3_plugin_service.py +468 -0
- chia/data_layer/util/__init__.py +0 -0
- chia/data_layer/util/benchmark.py +107 -0
- chia/data_layer/util/plugin.py +40 -0
- chia/farmer/__init__.py +0 -0
- chia/farmer/farmer.py +923 -0
- chia/farmer/farmer_api.py +820 -0
- chia/full_node/__init__.py +0 -0
- chia/full_node/bitcoin_fee_estimator.py +85 -0
- chia/full_node/block_height_map.py +271 -0
- chia/full_node/block_store.py +576 -0
- chia/full_node/bundle_tools.py +19 -0
- chia/full_node/coin_store.py +647 -0
- chia/full_node/fee_estimate.py +54 -0
- chia/full_node/fee_estimate_store.py +24 -0
- chia/full_node/fee_estimation.py +92 -0
- chia/full_node/fee_estimator.py +90 -0
- chia/full_node/fee_estimator_constants.py +38 -0
- chia/full_node/fee_estimator_interface.py +42 -0
- chia/full_node/fee_history.py +25 -0
- chia/full_node/fee_tracker.py +564 -0
- chia/full_node/full_node.py +3327 -0
- chia/full_node/full_node_api.py +2025 -0
- chia/full_node/full_node_store.py +1033 -0
- chia/full_node/hint_management.py +56 -0
- chia/full_node/hint_store.py +93 -0
- chia/full_node/mempool.py +589 -0
- chia/full_node/mempool_check_conditions.py +146 -0
- chia/full_node/mempool_manager.py +853 -0
- chia/full_node/pending_tx_cache.py +112 -0
- chia/full_node/puzzles/__init__.py +0 -0
- chia/full_node/puzzles/block_program_zero.clsp +14 -0
- chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
- chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
- chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
- chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
- chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
- chia/full_node/signage_point.py +16 -0
- chia/full_node/subscriptions.py +247 -0
- chia/full_node/sync_store.py +146 -0
- chia/full_node/tx_processing_queue.py +78 -0
- chia/full_node/util/__init__.py +0 -0
- chia/full_node/weight_proof.py +1720 -0
- chia/harvester/__init__.py +0 -0
- chia/harvester/harvester.py +272 -0
- chia/harvester/harvester_api.py +380 -0
- chia/introducer/__init__.py +0 -0
- chia/introducer/introducer.py +122 -0
- chia/introducer/introducer_api.py +70 -0
- chia/legacy/__init__.py +0 -0
- chia/legacy/keyring.py +155 -0
- chia/plot_sync/__init__.py +0 -0
- chia/plot_sync/delta.py +61 -0
- chia/plot_sync/exceptions.py +56 -0
- chia/plot_sync/receiver.py +386 -0
- chia/plot_sync/sender.py +340 -0
- chia/plot_sync/util.py +43 -0
- chia/plotters/__init__.py +0 -0
- chia/plotters/bladebit.py +388 -0
- chia/plotters/chiapos.py +63 -0
- chia/plotters/madmax.py +224 -0
- chia/plotters/plotters.py +577 -0
- chia/plotters/plotters_util.py +133 -0
- chia/plotting/__init__.py +0 -0
- chia/plotting/cache.py +213 -0
- chia/plotting/check_plots.py +283 -0
- chia/plotting/create_plots.py +278 -0
- chia/plotting/manager.py +436 -0
- chia/plotting/util.py +336 -0
- chia/pools/__init__.py +0 -0
- chia/pools/pool_config.py +110 -0
- chia/pools/pool_puzzles.py +459 -0
- chia/pools/pool_wallet.py +933 -0
- chia/pools/pool_wallet_info.py +118 -0
- chia/pools/puzzles/__init__.py +0 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
- chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
- chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
- chia/protocols/__init__.py +0 -0
- chia/protocols/farmer_protocol.py +102 -0
- chia/protocols/full_node_protocol.py +219 -0
- chia/protocols/harvester_protocol.py +216 -0
- chia/protocols/introducer_protocol.py +25 -0
- chia/protocols/pool_protocol.py +177 -0
- chia/protocols/protocol_message_types.py +139 -0
- chia/protocols/protocol_state_machine.py +87 -0
- chia/protocols/protocol_timing.py +8 -0
- chia/protocols/shared_protocol.py +86 -0
- chia/protocols/timelord_protocol.py +93 -0
- chia/protocols/wallet_protocol.py +401 -0
- chia/py.typed +0 -0
- chia/rpc/__init__.py +0 -0
- chia/rpc/crawler_rpc_api.py +80 -0
- chia/rpc/data_layer_rpc_api.py +644 -0
- chia/rpc/data_layer_rpc_client.py +188 -0
- chia/rpc/data_layer_rpc_util.py +58 -0
- chia/rpc/farmer_rpc_api.py +365 -0
- chia/rpc/farmer_rpc_client.py +86 -0
- chia/rpc/full_node_rpc_api.py +959 -0
- chia/rpc/full_node_rpc_client.py +292 -0
- chia/rpc/harvester_rpc_api.py +141 -0
- chia/rpc/harvester_rpc_client.py +54 -0
- chia/rpc/rpc_client.py +164 -0
- chia/rpc/rpc_server.py +521 -0
- chia/rpc/timelord_rpc_api.py +32 -0
- chia/rpc/util.py +93 -0
- chia/rpc/wallet_request_types.py +904 -0
- chia/rpc/wallet_rpc_api.py +4943 -0
- chia/rpc/wallet_rpc_client.py +1814 -0
- chia/seeder/__init__.py +0 -0
- chia/seeder/crawl_store.py +425 -0
- chia/seeder/crawler.py +410 -0
- chia/seeder/crawler_api.py +135 -0
- chia/seeder/dns_server.py +593 -0
- chia/seeder/peer_record.py +146 -0
- chia/seeder/start_crawler.py +92 -0
- chia/server/__init__.py +0 -0
- chia/server/address_manager.py +658 -0
- chia/server/address_manager_store.py +237 -0
- chia/server/api_protocol.py +116 -0
- chia/server/capabilities.py +24 -0
- chia/server/chia_policy.py +346 -0
- chia/server/introducer_peers.py +76 -0
- chia/server/node_discovery.py +714 -0
- chia/server/outbound_message.py +33 -0
- chia/server/rate_limit_numbers.py +214 -0
- chia/server/rate_limits.py +153 -0
- chia/server/server.py +741 -0
- chia/server/signal_handlers.py +120 -0
- chia/server/ssl_context.py +32 -0
- chia/server/start_data_layer.py +151 -0
- chia/server/start_farmer.py +98 -0
- chia/server/start_full_node.py +112 -0
- chia/server/start_harvester.py +93 -0
- chia/server/start_introducer.py +81 -0
- chia/server/start_service.py +316 -0
- chia/server/start_timelord.py +89 -0
- chia/server/start_wallet.py +113 -0
- chia/server/upnp.py +118 -0
- chia/server/ws_connection.py +766 -0
- chia/simulator/__init__.py +0 -0
- chia/simulator/add_blocks_in_batches.py +54 -0
- chia/simulator/block_tools.py +2054 -0
- chia/simulator/full_node_simulator.py +794 -0
- chia/simulator/keyring.py +128 -0
- chia/simulator/setup_services.py +506 -0
- chia/simulator/simulator_constants.py +13 -0
- chia/simulator/simulator_full_node_rpc_api.py +99 -0
- chia/simulator/simulator_full_node_rpc_client.py +60 -0
- chia/simulator/simulator_protocol.py +29 -0
- chia/simulator/simulator_test_tools.py +164 -0
- chia/simulator/socket.py +24 -0
- chia/simulator/ssl_certs.py +114 -0
- chia/simulator/ssl_certs_1.py +697 -0
- chia/simulator/ssl_certs_10.py +697 -0
- chia/simulator/ssl_certs_2.py +697 -0
- chia/simulator/ssl_certs_3.py +697 -0
- chia/simulator/ssl_certs_4.py +697 -0
- chia/simulator/ssl_certs_5.py +697 -0
- chia/simulator/ssl_certs_6.py +697 -0
- chia/simulator/ssl_certs_7.py +697 -0
- chia/simulator/ssl_certs_8.py +697 -0
- chia/simulator/ssl_certs_9.py +697 -0
- chia/simulator/start_simulator.py +143 -0
- chia/simulator/wallet_tools.py +246 -0
- chia/ssl/__init__.py +0 -0
- chia/ssl/chia_ca.crt +19 -0
- chia/ssl/chia_ca.key +28 -0
- chia/ssl/create_ssl.py +249 -0
- chia/ssl/dst_root_ca.pem +20 -0
- chia/timelord/__init__.py +0 -0
- chia/timelord/iters_from_block.py +50 -0
- chia/timelord/timelord.py +1226 -0
- chia/timelord/timelord_api.py +138 -0
- chia/timelord/timelord_launcher.py +190 -0
- chia/timelord/timelord_state.py +244 -0
- chia/timelord/types.py +22 -0
- chia/types/__init__.py +0 -0
- chia/types/aliases.py +35 -0
- chia/types/block_protocol.py +20 -0
- chia/types/blockchain_format/__init__.py +0 -0
- chia/types/blockchain_format/classgroup.py +5 -0
- chia/types/blockchain_format/coin.py +28 -0
- chia/types/blockchain_format/foliage.py +8 -0
- chia/types/blockchain_format/pool_target.py +5 -0
- chia/types/blockchain_format/program.py +269 -0
- chia/types/blockchain_format/proof_of_space.py +135 -0
- chia/types/blockchain_format/reward_chain_block.py +6 -0
- chia/types/blockchain_format/serialized_program.py +5 -0
- chia/types/blockchain_format/sized_bytes.py +11 -0
- chia/types/blockchain_format/slots.py +9 -0
- chia/types/blockchain_format/sub_epoch_summary.py +5 -0
- chia/types/blockchain_format/tree_hash.py +72 -0
- chia/types/blockchain_format/vdf.py +86 -0
- chia/types/clvm_cost.py +13 -0
- chia/types/coin_record.py +43 -0
- chia/types/coin_spend.py +115 -0
- chia/types/condition_opcodes.py +73 -0
- chia/types/condition_with_args.py +16 -0
- chia/types/eligible_coin_spends.py +365 -0
- chia/types/end_of_slot_bundle.py +5 -0
- chia/types/fee_rate.py +38 -0
- chia/types/full_block.py +5 -0
- chia/types/generator_types.py +13 -0
- chia/types/header_block.py +5 -0
- chia/types/internal_mempool_item.py +18 -0
- chia/types/mempool_inclusion_status.py +9 -0
- chia/types/mempool_item.py +85 -0
- chia/types/mempool_submission_status.py +30 -0
- chia/types/mojos.py +7 -0
- chia/types/peer_info.py +64 -0
- chia/types/signing_mode.py +29 -0
- chia/types/spend_bundle.py +30 -0
- chia/types/spend_bundle_conditions.py +7 -0
- chia/types/transaction_queue_entry.py +55 -0
- chia/types/unfinished_block.py +5 -0
- chia/types/unfinished_header_block.py +37 -0
- chia/types/validation_state.py +14 -0
- chia/types/weight_proof.py +49 -0
- chia/util/__init__.py +0 -0
- chia/util/action_scope.py +168 -0
- chia/util/async_pool.py +226 -0
- chia/util/augmented_chain.py +134 -0
- chia/util/batches.py +42 -0
- chia/util/bech32m.py +126 -0
- chia/util/beta_metrics.py +119 -0
- chia/util/block_cache.py +56 -0
- chia/util/byte_types.py +12 -0
- chia/util/check_fork_next_block.py +33 -0
- chia/util/chia_logging.py +144 -0
- chia/util/chia_version.py +33 -0
- chia/util/collection.py +17 -0
- chia/util/condition_tools.py +201 -0
- chia/util/config.py +367 -0
- chia/util/cpu.py +22 -0
- chia/util/db_synchronous.py +23 -0
- chia/util/db_version.py +32 -0
- chia/util/db_wrapper.py +430 -0
- chia/util/default_root.py +27 -0
- chia/util/dump_keyring.py +93 -0
- chia/util/english.txt +2048 -0
- chia/util/errors.py +353 -0
- chia/util/file_keyring.py +469 -0
- chia/util/files.py +97 -0
- chia/util/full_block_utils.py +345 -0
- chia/util/generator_tools.py +72 -0
- chia/util/hash.py +31 -0
- chia/util/initial-config.yaml +694 -0
- chia/util/inline_executor.py +26 -0
- chia/util/ints.py +19 -0
- chia/util/ip_address.py +39 -0
- chia/util/json_util.py +37 -0
- chia/util/keychain.py +676 -0
- chia/util/keyring_wrapper.py +327 -0
- chia/util/limited_semaphore.py +41 -0
- chia/util/lock.py +49 -0
- chia/util/log_exceptions.py +32 -0
- chia/util/logging.py +36 -0
- chia/util/lru_cache.py +31 -0
- chia/util/math.py +20 -0
- chia/util/network.py +182 -0
- chia/util/paginator.py +48 -0
- chia/util/path.py +31 -0
- chia/util/permissions.py +20 -0
- chia/util/prev_transaction_block.py +21 -0
- chia/util/priority_mutex.py +95 -0
- chia/util/profiler.py +197 -0
- chia/util/recursive_replace.py +24 -0
- chia/util/safe_cancel_task.py +16 -0
- chia/util/service_groups.py +47 -0
- chia/util/setproctitle.py +22 -0
- chia/util/significant_bits.py +32 -0
- chia/util/ssl_check.py +213 -0
- chia/util/streamable.py +642 -0
- chia/util/task_referencer.py +59 -0
- chia/util/task_timing.py +382 -0
- chia/util/timing.py +67 -0
- chia/util/vdf_prover.py +30 -0
- chia/util/virtual_project_analysis.py +540 -0
- chia/util/ws_message.py +66 -0
- chia/wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/__init__.py +0 -0
- chia/wallet/cat_wallet/cat_constants.py +75 -0
- chia/wallet/cat_wallet/cat_info.py +47 -0
- chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
- chia/wallet/cat_wallet/cat_utils.py +164 -0
- chia/wallet/cat_wallet/cat_wallet.py +855 -0
- chia/wallet/cat_wallet/dao_cat_info.py +28 -0
- chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
- chia/wallet/cat_wallet/lineage_store.py +74 -0
- chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
- chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
- chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
- chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
- chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
- chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
- chia/wallet/coin_selection.py +188 -0
- chia/wallet/conditions.py +1512 -0
- chia/wallet/dao_wallet/__init__.py +0 -0
- chia/wallet/dao_wallet/dao_info.py +61 -0
- chia/wallet/dao_wallet/dao_utils.py +811 -0
- chia/wallet/dao_wallet/dao_wallet.py +2119 -0
- chia/wallet/db_wallet/__init__.py +0 -0
- chia/wallet/db_wallet/db_wallet_puzzles.py +111 -0
- chia/wallet/derivation_record.py +30 -0
- chia/wallet/derive_keys.py +146 -0
- chia/wallet/did_wallet/__init__.py +0 -0
- chia/wallet/did_wallet/did_info.py +39 -0
- chia/wallet/did_wallet/did_wallet.py +1494 -0
- chia/wallet/did_wallet/did_wallet_puzzles.py +221 -0
- chia/wallet/did_wallet/puzzles/__init__.py +0 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
- chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
- chia/wallet/driver_protocol.py +26 -0
- chia/wallet/key_val_store.py +55 -0
- chia/wallet/lineage_proof.py +58 -0
- chia/wallet/nft_wallet/__init__.py +0 -0
- chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
- chia/wallet/nft_wallet/nft_info.py +120 -0
- chia/wallet/nft_wallet/nft_puzzles.py +305 -0
- chia/wallet/nft_wallet/nft_wallet.py +1687 -0
- chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
- chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
- chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
- chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
- chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
- chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
- chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
- chia/wallet/nft_wallet/uncurry_nft.py +217 -0
- chia/wallet/notification_manager.py +117 -0
- chia/wallet/notification_store.py +178 -0
- chia/wallet/outer_puzzles.py +84 -0
- chia/wallet/payment.py +33 -0
- chia/wallet/puzzle_drivers.py +118 -0
- chia/wallet/puzzles/__init__.py +0 -0
- chia/wallet/puzzles/augmented_condition.clsp +13 -0
- chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
- chia/wallet/puzzles/clawback/__init__.py +0 -0
- chia/wallet/puzzles/clawback/drivers.py +188 -0
- chia/wallet/puzzles/clawback/metadata.py +38 -0
- chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
- chia/wallet/puzzles/condition_codes.clib +77 -0
- chia/wallet/puzzles/curry-and-treehash.clib +102 -0
- chia/wallet/puzzles/curry.clib +135 -0
- chia/wallet/puzzles/curry_by_index.clib +16 -0
- chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
- chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
- chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp +35 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
- chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_lockup.clsp +288 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
- chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal.clsp +377 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
- chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
- chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
- chia/wallet/puzzles/dao_treasury.clsp +115 -0
- chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
- chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
- chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
- chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
- chia/wallet/puzzles/json.clib +25 -0
- chia/wallet/puzzles/load_clvm.py +161 -0
- chia/wallet/puzzles/merkle_utils.clib +18 -0
- chia/wallet/puzzles/notification.clsp +7 -0
- chia/wallet/puzzles/notification.clsp.hex +1 -0
- chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
- chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.clsp +3 -0
- chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_conditions.py +26 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
- chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_conditions.py +21 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
- chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle.py +34 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +160 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
- chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +21 -0
- chia/wallet/puzzles/p2_parent.clsp +19 -0
- chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
- chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_puzzle_hash.py +27 -0
- chia/wallet/puzzles/p2_singleton.clsp +30 -0
- chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
- chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
- chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
- chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/puzzles/puzzle_utils.py +34 -0
- chia/wallet/puzzles/settlement_payments.clsp +49 -0
- chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
- chia/wallet/puzzles/sha256tree.clib +11 -0
- chia/wallet/puzzles/singleton_launcher.clsp +16 -0
- chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
- chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer.py +296 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
- chia/wallet/puzzles/singleton_top_layer_v1_1.py +345 -0
- chia/wallet/puzzles/singleton_truths.clib +21 -0
- chia/wallet/puzzles/tails.py +348 -0
- chia/wallet/puzzles/utility_macros.clib +48 -0
- chia/wallet/signer_protocol.py +125 -0
- chia/wallet/singleton.py +106 -0
- chia/wallet/singleton_record.py +30 -0
- chia/wallet/trade_manager.py +1102 -0
- chia/wallet/trade_record.py +67 -0
- chia/wallet/trading/__init__.py +0 -0
- chia/wallet/trading/offer.py +702 -0
- chia/wallet/trading/trade_status.py +13 -0
- chia/wallet/trading/trade_store.py +526 -0
- chia/wallet/transaction_record.py +158 -0
- chia/wallet/transaction_sorting.py +14 -0
- chia/wallet/uncurried_puzzle.py +17 -0
- chia/wallet/util/__init__.py +0 -0
- chia/wallet/util/address_type.py +55 -0
- chia/wallet/util/blind_signer_tl.py +164 -0
- chia/wallet/util/clvm_streamable.py +203 -0
- chia/wallet/util/compute_hints.py +66 -0
- chia/wallet/util/compute_memos.py +43 -0
- chia/wallet/util/curry_and_treehash.py +91 -0
- chia/wallet/util/debug_spend_bundle.py +232 -0
- chia/wallet/util/merkle_tree.py +100 -0
- chia/wallet/util/merkle_utils.py +102 -0
- chia/wallet/util/new_peak_queue.py +82 -0
- chia/wallet/util/notifications.py +12 -0
- chia/wallet/util/peer_request_cache.py +174 -0
- chia/wallet/util/pprint.py +39 -0
- chia/wallet/util/puzzle_compression.py +95 -0
- chia/wallet/util/puzzle_decorator.py +100 -0
- chia/wallet/util/puzzle_decorator_type.py +7 -0
- chia/wallet/util/query_filter.py +59 -0
- chia/wallet/util/transaction_type.py +23 -0
- chia/wallet/util/tx_config.py +158 -0
- chia/wallet/util/wallet_sync_utils.py +351 -0
- chia/wallet/util/wallet_types.py +72 -0
- chia/wallet/vc_wallet/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_cat_drivers.py +664 -0
- chia/wallet/vc_wallet/cr_cat_wallet.py +877 -0
- chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
- chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
- chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
- chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
- chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_drivers.py +838 -0
- chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
- chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
- chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
- chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
- chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
- chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
- chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
- chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
- chia/wallet/vc_wallet/vc_store.py +263 -0
- chia/wallet/vc_wallet/vc_wallet.py +638 -0
- chia/wallet/wallet.py +698 -0
- chia/wallet/wallet_action_scope.py +96 -0
- chia/wallet/wallet_blockchain.py +244 -0
- chia/wallet/wallet_coin_record.py +72 -0
- chia/wallet/wallet_coin_store.py +351 -0
- chia/wallet/wallet_info.py +35 -0
- chia/wallet/wallet_interested_store.py +188 -0
- chia/wallet/wallet_nft_store.py +279 -0
- chia/wallet/wallet_node.py +1765 -0
- chia/wallet/wallet_node_api.py +207 -0
- chia/wallet/wallet_pool_store.py +119 -0
- chia/wallet/wallet_protocol.py +90 -0
- chia/wallet/wallet_puzzle_store.py +396 -0
- chia/wallet/wallet_retry_store.py +70 -0
- chia/wallet/wallet_singleton_store.py +259 -0
- chia/wallet/wallet_spend_bundle.py +25 -0
- chia/wallet/wallet_state_manager.py +2819 -0
- chia/wallet/wallet_transaction_store.py +496 -0
- chia/wallet/wallet_user_store.py +110 -0
- chia/wallet/wallet_weight_proof_handler.py +126 -0
- chia_blockchain-2.5.1rc1.dist-info/LICENSE +201 -0
- chia_blockchain-2.5.1rc1.dist-info/METADATA +156 -0
- chia_blockchain-2.5.1rc1.dist-info/RECORD +1042 -0
- chia_blockchain-2.5.1rc1.dist-info/WHEEL +4 -0
- chia_blockchain-2.5.1rc1.dist-info/entry_points.txt +17 -0
- mozilla-ca/cacert.pem +3611 -0
|
@@ -0,0 +1,1291 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import dataclasses
|
|
6
|
+
import functools
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import random
|
|
11
|
+
import time
|
|
12
|
+
import traceback
|
|
13
|
+
from collections.abc import AsyncIterator, Awaitable
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast, final
|
|
16
|
+
|
|
17
|
+
import aiohttp
|
|
18
|
+
|
|
19
|
+
from chia.data_layer.data_layer_errors import KeyNotFoundError
|
|
20
|
+
from chia.data_layer.data_layer_util import (
|
|
21
|
+
DiffData,
|
|
22
|
+
InternalNode,
|
|
23
|
+
KeysPaginationData,
|
|
24
|
+
KeysValuesPaginationData,
|
|
25
|
+
KeyValue,
|
|
26
|
+
KVDiffPaginationData,
|
|
27
|
+
Layer,
|
|
28
|
+
Offer,
|
|
29
|
+
OfferStore,
|
|
30
|
+
PluginRemote,
|
|
31
|
+
PluginStatus,
|
|
32
|
+
Proof,
|
|
33
|
+
ProofOfInclusion,
|
|
34
|
+
ProofOfInclusionLayer,
|
|
35
|
+
Root,
|
|
36
|
+
ServerInfo,
|
|
37
|
+
Status,
|
|
38
|
+
StoreProofs,
|
|
39
|
+
Subscription,
|
|
40
|
+
SyncStatus,
|
|
41
|
+
TerminalNode,
|
|
42
|
+
Unspecified,
|
|
43
|
+
UnsubscribeData,
|
|
44
|
+
leaf_hash,
|
|
45
|
+
unspecified,
|
|
46
|
+
)
|
|
47
|
+
from chia.data_layer.data_layer_wallet import DataLayerWallet, Mirror, SingletonRecord, verify_offer
|
|
48
|
+
from chia.data_layer.data_store import DataStore
|
|
49
|
+
from chia.data_layer.download_data import (
|
|
50
|
+
delete_full_file_if_exists,
|
|
51
|
+
get_delta_filename_path,
|
|
52
|
+
get_full_tree_filename_path,
|
|
53
|
+
insert_from_delta_file,
|
|
54
|
+
write_files_for_root,
|
|
55
|
+
)
|
|
56
|
+
from chia.rpc.rpc_server import StateChangedProtocol, default_get_connections
|
|
57
|
+
from chia.rpc.wallet_request_types import LogIn
|
|
58
|
+
from chia.rpc.wallet_rpc_client import WalletRpcClient
|
|
59
|
+
from chia.server.outbound_message import NodeType
|
|
60
|
+
from chia.server.server import ChiaServer
|
|
61
|
+
from chia.server.ws_connection import WSChiaConnection
|
|
62
|
+
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
63
|
+
from chia.util.async_pool import Job, QueuedAsyncPool
|
|
64
|
+
from chia.util.ints import uint32, uint64
|
|
65
|
+
from chia.util.path import path_from_root
|
|
66
|
+
from chia.util.task_referencer import create_referenced_task
|
|
67
|
+
from chia.wallet.trade_record import TradeRecord
|
|
68
|
+
from chia.wallet.trading.offer import Offer as TradingOffer
|
|
69
|
+
from chia.wallet.transaction_record import TransactionRecord
|
|
70
|
+
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def get_plugin_info(plugin_remote: PluginRemote) -> tuple[PluginRemote, dict[str, Any]]:
|
|
74
|
+
try:
|
|
75
|
+
async with aiohttp.ClientSession() as session:
|
|
76
|
+
async with session.post(
|
|
77
|
+
plugin_remote.url + "/plugin_info",
|
|
78
|
+
json={},
|
|
79
|
+
headers=plugin_remote.headers,
|
|
80
|
+
) as response:
|
|
81
|
+
ret = {"status": response.status}
|
|
82
|
+
if response.status == 200:
|
|
83
|
+
ret["response"] = json.loads(await response.text())
|
|
84
|
+
return plugin_remote, ret
|
|
85
|
+
except aiohttp.ClientError as e:
|
|
86
|
+
return plugin_remote, {"error": f"ClientError: {e}"}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@final
|
|
90
|
+
@dataclasses.dataclass
|
|
91
|
+
class DataLayer:
|
|
92
|
+
if TYPE_CHECKING:
|
|
93
|
+
from chia.rpc.rpc_server import RpcServiceProtocol
|
|
94
|
+
|
|
95
|
+
_protocol_check: ClassVar[RpcServiceProtocol] = cast("DataLayer", None)
|
|
96
|
+
|
|
97
|
+
db_path: Path
|
|
98
|
+
config: dict[str, Any]
|
|
99
|
+
root_path: Path
|
|
100
|
+
log: logging.Logger
|
|
101
|
+
wallet_rpc_init: Awaitable[WalletRpcClient]
|
|
102
|
+
downloaders: list[PluginRemote]
|
|
103
|
+
uploaders: list[PluginRemote]
|
|
104
|
+
maximum_full_file_count: int
|
|
105
|
+
server_files_location: Path
|
|
106
|
+
unsubscribe_data_queue: list[UnsubscribeData]
|
|
107
|
+
_server: Optional[ChiaServer] = None
|
|
108
|
+
none_bytes: bytes32 = bytes32.zeros
|
|
109
|
+
initialized: bool = False
|
|
110
|
+
_data_store: Optional[DataStore] = None
|
|
111
|
+
state_changed_callback: Optional[StateChangedProtocol] = None
|
|
112
|
+
_shut_down: bool = False
|
|
113
|
+
periodically_manage_data_task: Optional[asyncio.Task[None]] = None
|
|
114
|
+
_wallet_rpc: Optional[WalletRpcClient] = None
|
|
115
|
+
subscription_lock: asyncio.Lock = dataclasses.field(default_factory=asyncio.Lock)
|
|
116
|
+
subscription_update_concurrency: int = 5
|
|
117
|
+
client_timeout: aiohttp.ClientTimeout = dataclasses.field(
|
|
118
|
+
default_factory=functools.partial(aiohttp.ClientTimeout, total=45, sock_connect=5)
|
|
119
|
+
)
|
|
120
|
+
group_files_by_store: bool = False
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def server(self) -> ChiaServer:
|
|
124
|
+
# This is a stop gap until the class usage is refactored such the values of
|
|
125
|
+
# integral attributes are known at creation of the instance.
|
|
126
|
+
if self._server is None:
|
|
127
|
+
raise RuntimeError("server not assigned")
|
|
128
|
+
|
|
129
|
+
return self._server
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def data_store(self) -> DataStore:
|
|
133
|
+
# This is a stop gap until the class usage is refactored such the values of
|
|
134
|
+
# integral attributes are known at creation of the instance.
|
|
135
|
+
if self._data_store is None:
|
|
136
|
+
raise RuntimeError("data_store not assigned")
|
|
137
|
+
|
|
138
|
+
return self._data_store
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def wallet_rpc(self) -> WalletRpcClient:
|
|
142
|
+
# This is a stop gap until the class usage is refactored such the values of
|
|
143
|
+
# integral attributes are known at creation of the instance.
|
|
144
|
+
if self._wallet_rpc is None:
|
|
145
|
+
raise RuntimeError("wallet_rpc not assigned")
|
|
146
|
+
|
|
147
|
+
return self._wallet_rpc
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def create(
|
|
151
|
+
cls,
|
|
152
|
+
config: dict[str, Any],
|
|
153
|
+
root_path: Path,
|
|
154
|
+
wallet_rpc_init: Awaitable[WalletRpcClient],
|
|
155
|
+
downloaders: list[PluginRemote],
|
|
156
|
+
uploaders: list[PluginRemote], # dont add FilesystemUploader to this, it is the default uploader
|
|
157
|
+
name: Optional[str] = None,
|
|
158
|
+
) -> DataLayer:
|
|
159
|
+
if name == "":
|
|
160
|
+
# TODO: If no code depends on "" counting as 'unspecified' then we do not
|
|
161
|
+
# need this.
|
|
162
|
+
name = None
|
|
163
|
+
|
|
164
|
+
server_files_replaced: str = config.get(
|
|
165
|
+
"server_files_location", "data_layer/db/server_files_location_CHALLENGE"
|
|
166
|
+
).replace("CHALLENGE", config["selected_network"])
|
|
167
|
+
|
|
168
|
+
db_path_replaced: str = config["database_path"].replace("CHALLENGE", config["selected_network"])
|
|
169
|
+
|
|
170
|
+
self = cls(
|
|
171
|
+
config=config,
|
|
172
|
+
root_path=root_path,
|
|
173
|
+
wallet_rpc_init=wallet_rpc_init,
|
|
174
|
+
log=logging.getLogger(name if name is None else __name__),
|
|
175
|
+
db_path=path_from_root(root_path, db_path_replaced),
|
|
176
|
+
server_files_location=path_from_root(root_path, server_files_replaced),
|
|
177
|
+
downloaders=downloaders,
|
|
178
|
+
uploaders=uploaders,
|
|
179
|
+
maximum_full_file_count=config.get("maximum_full_file_count", 1),
|
|
180
|
+
subscription_update_concurrency=config.get("subscription_update_concurrency", 5),
|
|
181
|
+
unsubscribe_data_queue=[],
|
|
182
|
+
client_timeout=aiohttp.ClientTimeout(
|
|
183
|
+
total=config.get("client_timeout", 45), sock_connect=config.get("connect_timeout", 5)
|
|
184
|
+
),
|
|
185
|
+
group_files_by_store=config.get("group_files_by_store", False),
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
189
|
+
self.server_files_location.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
|
|
191
|
+
return self
|
|
192
|
+
|
|
193
|
+
@contextlib.asynccontextmanager
|
|
194
|
+
async def manage(self) -> AsyncIterator[None]:
|
|
195
|
+
sql_log_path: Optional[Path] = None
|
|
196
|
+
if self.config.get("log_sqlite_cmds", False):
|
|
197
|
+
sql_log_path = path_from_root(self.root_path, "log/data_sql.log")
|
|
198
|
+
self.log.info(f"logging SQL commands to {sql_log_path}")
|
|
199
|
+
|
|
200
|
+
async with DataStore.managed(database=self.db_path, sql_log_path=sql_log_path) as self._data_store:
|
|
201
|
+
self._wallet_rpc = await self.wallet_rpc_init
|
|
202
|
+
|
|
203
|
+
await self._data_store.migrate_db()
|
|
204
|
+
self.periodically_manage_data_task = create_referenced_task(self.periodically_manage_data())
|
|
205
|
+
try:
|
|
206
|
+
yield
|
|
207
|
+
finally:
|
|
208
|
+
# TODO: review for anything else we need to do here
|
|
209
|
+
self._shut_down = True
|
|
210
|
+
if self._wallet_rpc is not None:
|
|
211
|
+
self.wallet_rpc.close()
|
|
212
|
+
|
|
213
|
+
if self.periodically_manage_data_task is not None:
|
|
214
|
+
try:
|
|
215
|
+
self.periodically_manage_data_task.cancel()
|
|
216
|
+
except asyncio.CancelledError:
|
|
217
|
+
pass
|
|
218
|
+
if self._wallet_rpc is not None:
|
|
219
|
+
await self.wallet_rpc.await_closed()
|
|
220
|
+
|
|
221
|
+
def _set_state_changed_callback(self, callback: StateChangedProtocol) -> None:
|
|
222
|
+
self.state_changed_callback = callback
|
|
223
|
+
|
|
224
|
+
async def on_connect(self, connection: WSChiaConnection) -> None:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
def get_connections(self, request_node_type: Optional[NodeType]) -> list[dict[str, Any]]:
|
|
228
|
+
return default_get_connections(server=self.server, request_node_type=request_node_type)
|
|
229
|
+
|
|
230
|
+
def set_server(self, server: ChiaServer) -> None:
|
|
231
|
+
self._server = server
|
|
232
|
+
|
|
233
|
+
async def wallet_log_in(self, fingerprint: int) -> int:
|
|
234
|
+
try:
|
|
235
|
+
result = await self.wallet_rpc.log_in(LogIn(uint32(fingerprint)))
|
|
236
|
+
except ValueError as e:
|
|
237
|
+
raise Exception(f"DataLayer wallet RPC log in request failed: {e.args[0]}")
|
|
238
|
+
|
|
239
|
+
return result.fingerprint
|
|
240
|
+
|
|
241
|
+
async def create_store(self, fee: uint64, root: bytes32 = bytes32.zeros) -> tuple[list[TransactionRecord], bytes32]:
|
|
242
|
+
txs, store_id = await self.wallet_rpc.create_new_dl(root, fee)
|
|
243
|
+
res = await self.data_store.create_tree(store_id=store_id)
|
|
244
|
+
if res is None:
|
|
245
|
+
self.log.fatal("failed creating store")
|
|
246
|
+
self.initialized = True
|
|
247
|
+
return txs, store_id
|
|
248
|
+
|
|
249
|
+
async def batch_update(
|
|
250
|
+
self,
|
|
251
|
+
store_id: bytes32,
|
|
252
|
+
changelist: list[dict[str, Any]],
|
|
253
|
+
fee: uint64,
|
|
254
|
+
submit_on_chain: bool = True,
|
|
255
|
+
) -> Optional[TransactionRecord]:
|
|
256
|
+
status = Status.PENDING if submit_on_chain else Status.PENDING_BATCH
|
|
257
|
+
await self.batch_insert(store_id=store_id, changelist=changelist, status=status)
|
|
258
|
+
await self.data_store.clean_node_table()
|
|
259
|
+
|
|
260
|
+
if submit_on_chain:
|
|
261
|
+
return await self.publish_update(store_id=store_id, fee=fee)
|
|
262
|
+
else:
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
async def _get_publishable_root_hash(self, store_id: bytes32) -> bytes32:
|
|
266
|
+
pending_root: Optional[Root] = await self.data_store.get_pending_root(store_id=store_id)
|
|
267
|
+
if pending_root is None:
|
|
268
|
+
raise Exception("Latest root is already confirmed.")
|
|
269
|
+
if pending_root.status == Status.PENDING_BATCH:
|
|
270
|
+
raise Exception("Unable to publish on chain, batch update set still open.")
|
|
271
|
+
|
|
272
|
+
return self.none_bytes if pending_root.node_hash is None else pending_root.node_hash
|
|
273
|
+
|
|
274
|
+
async def multistore_batch_update(
|
|
275
|
+
self,
|
|
276
|
+
store_updates: list[dict[str, Any]],
|
|
277
|
+
fee: uint64,
|
|
278
|
+
submit_on_chain: bool = True,
|
|
279
|
+
) -> list[TransactionRecord]:
|
|
280
|
+
store_ids: set[bytes32] = set()
|
|
281
|
+
for update in store_updates:
|
|
282
|
+
store_id = update["store_id"]
|
|
283
|
+
changelist = update["changelist"]
|
|
284
|
+
|
|
285
|
+
if store_id in store_ids:
|
|
286
|
+
raise Exception(f"Store id {store_id.hex()} must appear in a single update")
|
|
287
|
+
store_ids.add(store_id)
|
|
288
|
+
|
|
289
|
+
status = Status.PENDING if submit_on_chain else Status.PENDING_BATCH
|
|
290
|
+
await self.batch_insert(store_id=store_id, changelist=changelist, status=status)
|
|
291
|
+
|
|
292
|
+
await self.data_store.clean_node_table()
|
|
293
|
+
|
|
294
|
+
if submit_on_chain:
|
|
295
|
+
update_dictionary: dict[bytes32, bytes32] = {}
|
|
296
|
+
for store_id in store_ids:
|
|
297
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
298
|
+
root_hash = await self._get_publishable_root_hash(store_id=store_id)
|
|
299
|
+
update_dictionary[store_id] = root_hash
|
|
300
|
+
transaction_records = await self.wallet_rpc.dl_update_multiple(update_dictionary=update_dictionary, fee=fee)
|
|
301
|
+
return transaction_records
|
|
302
|
+
else:
|
|
303
|
+
return []
|
|
304
|
+
|
|
305
|
+
async def submit_pending_root(
|
|
306
|
+
self,
|
|
307
|
+
store_id: bytes32,
|
|
308
|
+
fee: uint64,
|
|
309
|
+
) -> TransactionRecord:
|
|
310
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
311
|
+
|
|
312
|
+
pending_root: Optional[Root] = await self.data_store.get_pending_root(store_id=store_id)
|
|
313
|
+
if pending_root is None:
|
|
314
|
+
raise Exception("Latest root is already confirmed.")
|
|
315
|
+
if pending_root.status == Status.PENDING:
|
|
316
|
+
raise Exception("Pending root is already submitted.")
|
|
317
|
+
|
|
318
|
+
await self.data_store.change_root_status(pending_root, Status.PENDING)
|
|
319
|
+
return await self.publish_update(store_id, fee)
|
|
320
|
+
|
|
321
|
+
async def submit_all_pending_roots(self, fee: uint64) -> list[TransactionRecord]:
|
|
322
|
+
pending_roots = await self.data_store.get_all_pending_batches_roots()
|
|
323
|
+
update_dictionary: dict[bytes32, bytes32] = {}
|
|
324
|
+
if len(pending_roots) == 0:
|
|
325
|
+
raise Exception("No pending roots found to submit")
|
|
326
|
+
for pending_root in pending_roots:
|
|
327
|
+
root_hash = pending_root.node_hash if pending_root.node_hash is not None else self.none_bytes
|
|
328
|
+
update_dictionary[pending_root.store_id] = root_hash
|
|
329
|
+
await self.data_store.change_root_status(pending_root, Status.PENDING)
|
|
330
|
+
transaction_records = await self.wallet_rpc.dl_update_multiple(update_dictionary=update_dictionary, fee=fee)
|
|
331
|
+
return transaction_records
|
|
332
|
+
|
|
333
|
+
async def batch_insert(
|
|
334
|
+
self,
|
|
335
|
+
store_id: bytes32,
|
|
336
|
+
changelist: list[dict[str, Any]],
|
|
337
|
+
status: Status = Status.PENDING,
|
|
338
|
+
enable_batch_autoinsert: Optional[bool] = None,
|
|
339
|
+
) -> bytes32:
|
|
340
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
341
|
+
|
|
342
|
+
async with self.data_store.transaction():
|
|
343
|
+
pending_root: Optional[Root] = await self.data_store.get_pending_root(store_id=store_id)
|
|
344
|
+
if pending_root is not None and pending_root.status == Status.PENDING:
|
|
345
|
+
raise Exception("Already have a pending root waiting for confirmation.")
|
|
346
|
+
|
|
347
|
+
# check before any DL changes that this singleton is currently owned by this wallet
|
|
348
|
+
singleton_records: list[SingletonRecord] = await self.get_owned_stores()
|
|
349
|
+
if not any(store_id == singleton.launcher_id for singleton in singleton_records):
|
|
350
|
+
raise ValueError(f"Singleton with launcher ID {store_id} is not owned by DL Wallet")
|
|
351
|
+
|
|
352
|
+
t1 = time.monotonic()
|
|
353
|
+
if enable_batch_autoinsert is None:
|
|
354
|
+
enable_batch_autoinsert = self.config.get("enable_batch_autoinsert", True)
|
|
355
|
+
batch_hash = await self.data_store.insert_batch(store_id, changelist, status, enable_batch_autoinsert)
|
|
356
|
+
t2 = time.monotonic()
|
|
357
|
+
self.log.info(f"Data store batch update process time: {t2 - t1}.")
|
|
358
|
+
# todo return empty node hash from get_tree_root
|
|
359
|
+
if batch_hash is not None:
|
|
360
|
+
node_hash = batch_hash
|
|
361
|
+
else:
|
|
362
|
+
node_hash = self.none_bytes # todo change
|
|
363
|
+
|
|
364
|
+
return node_hash
|
|
365
|
+
|
|
366
|
+
async def publish_update(
|
|
367
|
+
self,
|
|
368
|
+
store_id: bytes32,
|
|
369
|
+
fee: uint64,
|
|
370
|
+
) -> TransactionRecord:
|
|
371
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
372
|
+
root_hash = await self._get_publishable_root_hash(store_id=store_id)
|
|
373
|
+
transaction_record = await self.wallet_rpc.dl_update_root(
|
|
374
|
+
launcher_id=store_id,
|
|
375
|
+
new_root=root_hash,
|
|
376
|
+
fee=fee,
|
|
377
|
+
)
|
|
378
|
+
return transaction_record
|
|
379
|
+
|
|
380
|
+
async def get_key_value_hash(
|
|
381
|
+
self,
|
|
382
|
+
store_id: bytes32,
|
|
383
|
+
key: bytes,
|
|
384
|
+
root_hash: Union[bytes32, Unspecified] = unspecified,
|
|
385
|
+
) -> bytes32:
|
|
386
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
387
|
+
|
|
388
|
+
async with self.data_store.transaction():
|
|
389
|
+
node = await self.data_store.get_node_by_key(store_id=store_id, key=key, root_hash=root_hash)
|
|
390
|
+
return node.hash
|
|
391
|
+
|
|
392
|
+
async def get_value(
|
|
393
|
+
self, store_id: bytes32, key: bytes, root_hash: Union[bytes32, Unspecified] = unspecified
|
|
394
|
+
) -> bytes:
|
|
395
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
396
|
+
|
|
397
|
+
async with self.data_store.transaction():
|
|
398
|
+
# this either returns the node or raises an exception
|
|
399
|
+
res = await self.data_store.get_node_by_key(store_id=store_id, key=key, root_hash=root_hash)
|
|
400
|
+
return res.value
|
|
401
|
+
|
|
402
|
+
async def get_keys_values(
|
|
403
|
+
self,
|
|
404
|
+
store_id: bytes32,
|
|
405
|
+
root_hash: Union[bytes32, Unspecified],
|
|
406
|
+
) -> list[TerminalNode]:
|
|
407
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
408
|
+
|
|
409
|
+
res = await self.data_store.get_keys_values(store_id, root_hash)
|
|
410
|
+
if res is None:
|
|
411
|
+
self.log.error("Failed to fetch keys values")
|
|
412
|
+
return res
|
|
413
|
+
|
|
414
|
+
async def get_keys_values_paginated(
|
|
415
|
+
self,
|
|
416
|
+
store_id: bytes32,
|
|
417
|
+
root_hash: Union[bytes32, Unspecified],
|
|
418
|
+
page: int,
|
|
419
|
+
max_page_size: Optional[int] = None,
|
|
420
|
+
) -> KeysValuesPaginationData:
|
|
421
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
422
|
+
|
|
423
|
+
if max_page_size is None:
|
|
424
|
+
max_page_size = 40 * 1024 * 1024
|
|
425
|
+
res = await self.data_store.get_keys_values_paginated(store_id, page, max_page_size, root_hash)
|
|
426
|
+
return res
|
|
427
|
+
|
|
428
|
+
async def get_keys(self, store_id: bytes32, root_hash: Union[bytes32, Unspecified]) -> list[bytes]:
|
|
429
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
430
|
+
|
|
431
|
+
res = await self.data_store.get_keys(store_id, root_hash)
|
|
432
|
+
return res
|
|
433
|
+
|
|
434
|
+
async def get_keys_paginated(
|
|
435
|
+
self,
|
|
436
|
+
store_id: bytes32,
|
|
437
|
+
root_hash: Union[bytes32, Unspecified],
|
|
438
|
+
page: int,
|
|
439
|
+
max_page_size: Optional[int] = None,
|
|
440
|
+
) -> KeysPaginationData:
|
|
441
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
442
|
+
|
|
443
|
+
if max_page_size is None:
|
|
444
|
+
max_page_size = 40 * 1024 * 1024
|
|
445
|
+
res = await self.data_store.get_keys_paginated(store_id, page, max_page_size, root_hash)
|
|
446
|
+
return res
|
|
447
|
+
|
|
448
|
+
async def get_ancestors(self, node_hash: bytes32, store_id: bytes32) -> list[InternalNode]:
|
|
449
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
450
|
+
|
|
451
|
+
res = await self.data_store.get_ancestors(node_hash=node_hash, store_id=store_id)
|
|
452
|
+
if res is None:
|
|
453
|
+
self.log.error("Failed to get ancestors")
|
|
454
|
+
return res
|
|
455
|
+
|
|
456
|
+
async def get_root(self, store_id: bytes32) -> Optional[SingletonRecord]:
|
|
457
|
+
latest = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
458
|
+
if latest is None:
|
|
459
|
+
self.log.error(f"Failed to get root for {store_id.hex()}")
|
|
460
|
+
return latest
|
|
461
|
+
|
|
462
|
+
async def get_local_root(self, store_id: bytes32) -> Optional[bytes32]:
|
|
463
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
464
|
+
|
|
465
|
+
res = await self.data_store.get_tree_root(store_id=store_id)
|
|
466
|
+
if res is None:
|
|
467
|
+
self.log.error(f"Failed to get root for {store_id.hex()}")
|
|
468
|
+
return None
|
|
469
|
+
return res.node_hash
|
|
470
|
+
|
|
471
|
+
async def get_root_history(self, store_id: bytes32) -> list[SingletonRecord]:
|
|
472
|
+
records = await self.wallet_rpc.dl_history(store_id)
|
|
473
|
+
if records is None:
|
|
474
|
+
self.log.error(f"Failed to get root history for {store_id.hex()}")
|
|
475
|
+
root_history = []
|
|
476
|
+
prev: Optional[SingletonRecord] = None
|
|
477
|
+
for record in records:
|
|
478
|
+
if prev is None or record.root != prev.root:
|
|
479
|
+
root_history.append(record)
|
|
480
|
+
prev = record
|
|
481
|
+
return root_history
|
|
482
|
+
|
|
483
|
+
async def _update_confirmation_status(self, store_id: bytes32) -> None:
|
|
484
|
+
async with self.data_store.transaction():
|
|
485
|
+
try:
|
|
486
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
487
|
+
except Exception:
|
|
488
|
+
root = None
|
|
489
|
+
singleton_record: Optional[SingletonRecord] = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
490
|
+
if singleton_record is None:
|
|
491
|
+
return
|
|
492
|
+
if root is None:
|
|
493
|
+
pending_root = await self.data_store.get_pending_root(store_id=store_id)
|
|
494
|
+
if pending_root is not None and pending_root.status == Status.PENDING:
|
|
495
|
+
if pending_root.generation == 0 and pending_root.node_hash is None:
|
|
496
|
+
await self.data_store.change_root_status(pending_root, Status.COMMITTED)
|
|
497
|
+
await self.data_store.clear_pending_roots(store_id=store_id)
|
|
498
|
+
return
|
|
499
|
+
else:
|
|
500
|
+
root = None
|
|
501
|
+
if root is None:
|
|
502
|
+
self.log.info(f"Don't have pending root for {store_id}.")
|
|
503
|
+
return
|
|
504
|
+
if root.generation == singleton_record.generation:
|
|
505
|
+
return
|
|
506
|
+
if root.generation > singleton_record.generation:
|
|
507
|
+
self.log.info(
|
|
508
|
+
f"Local root ahead of chain root: {root.generation} {singleton_record.generation}. "
|
|
509
|
+
"Maybe we're doing a batch update."
|
|
510
|
+
)
|
|
511
|
+
return
|
|
512
|
+
wallet_history = await self.wallet_rpc.dl_history(
|
|
513
|
+
launcher_id=store_id,
|
|
514
|
+
min_generation=uint32(root.generation + 1),
|
|
515
|
+
max_generation=singleton_record.generation,
|
|
516
|
+
)
|
|
517
|
+
new_hashes = [record.root for record in reversed(wallet_history)]
|
|
518
|
+
root_hash = self.none_bytes if root.node_hash is None else root.node_hash
|
|
519
|
+
generation_shift = 0
|
|
520
|
+
while len(new_hashes) > 0 and new_hashes[0] == root_hash:
|
|
521
|
+
generation_shift += 1
|
|
522
|
+
new_hashes.pop(0)
|
|
523
|
+
if generation_shift > 0:
|
|
524
|
+
await self.data_store.clear_pending_roots(store_id=store_id)
|
|
525
|
+
await self.data_store.shift_root_generations(store_id=store_id, shift_size=generation_shift)
|
|
526
|
+
else:
|
|
527
|
+
expected_root_hash = None if new_hashes[0] == self.none_bytes else new_hashes[0]
|
|
528
|
+
pending_root = await self.data_store.get_pending_root(store_id=store_id)
|
|
529
|
+
if (
|
|
530
|
+
pending_root is not None
|
|
531
|
+
and pending_root.generation == root.generation + 1
|
|
532
|
+
and pending_root.node_hash == expected_root_hash
|
|
533
|
+
and pending_root.status == Status.PENDING
|
|
534
|
+
):
|
|
535
|
+
await self.data_store.change_root_status(pending_root, Status.COMMITTED)
|
|
536
|
+
await self.data_store.build_ancestor_table_for_latest_root(store_id=store_id)
|
|
537
|
+
await self.data_store.clear_pending_roots(store_id=store_id)
|
|
538
|
+
|
|
539
|
+
async def fetch_and_validate(self, store_id: bytes32) -> None:
|
|
540
|
+
singleton_record: Optional[SingletonRecord] = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
541
|
+
if singleton_record is None:
|
|
542
|
+
self.log.info(f"Fetch data: No singleton record for {store_id}.")
|
|
543
|
+
return
|
|
544
|
+
if singleton_record.generation == uint32(0):
|
|
545
|
+
self.log.info(f"Fetch data: No data on chain for {store_id}.")
|
|
546
|
+
return
|
|
547
|
+
|
|
548
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
549
|
+
|
|
550
|
+
if not await self.data_store.store_id_exists(store_id=store_id):
|
|
551
|
+
await self.data_store.create_tree(store_id=store_id, status=Status.COMMITTED)
|
|
552
|
+
|
|
553
|
+
timestamp = int(time.time())
|
|
554
|
+
servers_info = await self.data_store.get_available_servers_for_store(store_id, timestamp)
|
|
555
|
+
# TODO: maybe append a random object to the whole DataLayer class?
|
|
556
|
+
random.shuffle(servers_info)
|
|
557
|
+
success = False
|
|
558
|
+
for server_info in servers_info:
|
|
559
|
+
url = server_info.url
|
|
560
|
+
|
|
561
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
562
|
+
if root.generation > singleton_record.generation:
|
|
563
|
+
self.log.info(
|
|
564
|
+
"Fetch data: local DL store is ahead of chain generation. "
|
|
565
|
+
f"Local root: {root}. Singleton: {singleton_record}"
|
|
566
|
+
)
|
|
567
|
+
break
|
|
568
|
+
if root.generation == singleton_record.generation:
|
|
569
|
+
self.log.info(f"Fetch data: wallet generation matching on-chain generation: {store_id}.")
|
|
570
|
+
break
|
|
571
|
+
|
|
572
|
+
self.log.info(
|
|
573
|
+
f"Downloading files {store_id}. "
|
|
574
|
+
f"Current wallet generation: {root.generation}. "
|
|
575
|
+
f"Target wallet generation: {singleton_record.generation}. "
|
|
576
|
+
f"Server used: {url}."
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
to_download = await self.wallet_rpc.dl_history(
|
|
580
|
+
launcher_id=store_id,
|
|
581
|
+
min_generation=uint32(root.generation + 1),
|
|
582
|
+
max_generation=singleton_record.generation,
|
|
583
|
+
)
|
|
584
|
+
try:
|
|
585
|
+
proxy_url = self.config.get("proxy_url", None)
|
|
586
|
+
success = await insert_from_delta_file(
|
|
587
|
+
self.data_store,
|
|
588
|
+
store_id,
|
|
589
|
+
root.generation,
|
|
590
|
+
target_generation=singleton_record.generation,
|
|
591
|
+
root_hashes=[record.root for record in reversed(to_download)],
|
|
592
|
+
server_info=server_info,
|
|
593
|
+
client_foldername=self.server_files_location,
|
|
594
|
+
timeout=self.client_timeout,
|
|
595
|
+
log=self.log,
|
|
596
|
+
proxy_url=proxy_url,
|
|
597
|
+
downloader=await self.get_downloader(store_id, url),
|
|
598
|
+
group_files_by_store=self.group_files_by_store,
|
|
599
|
+
maximum_full_file_count=self.maximum_full_file_count,
|
|
600
|
+
)
|
|
601
|
+
if success:
|
|
602
|
+
self.log.info(
|
|
603
|
+
f"Finished downloading and validating {store_id}. "
|
|
604
|
+
f"Wallet generation saved: {singleton_record.generation}. "
|
|
605
|
+
f"Root hash saved: {singleton_record.root}."
|
|
606
|
+
)
|
|
607
|
+
break
|
|
608
|
+
except aiohttp.client_exceptions.ClientConnectorError:
|
|
609
|
+
self.log.warning(f"Server {url} unavailable for {store_id}.")
|
|
610
|
+
except Exception as e:
|
|
611
|
+
self.log.warning(f"Exception while downloading files for {store_id}: {e} {traceback.format_exc()}.")
|
|
612
|
+
|
|
613
|
+
# if there aren't any servers then don't try to write the full tree
|
|
614
|
+
if not success and len(servers_info) > 0:
|
|
615
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
616
|
+
if root.node_hash is None:
|
|
617
|
+
return
|
|
618
|
+
filename_full_tree = get_full_tree_filename_path(
|
|
619
|
+
foldername=self.server_files_location,
|
|
620
|
+
store_id=store_id,
|
|
621
|
+
node_hash=root.node_hash,
|
|
622
|
+
generation=root.generation,
|
|
623
|
+
group_by_store=self.group_files_by_store,
|
|
624
|
+
)
|
|
625
|
+
# Had trouble with this generation, so generate full file for the generation we currently have
|
|
626
|
+
if not os.path.exists(filename_full_tree):
|
|
627
|
+
with open(filename_full_tree, "wb") as writer:
|
|
628
|
+
await self.data_store.write_tree_to_file(
|
|
629
|
+
root=root,
|
|
630
|
+
node_hash=root.node_hash,
|
|
631
|
+
store_id=store_id,
|
|
632
|
+
deltas_only=False,
|
|
633
|
+
writer=writer,
|
|
634
|
+
)
|
|
635
|
+
self.log.info(f"Successfully written full tree filename {filename_full_tree}.")
|
|
636
|
+
|
|
637
|
+
async def get_downloader(self, store_id: bytes32, url: str) -> Optional[PluginRemote]:
|
|
638
|
+
request_json = {"store_id": store_id.hex(), "url": url}
|
|
639
|
+
for d in self.downloaders:
|
|
640
|
+
async with aiohttp.ClientSession() as session:
|
|
641
|
+
try:
|
|
642
|
+
async with session.post(
|
|
643
|
+
d.url + "/handle_download",
|
|
644
|
+
json=request_json,
|
|
645
|
+
headers=d.headers,
|
|
646
|
+
) as response:
|
|
647
|
+
res_json = await response.json()
|
|
648
|
+
if res_json["handle_download"]:
|
|
649
|
+
return d
|
|
650
|
+
except Exception as e:
|
|
651
|
+
self.log.error(f"get_downloader could not get response: {type(e).__name__}: {e}")
|
|
652
|
+
return None
|
|
653
|
+
|
|
654
|
+
async def clean_old_full_tree_files(self, store_id: bytes32) -> None:
|
|
655
|
+
singleton_record: Optional[SingletonRecord] = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
656
|
+
if singleton_record is None:
|
|
657
|
+
return
|
|
658
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
659
|
+
|
|
660
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
661
|
+
latest_generation = root.generation
|
|
662
|
+
full_tree_first_publish_generation = max(0, latest_generation - self.maximum_full_file_count + 1)
|
|
663
|
+
foldername = self.server_files_location
|
|
664
|
+
|
|
665
|
+
for generation in range(full_tree_first_publish_generation - 1, 0, -1):
|
|
666
|
+
root = await self.data_store.get_tree_root(store_id=store_id, generation=generation)
|
|
667
|
+
file_exists = delete_full_file_if_exists(foldername, store_id, root)
|
|
668
|
+
if not file_exists:
|
|
669
|
+
break
|
|
670
|
+
|
|
671
|
+
async def upload_files(self, store_id: bytes32) -> None:
|
|
672
|
+
uploaders = await self.get_uploaders(store_id)
|
|
673
|
+
singleton_record: Optional[SingletonRecord] = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
674
|
+
if singleton_record is None:
|
|
675
|
+
self.log.info(f"Upload files: no on-chain record for {store_id}.")
|
|
676
|
+
return
|
|
677
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
678
|
+
|
|
679
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
680
|
+
latest_generation = root.generation
|
|
681
|
+
# Don't store full tree files before this generation.
|
|
682
|
+
full_tree_first_publish_generation = max(0, latest_generation - self.maximum_full_file_count + 1)
|
|
683
|
+
publish_generation = min(singleton_record.generation, 0 if root is None else root.generation)
|
|
684
|
+
# If we make some batch updates, which get confirmed to the chain, we need to create the files.
|
|
685
|
+
# We iterate back and write the missing files, until we find the files already written.
|
|
686
|
+
root = await self.data_store.get_tree_root(store_id=store_id, generation=publish_generation)
|
|
687
|
+
while publish_generation > 0:
|
|
688
|
+
write_file_result = await write_files_for_root(
|
|
689
|
+
self.data_store,
|
|
690
|
+
store_id,
|
|
691
|
+
root,
|
|
692
|
+
self.server_files_location,
|
|
693
|
+
full_tree_first_publish_generation,
|
|
694
|
+
group_by_store=self.group_files_by_store,
|
|
695
|
+
)
|
|
696
|
+
if not write_file_result.result:
|
|
697
|
+
# this particular return only happens if the files already exist, no need to log anything
|
|
698
|
+
break
|
|
699
|
+
try:
|
|
700
|
+
if uploaders is not None and len(uploaders) > 0:
|
|
701
|
+
request_json = {
|
|
702
|
+
"store_id": store_id.hex(),
|
|
703
|
+
"diff_filename": write_file_result.diff_tree.name,
|
|
704
|
+
"group_files_by_store": self.group_files_by_store,
|
|
705
|
+
}
|
|
706
|
+
if write_file_result.full_tree is not None:
|
|
707
|
+
request_json["full_tree_filename"] = write_file_result.full_tree.name
|
|
708
|
+
|
|
709
|
+
for uploader in uploaders:
|
|
710
|
+
self.log.info(f"Using uploader {uploader} for store {store_id.hex()}")
|
|
711
|
+
async with aiohttp.ClientSession() as session:
|
|
712
|
+
async with session.post(
|
|
713
|
+
uploader.url + "/upload",
|
|
714
|
+
json=request_json,
|
|
715
|
+
headers=uploader.headers,
|
|
716
|
+
) as response:
|
|
717
|
+
res_json = await response.json()
|
|
718
|
+
if res_json["uploaded"]:
|
|
719
|
+
self.log.info(
|
|
720
|
+
f"Uploaded files to {uploader} for store {store_id.hex()} "
|
|
721
|
+
f"generation {publish_generation}"
|
|
722
|
+
)
|
|
723
|
+
else:
|
|
724
|
+
self.log.error(
|
|
725
|
+
f"Failed to upload files to, will retry later: {uploader} : {res_json}"
|
|
726
|
+
)
|
|
727
|
+
except Exception as e:
|
|
728
|
+
self.log.error(f"Exception uploading files, will retry later: store id {store_id}")
|
|
729
|
+
self.log.debug(f"Failed to upload files, cleaning local files: {type(e).__name__}: {e}")
|
|
730
|
+
if write_file_result.full_tree is not None:
|
|
731
|
+
os.remove(write_file_result.full_tree)
|
|
732
|
+
os.remove(write_file_result.diff_tree)
|
|
733
|
+
publish_generation -= 1
|
|
734
|
+
root = await self.data_store.get_tree_root(store_id=store_id, generation=publish_generation)
|
|
735
|
+
|
|
736
|
+
async def add_missing_files(self, store_id: bytes32, overwrite: bool, foldername: Optional[Path]) -> None:
|
|
737
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
738
|
+
latest_generation = root.generation
|
|
739
|
+
full_tree_first_publish_generation = max(0, latest_generation - self.maximum_full_file_count + 1)
|
|
740
|
+
singleton_record: Optional[SingletonRecord] = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
741
|
+
if singleton_record is None:
|
|
742
|
+
self.log.error(f"No singleton record found for: {store_id}")
|
|
743
|
+
return
|
|
744
|
+
max_generation = min(singleton_record.generation, 0 if root is None else root.generation)
|
|
745
|
+
server_files_location = foldername if foldername is not None else self.server_files_location
|
|
746
|
+
files = []
|
|
747
|
+
for generation in range(1, max_generation + 1):
|
|
748
|
+
root = await self.data_store.get_tree_root(store_id=store_id, generation=generation)
|
|
749
|
+
res = await write_files_for_root(
|
|
750
|
+
self.data_store,
|
|
751
|
+
store_id,
|
|
752
|
+
root,
|
|
753
|
+
server_files_location,
|
|
754
|
+
full_tree_first_publish_generation,
|
|
755
|
+
overwrite,
|
|
756
|
+
self.group_files_by_store,
|
|
757
|
+
)
|
|
758
|
+
files.append(res.diff_tree.name)
|
|
759
|
+
if res.full_tree is not None:
|
|
760
|
+
files.append(res.full_tree.name)
|
|
761
|
+
|
|
762
|
+
uploaders = await self.get_uploaders(store_id)
|
|
763
|
+
if uploaders is not None and len(uploaders) > 0:
|
|
764
|
+
request_json = {
|
|
765
|
+
"store_id": store_id.hex(),
|
|
766
|
+
"files": json.dumps(files),
|
|
767
|
+
"group_files_by_store": self.group_files_by_store,
|
|
768
|
+
}
|
|
769
|
+
for uploader in uploaders:
|
|
770
|
+
async with aiohttp.ClientSession() as session:
|
|
771
|
+
async with session.post(
|
|
772
|
+
uploader.url + "/add_missing_files",
|
|
773
|
+
json=request_json,
|
|
774
|
+
headers=uploader.headers,
|
|
775
|
+
) as response:
|
|
776
|
+
res_json = await response.json()
|
|
777
|
+
if not res_json["uploaded"]:
|
|
778
|
+
self.log.error(f"failed to upload to uploader {uploader}")
|
|
779
|
+
else:
|
|
780
|
+
self.log.debug(f"uploaded to uploader {uploader}")
|
|
781
|
+
|
|
782
|
+
async def subscribe(self, store_id: bytes32, urls: list[str]) -> Subscription:
|
|
783
|
+
parsed_urls = [url.rstrip("/") for url in urls]
|
|
784
|
+
subscription = Subscription(store_id, [ServerInfo(url, 0, 0) for url in parsed_urls])
|
|
785
|
+
await self.wallet_rpc.dl_track_new(subscription.store_id)
|
|
786
|
+
async with self.subscription_lock:
|
|
787
|
+
await self.data_store.subscribe(subscription)
|
|
788
|
+
self.log.info(f"Done adding subscription: {subscription.store_id}")
|
|
789
|
+
return subscription
|
|
790
|
+
|
|
791
|
+
async def remove_subscriptions(self, store_id: bytes32, urls: list[str]) -> None:
|
|
792
|
+
parsed_urls = [url.rstrip("/") for url in urls]
|
|
793
|
+
async with self.subscription_lock:
|
|
794
|
+
await self.data_store.remove_subscriptions(store_id, parsed_urls)
|
|
795
|
+
|
|
796
|
+
async def unsubscribe(self, store_id: bytes32, retain_data: bool) -> None:
|
|
797
|
+
async with self.subscription_lock:
|
|
798
|
+
subscriptions = await self.data_store.get_subscriptions()
|
|
799
|
+
if store_id not in (subscription.store_id for subscription in subscriptions):
|
|
800
|
+
raise RuntimeError("No subscription found for the given store_id.")
|
|
801
|
+
|
|
802
|
+
# Unsubscribe is processed later, after all fetching of data is done, to avoid races.
|
|
803
|
+
self.unsubscribe_data_queue.append(UnsubscribeData(store_id, retain_data))
|
|
804
|
+
|
|
805
|
+
async def process_unsubscribe(self, store_id: bytes32, retain_data: bool) -> None:
|
|
806
|
+
# This function already acquired `subscriptions_lock`.
|
|
807
|
+
subscriptions = await self.data_store.get_subscriptions()
|
|
808
|
+
if store_id not in (subscription.store_id for subscription in subscriptions):
|
|
809
|
+
raise RuntimeError("No subscription found for the given store_id.")
|
|
810
|
+
paths: list[Path] = []
|
|
811
|
+
if await self.data_store.store_id_exists(store_id) and not retain_data:
|
|
812
|
+
generation = await self.data_store.get_tree_generation(store_id)
|
|
813
|
+
all_roots = await self.data_store.get_roots_between(store_id, 1, generation + 1)
|
|
814
|
+
for root in all_roots:
|
|
815
|
+
root_hash = root.node_hash if root.node_hash is not None else self.none_bytes
|
|
816
|
+
for group_by_store in (True, False):
|
|
817
|
+
paths.append(
|
|
818
|
+
get_full_tree_filename_path(
|
|
819
|
+
self.server_files_location,
|
|
820
|
+
store_id,
|
|
821
|
+
root_hash,
|
|
822
|
+
root.generation,
|
|
823
|
+
group_by_store,
|
|
824
|
+
)
|
|
825
|
+
)
|
|
826
|
+
paths.append(
|
|
827
|
+
get_delta_filename_path(
|
|
828
|
+
self.server_files_location,
|
|
829
|
+
store_id,
|
|
830
|
+
root_hash,
|
|
831
|
+
root.generation,
|
|
832
|
+
group_by_store,
|
|
833
|
+
)
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
# stop tracking first, then unsubscribe from the data store
|
|
837
|
+
await self.wallet_rpc.dl_stop_tracking(store_id)
|
|
838
|
+
await self.data_store.unsubscribe(store_id)
|
|
839
|
+
if not retain_data:
|
|
840
|
+
await self.data_store.delete_store_data(store_id)
|
|
841
|
+
|
|
842
|
+
self.log.info(f"Unsubscribed to {store_id}")
|
|
843
|
+
for file_path in paths:
|
|
844
|
+
try:
|
|
845
|
+
file_path.unlink()
|
|
846
|
+
except FileNotFoundError:
|
|
847
|
+
pass
|
|
848
|
+
|
|
849
|
+
async def get_subscriptions(self) -> list[Subscription]:
|
|
850
|
+
async with self.subscription_lock:
|
|
851
|
+
return await self.data_store.get_subscriptions()
|
|
852
|
+
|
|
853
|
+
async def add_mirror(self, store_id: bytes32, urls: list[str], amount: uint64, fee: uint64) -> None:
|
|
854
|
+
if not urls:
|
|
855
|
+
raise RuntimeError("URL list can't be empty")
|
|
856
|
+
bytes_urls = [bytes(url, "utf8") for url in urls]
|
|
857
|
+
await self.wallet_rpc.dl_new_mirror(store_id, amount, bytes_urls, fee)
|
|
858
|
+
|
|
859
|
+
async def delete_mirror(self, coin_id: bytes32, fee: uint64) -> None:
|
|
860
|
+
await self.wallet_rpc.dl_delete_mirror(coin_id, fee)
|
|
861
|
+
|
|
862
|
+
async def get_mirrors(self, store_id: bytes32) -> list[Mirror]:
|
|
863
|
+
mirrors: list[Mirror] = await self.wallet_rpc.dl_get_mirrors(store_id)
|
|
864
|
+
return [mirror for mirror in mirrors if mirror.urls]
|
|
865
|
+
|
|
866
|
+
async def update_subscriptions_from_wallet(self, store_id: bytes32) -> None:
|
|
867
|
+
mirrors: list[Mirror] = await self.wallet_rpc.dl_get_mirrors(store_id)
|
|
868
|
+
urls: list[str] = []
|
|
869
|
+
for mirror in mirrors:
|
|
870
|
+
urls += [url.decode("utf8") for url in mirror.urls]
|
|
871
|
+
urls = [url.rstrip("/") for url in urls]
|
|
872
|
+
await self.data_store.update_subscriptions_from_wallet(store_id, urls)
|
|
873
|
+
|
|
874
|
+
async def get_owned_stores(self) -> list[SingletonRecord]:
|
|
875
|
+
return await self.wallet_rpc.dl_owned_singletons()
|
|
876
|
+
|
|
877
|
+
async def get_kv_diff(self, store_id: bytes32, hash_1: bytes32, hash_2: bytes32) -> set[DiffData]:
|
|
878
|
+
return await self.data_store.get_kv_diff(store_id, hash_1, hash_2)
|
|
879
|
+
|
|
880
|
+
async def get_kv_diff_paginated(
|
|
881
|
+
self,
|
|
882
|
+
store_id: bytes32,
|
|
883
|
+
# NOTE: empty is expressed as zeros
|
|
884
|
+
hash_1: bytes32,
|
|
885
|
+
hash_2: bytes32,
|
|
886
|
+
page: int,
|
|
887
|
+
max_page_size: Optional[int] = None,
|
|
888
|
+
) -> KVDiffPaginationData:
|
|
889
|
+
if max_page_size is None:
|
|
890
|
+
max_page_size = 40 * 1024 * 1024
|
|
891
|
+
return await self.data_store.get_kv_diff_paginated(store_id, page, max_page_size, hash_1, hash_2)
|
|
892
|
+
|
|
893
|
+
async def periodically_manage_data(self) -> None:
|
|
894
|
+
manage_data_interval = self.config.get("manage_data_interval", 60)
|
|
895
|
+
while not self._shut_down:
|
|
896
|
+
async with self.subscription_lock:
|
|
897
|
+
try:
|
|
898
|
+
subscriptions = await self.data_store.get_subscriptions()
|
|
899
|
+
for subscription in subscriptions:
|
|
900
|
+
await self.wallet_rpc.dl_track_new(subscription.store_id)
|
|
901
|
+
break
|
|
902
|
+
except aiohttp.client_exceptions.ClientConnectorError:
|
|
903
|
+
pass
|
|
904
|
+
except Exception as e:
|
|
905
|
+
self.log.error(f"Exception while requesting wallet track subscription: {type(e)} {e}")
|
|
906
|
+
|
|
907
|
+
self.log.warning("Cannot connect to the wallet. Retrying in 3s.")
|
|
908
|
+
|
|
909
|
+
delay_until = time.monotonic() + 3
|
|
910
|
+
while time.monotonic() < delay_until:
|
|
911
|
+
if self._shut_down:
|
|
912
|
+
break
|
|
913
|
+
await asyncio.sleep(0.1)
|
|
914
|
+
|
|
915
|
+
while not self._shut_down:
|
|
916
|
+
# Add existing subscriptions
|
|
917
|
+
async with self.subscription_lock:
|
|
918
|
+
subscriptions = await self.data_store.get_subscriptions()
|
|
919
|
+
|
|
920
|
+
# pseudo-subscribe to all unsubscribed owned stores
|
|
921
|
+
# Need this to make sure we process updates and generate DAT files
|
|
922
|
+
try:
|
|
923
|
+
owned_stores = await self.get_owned_stores()
|
|
924
|
+
except (ValueError, aiohttp.client_exceptions.ClientConnectorError):
|
|
925
|
+
# Sometimes the DL wallet isn't available, so we can't get the owned stores.
|
|
926
|
+
# We'll try again next time.
|
|
927
|
+
owned_stores = []
|
|
928
|
+
except Exception as e:
|
|
929
|
+
self.log.error(f"Exception while fetching owned stores: {type(e)} {e} {traceback.format_exc()}")
|
|
930
|
+
owned_stores = []
|
|
931
|
+
|
|
932
|
+
subscription_store_ids = {subscription.store_id for subscription in subscriptions}
|
|
933
|
+
for record in owned_stores:
|
|
934
|
+
store_id = record.launcher_id
|
|
935
|
+
if store_id not in subscription_store_ids:
|
|
936
|
+
try:
|
|
937
|
+
# don't actually subscribe, just add to the list
|
|
938
|
+
subscriptions.insert(0, Subscription(store_id=store_id, servers_info=[]))
|
|
939
|
+
except Exception as e:
|
|
940
|
+
self.log.info(
|
|
941
|
+
f"Can't subscribe to owned store {store_id}: {type(e)} {e} {traceback.format_exc()}"
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
# Optionally
|
|
945
|
+
# Subscribe to all local non-owned store_ids that we can find on chain.
|
|
946
|
+
# This is the prior behavior where all local stores, both owned and not owned, are subscribed to.
|
|
947
|
+
if self.config.get("auto_subscribe_to_local_stores", False):
|
|
948
|
+
local_store_ids = await self.data_store.get_store_ids()
|
|
949
|
+
subscription_store_ids = {subscription.store_id for subscription in subscriptions}
|
|
950
|
+
for local_id in local_store_ids:
|
|
951
|
+
if local_id not in subscription_store_ids:
|
|
952
|
+
try:
|
|
953
|
+
subscription = await self.subscribe(local_id, [])
|
|
954
|
+
subscriptions.insert(0, subscription)
|
|
955
|
+
except Exception as e:
|
|
956
|
+
self.log.info(
|
|
957
|
+
f"Can't subscribe to local store {local_id}: {type(e)} {e} {traceback.format_exc()}"
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
work_queue: asyncio.Queue[Job[Subscription]] = asyncio.Queue()
|
|
961
|
+
async with QueuedAsyncPool.managed(
|
|
962
|
+
name="DataLayer subscription update pool",
|
|
963
|
+
worker_async_callable=self.update_subscription,
|
|
964
|
+
job_queue=work_queue,
|
|
965
|
+
target_worker_count=self.subscription_update_concurrency,
|
|
966
|
+
log=self.log,
|
|
967
|
+
):
|
|
968
|
+
jobs = [Job(input=subscription) for subscription in subscriptions]
|
|
969
|
+
for job in jobs:
|
|
970
|
+
await work_queue.put(job)
|
|
971
|
+
|
|
972
|
+
await asyncio.gather(*(job.done.wait() for job in jobs), return_exceptions=True)
|
|
973
|
+
|
|
974
|
+
# Do unsubscribes after the fetching of data is complete, to avoid races.
|
|
975
|
+
async with self.subscription_lock:
|
|
976
|
+
for unsubscribe_data in self.unsubscribe_data_queue:
|
|
977
|
+
await self.process_unsubscribe(unsubscribe_data.store_id, unsubscribe_data.retain_data)
|
|
978
|
+
self.unsubscribe_data_queue.clear()
|
|
979
|
+
await asyncio.sleep(manage_data_interval)
|
|
980
|
+
|
|
981
|
+
async def update_subscription(
|
|
982
|
+
self,
|
|
983
|
+
worker_id: int,
|
|
984
|
+
job: Job[Subscription],
|
|
985
|
+
) -> None:
|
|
986
|
+
subscription = job.input
|
|
987
|
+
|
|
988
|
+
try:
|
|
989
|
+
await self.update_subscriptions_from_wallet(subscription.store_id)
|
|
990
|
+
await self.fetch_and_validate(subscription.store_id)
|
|
991
|
+
await self.upload_files(subscription.store_id)
|
|
992
|
+
await self.clean_old_full_tree_files(subscription.store_id)
|
|
993
|
+
except Exception as e:
|
|
994
|
+
self.log.error(f"Exception while fetching data: {type(e)} {e} {traceback.format_exc()}.")
|
|
995
|
+
|
|
996
|
+
async def build_offer_changelist(
|
|
997
|
+
self,
|
|
998
|
+
store_id: bytes32,
|
|
999
|
+
inclusions: tuple[KeyValue, ...],
|
|
1000
|
+
) -> list[dict[str, Any]]:
|
|
1001
|
+
async with self.data_store.transaction():
|
|
1002
|
+
changelist: list[dict[str, Any]] = []
|
|
1003
|
+
for entry in inclusions:
|
|
1004
|
+
try:
|
|
1005
|
+
existing_value = await self.get_value(store_id=store_id, key=entry.key)
|
|
1006
|
+
except KeyNotFoundError:
|
|
1007
|
+
existing_value = None
|
|
1008
|
+
|
|
1009
|
+
if existing_value == entry.value:
|
|
1010
|
+
# already present, nothing needed
|
|
1011
|
+
continue
|
|
1012
|
+
|
|
1013
|
+
if existing_value is not None:
|
|
1014
|
+
# upsert, delete the existing key and value
|
|
1015
|
+
changelist.append(
|
|
1016
|
+
{
|
|
1017
|
+
"action": "delete",
|
|
1018
|
+
"key": entry.key,
|
|
1019
|
+
}
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
changelist.append(
|
|
1023
|
+
{
|
|
1024
|
+
"action": "insert",
|
|
1025
|
+
"key": entry.key,
|
|
1026
|
+
"value": entry.value,
|
|
1027
|
+
}
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
return changelist
|
|
1031
|
+
|
|
1032
|
+
async def process_offered_stores(self, offer_stores: tuple[OfferStore, ...]) -> dict[bytes32, StoreProofs]:
|
|
1033
|
+
for offer_store in offer_stores:
|
|
1034
|
+
await self._update_confirmation_status(store_id=offer_store.store_id)
|
|
1035
|
+
|
|
1036
|
+
async with self.data_store.transaction():
|
|
1037
|
+
our_store_proofs: dict[bytes32, StoreProofs] = {}
|
|
1038
|
+
for offer_store in offer_stores:
|
|
1039
|
+
changelist = await self.build_offer_changelist(
|
|
1040
|
+
store_id=offer_store.store_id,
|
|
1041
|
+
inclusions=offer_store.inclusions,
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
if len(changelist) > 0:
|
|
1045
|
+
new_root_hash = await self.batch_insert(
|
|
1046
|
+
store_id=offer_store.store_id,
|
|
1047
|
+
changelist=changelist,
|
|
1048
|
+
enable_batch_autoinsert=False,
|
|
1049
|
+
)
|
|
1050
|
+
else:
|
|
1051
|
+
existing_root = await self.get_root(store_id=offer_store.store_id)
|
|
1052
|
+
if existing_root is None:
|
|
1053
|
+
raise Exception(f"store id not available: {offer_store.store_id.hex()}")
|
|
1054
|
+
new_root_hash = existing_root.root
|
|
1055
|
+
|
|
1056
|
+
if new_root_hash is None:
|
|
1057
|
+
raise Exception("only inserts are supported so a None root hash should not be possible")
|
|
1058
|
+
|
|
1059
|
+
proofs: list[Proof] = []
|
|
1060
|
+
for entry in offer_store.inclusions:
|
|
1061
|
+
node_hash = await self.get_key_value_hash(
|
|
1062
|
+
store_id=offer_store.store_id,
|
|
1063
|
+
key=entry.key,
|
|
1064
|
+
root_hash=new_root_hash,
|
|
1065
|
+
)
|
|
1066
|
+
proof_of_inclusion = await self.data_store.get_proof_of_inclusion_by_hash(
|
|
1067
|
+
node_hash=node_hash,
|
|
1068
|
+
store_id=offer_store.store_id,
|
|
1069
|
+
root_hash=new_root_hash,
|
|
1070
|
+
)
|
|
1071
|
+
proof = Proof(
|
|
1072
|
+
key=entry.key,
|
|
1073
|
+
value=entry.value,
|
|
1074
|
+
node_hash=proof_of_inclusion.node_hash,
|
|
1075
|
+
layers=tuple(
|
|
1076
|
+
Layer(
|
|
1077
|
+
other_hash_side=layer.other_hash_side,
|
|
1078
|
+
other_hash=layer.other_hash,
|
|
1079
|
+
combined_hash=layer.combined_hash,
|
|
1080
|
+
)
|
|
1081
|
+
for layer in proof_of_inclusion.layers
|
|
1082
|
+
),
|
|
1083
|
+
)
|
|
1084
|
+
proofs.append(proof)
|
|
1085
|
+
store_proof = StoreProofs(store_id=offer_store.store_id, proofs=tuple(proofs))
|
|
1086
|
+
our_store_proofs[offer_store.store_id] = store_proof
|
|
1087
|
+
return our_store_proofs
|
|
1088
|
+
|
|
1089
|
+
async def make_offer(
|
|
1090
|
+
self,
|
|
1091
|
+
maker: tuple[OfferStore, ...],
|
|
1092
|
+
taker: tuple[OfferStore, ...],
|
|
1093
|
+
fee: uint64,
|
|
1094
|
+
) -> Offer:
|
|
1095
|
+
async with self.data_store.transaction():
|
|
1096
|
+
our_store_proofs = await self.process_offered_stores(offer_stores=maker)
|
|
1097
|
+
|
|
1098
|
+
offer_dict: dict[Union[uint32, str], int] = {
|
|
1099
|
+
**{offer_store.store_id.hex(): -1 for offer_store in maker},
|
|
1100
|
+
**{offer_store.store_id.hex(): 1 for offer_store in taker},
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
solver: dict[str, Any] = {
|
|
1104
|
+
"0x" + our_offer_store.store_id.hex(): {
|
|
1105
|
+
"new_root": "0x" + our_store_proofs[our_offer_store.store_id].proofs[0].root().hex(),
|
|
1106
|
+
"dependencies": [
|
|
1107
|
+
{
|
|
1108
|
+
"launcher_id": "0x" + their_offer_store.store_id.hex(),
|
|
1109
|
+
"values_to_prove": [
|
|
1110
|
+
"0x" + leaf_hash(key=entry.key, value=entry.value).hex()
|
|
1111
|
+
for entry in their_offer_store.inclusions
|
|
1112
|
+
],
|
|
1113
|
+
}
|
|
1114
|
+
for their_offer_store in taker
|
|
1115
|
+
],
|
|
1116
|
+
}
|
|
1117
|
+
for our_offer_store in maker
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
res = await self.wallet_rpc.create_offer_for_ids(
|
|
1121
|
+
offer_dict=offer_dict,
|
|
1122
|
+
solver=solver,
|
|
1123
|
+
driver_dict={},
|
|
1124
|
+
fee=fee,
|
|
1125
|
+
validate_only=False,
|
|
1126
|
+
# TODO: probably shouldn't be default but due to peculiarities in the RPC, we're using a stop gap.
|
|
1127
|
+
# This is not a change in behavior, the default was already implicit.
|
|
1128
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
offer = Offer(
|
|
1132
|
+
trade_id=res.trade_record.trade_id,
|
|
1133
|
+
offer=bytes(res.offer),
|
|
1134
|
+
taker=taker,
|
|
1135
|
+
maker=tuple(our_store_proofs.values()),
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
# being extra careful and verifying the offer before returning it
|
|
1139
|
+
trading_offer = TradingOffer.from_bytes(offer.offer)
|
|
1140
|
+
summary = await DataLayerWallet.get_offer_summary(offer=trading_offer)
|
|
1141
|
+
|
|
1142
|
+
verify_offer(maker=offer.maker, taker=offer.taker, summary=summary)
|
|
1143
|
+
|
|
1144
|
+
await self.data_store.clean_node_table()
|
|
1145
|
+
return offer
|
|
1146
|
+
|
|
1147
|
+
async def take_offer(
|
|
1148
|
+
self,
|
|
1149
|
+
offer_bytes: bytes,
|
|
1150
|
+
taker: tuple[OfferStore, ...],
|
|
1151
|
+
maker: tuple[StoreProofs, ...],
|
|
1152
|
+
fee: uint64,
|
|
1153
|
+
) -> TradeRecord:
|
|
1154
|
+
async with self.data_store.transaction():
|
|
1155
|
+
our_store_proofs = await self.process_offered_stores(offer_stores=taker)
|
|
1156
|
+
|
|
1157
|
+
offer = TradingOffer.from_bytes(offer_bytes)
|
|
1158
|
+
summary = await DataLayerWallet.get_offer_summary(offer=offer)
|
|
1159
|
+
|
|
1160
|
+
verify_offer(maker=maker, taker=taker, summary=summary)
|
|
1161
|
+
|
|
1162
|
+
all_store_proofs: dict[bytes32, StoreProofs] = {
|
|
1163
|
+
store_proofs.proofs[0].root(): store_proofs for store_proofs in [*maker, *our_store_proofs.values()]
|
|
1164
|
+
}
|
|
1165
|
+
proofs_of_inclusion: list[tuple[str, str, list[str]]] = []
|
|
1166
|
+
for root, store_proofs in all_store_proofs.items():
|
|
1167
|
+
for proof in store_proofs.proofs:
|
|
1168
|
+
layers = [
|
|
1169
|
+
ProofOfInclusionLayer(
|
|
1170
|
+
combined_hash=layer.combined_hash,
|
|
1171
|
+
other_hash_side=layer.other_hash_side,
|
|
1172
|
+
other_hash=layer.other_hash,
|
|
1173
|
+
)
|
|
1174
|
+
for layer in proof.layers
|
|
1175
|
+
]
|
|
1176
|
+
proof_of_inclusion = ProofOfInclusion(node_hash=proof.node_hash, layers=layers)
|
|
1177
|
+
sibling_sides_integer = proof_of_inclusion.sibling_sides_integer()
|
|
1178
|
+
proofs_of_inclusion.append(
|
|
1179
|
+
(
|
|
1180
|
+
root.hex(),
|
|
1181
|
+
str(sibling_sides_integer),
|
|
1182
|
+
["0x" + sibling_hash.hex() for sibling_hash in proof_of_inclusion.sibling_hashes()],
|
|
1183
|
+
)
|
|
1184
|
+
)
|
|
1185
|
+
|
|
1186
|
+
solver: dict[str, Any] = {
|
|
1187
|
+
"proofs_of_inclusion": proofs_of_inclusion,
|
|
1188
|
+
**{
|
|
1189
|
+
"0x" + our_offer_store.store_id.hex(): {
|
|
1190
|
+
"new_root": "0x" + root.hex(),
|
|
1191
|
+
"dependencies": [
|
|
1192
|
+
{
|
|
1193
|
+
"launcher_id": "0x" + their_offer_store.store_id.hex(),
|
|
1194
|
+
"values_to_prove": ["0x" + entry.node_hash.hex() for entry in their_offer_store.proofs],
|
|
1195
|
+
}
|
|
1196
|
+
for their_offer_store in maker
|
|
1197
|
+
],
|
|
1198
|
+
}
|
|
1199
|
+
for our_offer_store in taker
|
|
1200
|
+
},
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
await self.data_store.clean_node_table()
|
|
1204
|
+
|
|
1205
|
+
# Excluding wallet from transaction since failures in the wallet may occur
|
|
1206
|
+
# after the transaction is submitted to the chain. If we roll back data we
|
|
1207
|
+
# may lose published data.
|
|
1208
|
+
|
|
1209
|
+
trade_record = (
|
|
1210
|
+
await self.wallet_rpc.take_offer(
|
|
1211
|
+
offer=offer,
|
|
1212
|
+
solver=solver,
|
|
1213
|
+
fee=fee,
|
|
1214
|
+
# TODO: probably shouldn't be default but due to peculiarities in the RPC, we're using a stop gap.
|
|
1215
|
+
# This is not a change in behavior, the default was already implicit.
|
|
1216
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
1217
|
+
)
|
|
1218
|
+
).trade_record
|
|
1219
|
+
|
|
1220
|
+
return trade_record
|
|
1221
|
+
|
|
1222
|
+
async def cancel_offer(self, trade_id: bytes32, secure: bool, fee: uint64) -> None:
|
|
1223
|
+
store_ids: list[bytes32] = []
|
|
1224
|
+
|
|
1225
|
+
if not secure:
|
|
1226
|
+
trade_record = await self.wallet_rpc.get_offer(trade_id=trade_id, file_contents=True)
|
|
1227
|
+
trading_offer = TradingOffer.from_bytes(trade_record.offer)
|
|
1228
|
+
summary = await DataLayerWallet.get_offer_summary(offer=trading_offer)
|
|
1229
|
+
store_ids = [bytes32.from_hexstr(offered["launcher_id"]) for offered in summary["offered"]]
|
|
1230
|
+
|
|
1231
|
+
await self.wallet_rpc.cancel_offer(
|
|
1232
|
+
trade_id=trade_id,
|
|
1233
|
+
secure=secure,
|
|
1234
|
+
fee=fee,
|
|
1235
|
+
# TODO: probably shouldn't be default but due to peculiarities in the RPC, we're using a stop gap.
|
|
1236
|
+
# This is not a change in behavior, the default was already implicit.
|
|
1237
|
+
tx_config=DEFAULT_TX_CONFIG,
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
if not secure:
|
|
1241
|
+
for store_id in store_ids:
|
|
1242
|
+
await self.data_store.clear_pending_roots(store_id=store_id)
|
|
1243
|
+
|
|
1244
|
+
async def get_sync_status(self, store_id: bytes32) -> SyncStatus:
|
|
1245
|
+
await self._update_confirmation_status(store_id=store_id)
|
|
1246
|
+
|
|
1247
|
+
if not await self.data_store.store_id_exists(store_id=store_id):
|
|
1248
|
+
raise Exception(f"No store id stored in the local database for {store_id}")
|
|
1249
|
+
root = await self.data_store.get_tree_root(store_id=store_id)
|
|
1250
|
+
singleton_record = await self.wallet_rpc.dl_latest_singleton(store_id, True)
|
|
1251
|
+
if singleton_record is None:
|
|
1252
|
+
raise Exception(f"No singleton found for {store_id}")
|
|
1253
|
+
|
|
1254
|
+
return SyncStatus(
|
|
1255
|
+
root_hash=self.none_bytes if root.node_hash is None else root.node_hash,
|
|
1256
|
+
generation=root.generation,
|
|
1257
|
+
target_root_hash=singleton_record.root,
|
|
1258
|
+
target_generation=singleton_record.generation,
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1261
|
+
async def get_uploaders(self, store_id: bytes32) -> list[PluginRemote]:
|
|
1262
|
+
uploaders = []
|
|
1263
|
+
for uploader in self.uploaders:
|
|
1264
|
+
async with aiohttp.ClientSession() as session:
|
|
1265
|
+
try:
|
|
1266
|
+
async with session.post(
|
|
1267
|
+
uploader.url + "/handle_upload",
|
|
1268
|
+
json={"store_id": store_id.hex()},
|
|
1269
|
+
headers=uploader.headers,
|
|
1270
|
+
) as response:
|
|
1271
|
+
res_json = await response.json()
|
|
1272
|
+
if res_json["handle_upload"]:
|
|
1273
|
+
uploaders.append(uploader)
|
|
1274
|
+
except Exception as e:
|
|
1275
|
+
self.log.error(f"get_uploader could not get response {e}")
|
|
1276
|
+
return uploaders
|
|
1277
|
+
|
|
1278
|
+
async def check_plugins(self) -> PluginStatus:
|
|
1279
|
+
coros = [get_plugin_info(plugin_remote=plugin) for plugin in {*self.uploaders, *self.downloaders}]
|
|
1280
|
+
results = dict(await asyncio.gather(*coros))
|
|
1281
|
+
|
|
1282
|
+
unknown = {
|
|
1283
|
+
"name": "unknown",
|
|
1284
|
+
"version": "unknown",
|
|
1285
|
+
"instance": "unknown",
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
uploader_status = {uploader.url: results.get(uploader, unknown) for uploader in self.uploaders}
|
|
1289
|
+
downloader_status = {downloader.url: results.get(downloader, unknown) for downloader in self.downloaders}
|
|
1290
|
+
|
|
1291
|
+
return PluginStatus(uploaders=uploader_status, downloaders=downloader_status)
|