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
chia/daemon/server.py
ADDED
|
@@ -0,0 +1,1606 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import dataclasses
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import signal
|
|
10
|
+
import ssl
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import traceback
|
|
14
|
+
import uuid
|
|
15
|
+
from collections.abc import AsyncIterator
|
|
16
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
17
|
+
from contextlib import asynccontextmanager
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from types import FrameType
|
|
21
|
+
from typing import Any, Optional, TextIO
|
|
22
|
+
|
|
23
|
+
from chia_rs import G1Element
|
|
24
|
+
from typing_extensions import Protocol
|
|
25
|
+
|
|
26
|
+
from chia import __version__
|
|
27
|
+
from chia.cmds.init_funcs import check_keys, chia_init
|
|
28
|
+
from chia.cmds.passphrase_funcs import default_passphrase, using_default_passphrase
|
|
29
|
+
from chia.consensus.coinbase import create_puzzlehash_for_pk
|
|
30
|
+
from chia.daemon.keychain_server import KeychainServer, keychain_commands
|
|
31
|
+
from chia.daemon.windows_signal import kill
|
|
32
|
+
from chia.plotters.plotters import get_available_plotters
|
|
33
|
+
from chia.plotting.util import add_plot_directory
|
|
34
|
+
from chia.server.server import ssl_context_for_server
|
|
35
|
+
from chia.server.signal_handlers import SignalHandlers
|
|
36
|
+
from chia.util.bech32m import encode_puzzle_hash
|
|
37
|
+
from chia.util.chia_logging import initialize_service_logging
|
|
38
|
+
from chia.util.chia_version import chia_short_version
|
|
39
|
+
from chia.util.config import load_config
|
|
40
|
+
from chia.util.errors import KeychainCurrentPassphraseIsInvalid
|
|
41
|
+
from chia.util.ints import uint32
|
|
42
|
+
from chia.util.json_util import dict_to_json_str
|
|
43
|
+
from chia.util.keychain import Keychain, KeyData, passphrase_requirements, supports_os_passphrase_storage
|
|
44
|
+
from chia.util.lock import Lockfile, LockfileError
|
|
45
|
+
from chia.util.log_exceptions import log_exceptions
|
|
46
|
+
from chia.util.network import WebServer
|
|
47
|
+
from chia.util.safe_cancel_task import cancel_task_safe
|
|
48
|
+
from chia.util.service_groups import validate_service
|
|
49
|
+
from chia.util.setproctitle import setproctitle
|
|
50
|
+
from chia.util.task_referencer import create_referenced_task
|
|
51
|
+
from chia.util.ws_message import WsRpcMessage, create_payload, format_response
|
|
52
|
+
from chia.wallet.derive_keys import (
|
|
53
|
+
master_pk_to_wallet_pk_unhardened,
|
|
54
|
+
master_sk_to_farmer_sk,
|
|
55
|
+
master_sk_to_pool_sk,
|
|
56
|
+
master_sk_to_wallet_sk,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
io_pool_exc = ThreadPoolExecutor()
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
from aiohttp import WSMsgType, web
|
|
63
|
+
from aiohttp.web_ws import WebSocketResponse
|
|
64
|
+
except ModuleNotFoundError:
|
|
65
|
+
print("Error: Make sure to run . ./activate from the project folder before starting Chia.")
|
|
66
|
+
sys.exit()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
log = logging.getLogger(__name__)
|
|
70
|
+
|
|
71
|
+
service_plotter = "chia_plotter"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class PlotState(str, Enum):
|
|
75
|
+
SUBMITTED = "SUBMITTED"
|
|
76
|
+
RUNNING = "RUNNING"
|
|
77
|
+
REMOVING = "REMOVING"
|
|
78
|
+
FINISHED = "FINISHED"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class PlotEvent(str, Enum):
|
|
82
|
+
LOG_CHANGED = "log_changed"
|
|
83
|
+
STATE_CHANGED = "state_changed"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# determine if application is a script file or frozen exe
|
|
87
|
+
if getattr(sys, "frozen", False):
|
|
88
|
+
name_map = {
|
|
89
|
+
"chia": "chia",
|
|
90
|
+
"chia_data_layer": "start_data_layer",
|
|
91
|
+
"chia_data_layer_http": "start_data_layer_http",
|
|
92
|
+
"chia_wallet": "start_wallet",
|
|
93
|
+
"chia_full_node": "start_full_node",
|
|
94
|
+
"chia_harvester": "start_harvester",
|
|
95
|
+
"chia_farmer": "start_farmer",
|
|
96
|
+
"chia_introducer": "start_introducer",
|
|
97
|
+
"chia_timelord": "start_timelord",
|
|
98
|
+
"chia_timelord_launcher": "timelord_launcher",
|
|
99
|
+
"chia_full_node_simulator": "start_simulator",
|
|
100
|
+
"chia_seeder": "start_seeder",
|
|
101
|
+
"chia_crawler": "start_crawler",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
def executable_for_service(service_name: str) -> str:
|
|
105
|
+
application_path = os.path.dirname(sys.executable)
|
|
106
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
107
|
+
executable = name_map[service_name]
|
|
108
|
+
path = f"{application_path}/{executable}.exe"
|
|
109
|
+
return path
|
|
110
|
+
else:
|
|
111
|
+
path = f"{application_path}/{name_map[service_name]}"
|
|
112
|
+
return path
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
application_path = os.path.dirname(__file__)
|
|
116
|
+
|
|
117
|
+
def executable_for_service(service_name: str) -> str:
|
|
118
|
+
cmd_to_exec = shutil.which(service_name)
|
|
119
|
+
return cmd_to_exec if cmd_to_exec is not None else service_name
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def ping() -> dict[str, Any]:
|
|
123
|
+
response = {"success": True, "value": "pong"}
|
|
124
|
+
return response
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Command(Protocol):
|
|
128
|
+
async def __call__(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]: ...
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _get_keys_by_fingerprints(fingerprints: Optional[list[uint32]]) -> tuple[list[KeyData], set[uint32]]:
|
|
132
|
+
all_keys = Keychain().get_keys(include_secrets=True)
|
|
133
|
+
missing_fingerprints = set()
|
|
134
|
+
|
|
135
|
+
# if fingerprints is None, we want all keys, otherwise we want the keys that match the fingerprints
|
|
136
|
+
if fingerprints is None:
|
|
137
|
+
keys = all_keys
|
|
138
|
+
else:
|
|
139
|
+
if not isinstance(fingerprints, list):
|
|
140
|
+
raise ValueError("fingerprints must be a list of integer")
|
|
141
|
+
keys_by_fingerprint = {key.fingerprint: key for key in all_keys}
|
|
142
|
+
keys = []
|
|
143
|
+
for fingerprint in fingerprints:
|
|
144
|
+
f = uint32(fingerprint)
|
|
145
|
+
if f not in keys_by_fingerprint:
|
|
146
|
+
missing_fingerprints.add(f)
|
|
147
|
+
else:
|
|
148
|
+
keys.append(keys_by_fingerprint[f])
|
|
149
|
+
return keys, missing_fingerprints
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclasses.dataclass(frozen=True)
|
|
153
|
+
class StatusMessage:
|
|
154
|
+
service: str
|
|
155
|
+
command: str
|
|
156
|
+
destination: str
|
|
157
|
+
origin: str
|
|
158
|
+
data: dict[str, Any]
|
|
159
|
+
|
|
160
|
+
def create_payload(self) -> str:
|
|
161
|
+
return create_payload(command=self.command, data=self.data, origin=self.origin, destination=self.destination)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class WebSocketServer:
|
|
165
|
+
def __init__(
|
|
166
|
+
self,
|
|
167
|
+
root_path: Path,
|
|
168
|
+
ca_crt_path: Path,
|
|
169
|
+
ca_key_path: Path,
|
|
170
|
+
crt_path: Path,
|
|
171
|
+
key_path: Path,
|
|
172
|
+
run_check_keys_on_unlock: bool = False,
|
|
173
|
+
):
|
|
174
|
+
self.root_path = root_path
|
|
175
|
+
self.log = log
|
|
176
|
+
self.services: dict[str, list[subprocess.Popen]] = dict()
|
|
177
|
+
self.plots_queue: list[dict] = []
|
|
178
|
+
self.connections: dict[str, set[WebSocketResponse]] = dict() # service name : {WebSocketResponse}
|
|
179
|
+
self.ping_job: Optional[asyncio.Task] = None
|
|
180
|
+
self.net_config = load_config(root_path, "config.yaml")
|
|
181
|
+
self.self_hostname = self.net_config["self_hostname"]
|
|
182
|
+
self.daemon_port = self.net_config["daemon_port"]
|
|
183
|
+
self.daemon_max_message_size = self.net_config.get("daemon_max_message_size", 50 * 1000 * 1000)
|
|
184
|
+
self.heartbeat = self.net_config.get("daemon_heartbeat", 300)
|
|
185
|
+
self.webserver: Optional[WebServer] = None
|
|
186
|
+
self.ssl_context = ssl_context_for_server(ca_crt_path, ca_key_path, crt_path, key_path, log=self.log)
|
|
187
|
+
self.keychain_server = KeychainServer()
|
|
188
|
+
self.run_check_keys_on_unlock = run_check_keys_on_unlock
|
|
189
|
+
self.shutdown_event = asyncio.Event()
|
|
190
|
+
self.state_changed_msg_queue: asyncio.Queue[StatusMessage] = asyncio.Queue()
|
|
191
|
+
self.state_changed_task: Optional[asyncio.Task] = None
|
|
192
|
+
|
|
193
|
+
@asynccontextmanager
|
|
194
|
+
async def run(self) -> AsyncIterator[None]:
|
|
195
|
+
self.log.info(f"Starting Daemon Server ({self.self_hostname}:{self.daemon_port})")
|
|
196
|
+
|
|
197
|
+
# Note: the minimum_version has been already set to TLSv1_2
|
|
198
|
+
# in ssl_context_for_server()
|
|
199
|
+
# Daemon is internal connections, so override to TLSv1_3 only unless specified in the config
|
|
200
|
+
if ssl.HAS_TLSv1_3 and not self.net_config.get("daemon_allow_tls_1_2", False):
|
|
201
|
+
try:
|
|
202
|
+
self.ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
|
|
203
|
+
except ValueError:
|
|
204
|
+
# in case the attempt above confused the config, set it again (likely not needed but doesn't hurt)
|
|
205
|
+
self.ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
206
|
+
|
|
207
|
+
if self.ssl_context.minimum_version is not ssl.TLSVersion.TLSv1_3:
|
|
208
|
+
self.log.warning(
|
|
209
|
+
(
|
|
210
|
+
"Deprecation Warning: Your version of SSL (%s) does not support TLS1.3. "
|
|
211
|
+
"A future version of Chia will require TLS1.3."
|
|
212
|
+
),
|
|
213
|
+
ssl.OPENSSL_VERSION,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
self.state_changed_task = create_referenced_task(self._process_state_changed_queue())
|
|
217
|
+
self.webserver = await WebServer.create(
|
|
218
|
+
hostname=self.self_hostname,
|
|
219
|
+
port=self.daemon_port,
|
|
220
|
+
keepalive_timeout=300,
|
|
221
|
+
shutdown_timeout=3,
|
|
222
|
+
routes=[web.get("/", self.incoming_connection)],
|
|
223
|
+
ssl_context=self.ssl_context,
|
|
224
|
+
logger=self.log,
|
|
225
|
+
)
|
|
226
|
+
try:
|
|
227
|
+
yield
|
|
228
|
+
finally:
|
|
229
|
+
if not self.shutdown_event.is_set():
|
|
230
|
+
await self.stop()
|
|
231
|
+
await self.exit()
|
|
232
|
+
|
|
233
|
+
async def setup_process_global_state(self, signal_handlers: SignalHandlers) -> None:
|
|
234
|
+
signal_handlers.setup_async_signal_handler(handler=self._accept_signal)
|
|
235
|
+
|
|
236
|
+
async def _accept_signal(
|
|
237
|
+
self,
|
|
238
|
+
signal_: signal.Signals,
|
|
239
|
+
stack_frame: Optional[FrameType],
|
|
240
|
+
loop: asyncio.AbstractEventLoop,
|
|
241
|
+
) -> None:
|
|
242
|
+
self.log.info("Received signal %s (%s), shutting down.", signal_.name, signal_.value)
|
|
243
|
+
await self.stop()
|
|
244
|
+
|
|
245
|
+
async def stop_command(self, websocket: WebSocketResponse, request: dict[str, Any] = {}) -> dict[str, Any]:
|
|
246
|
+
return await self.stop()
|
|
247
|
+
|
|
248
|
+
async def stop(self) -> dict[str, Any]:
|
|
249
|
+
cancel_task_safe(self.ping_job, self.log)
|
|
250
|
+
cancel_task_safe(self.state_changed_task, self.log)
|
|
251
|
+
service_names = list(self.services.keys())
|
|
252
|
+
stop_service_jobs = [
|
|
253
|
+
create_referenced_task(kill_service(self.root_path, self.services, s_n)) for s_n in service_names
|
|
254
|
+
]
|
|
255
|
+
if stop_service_jobs:
|
|
256
|
+
await asyncio.wait(stop_service_jobs)
|
|
257
|
+
self.services.clear()
|
|
258
|
+
self.shutdown_event.set()
|
|
259
|
+
log.info(f"Daemon Server stopping, Services stopped: {service_names}")
|
|
260
|
+
return {"success": True, "services_stopped": service_names}
|
|
261
|
+
|
|
262
|
+
async def incoming_connection(self, request: web.Request) -> web.StreamResponse:
|
|
263
|
+
ws: WebSocketResponse = web.WebSocketResponse(
|
|
264
|
+
max_msg_size=self.daemon_max_message_size, heartbeat=self.heartbeat
|
|
265
|
+
)
|
|
266
|
+
await ws.prepare(request)
|
|
267
|
+
|
|
268
|
+
while True:
|
|
269
|
+
msg = await ws.receive()
|
|
270
|
+
self.log.debug("Received message: %s", msg)
|
|
271
|
+
decoded: WsRpcMessage = {
|
|
272
|
+
"command": "",
|
|
273
|
+
"ack": False,
|
|
274
|
+
"data": {},
|
|
275
|
+
"request_id": "",
|
|
276
|
+
"destination": "",
|
|
277
|
+
"origin": "",
|
|
278
|
+
}
|
|
279
|
+
if msg.type == WSMsgType.TEXT:
|
|
280
|
+
try:
|
|
281
|
+
decoded = json.loads(msg.data)
|
|
282
|
+
if "data" not in decoded:
|
|
283
|
+
decoded["data"] = {}
|
|
284
|
+
|
|
285
|
+
maybe_response = await self.handle_message(ws, decoded)
|
|
286
|
+
if maybe_response is None:
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
response, connections = maybe_response
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
tb = traceback.format_exc()
|
|
293
|
+
self.log.error(f"Error while handling message: {tb}")
|
|
294
|
+
error = {"success": False, "error": f"{e}"}
|
|
295
|
+
response = format_response(decoded, error)
|
|
296
|
+
connections = {ws} # send error back to the sender
|
|
297
|
+
|
|
298
|
+
await self.send_all_responses(connections, response)
|
|
299
|
+
else:
|
|
300
|
+
service_names = self.remove_connection(ws)
|
|
301
|
+
|
|
302
|
+
if len(service_names) == 0:
|
|
303
|
+
service_names = ["Unknown"]
|
|
304
|
+
|
|
305
|
+
closing_message = f"Closing websocket with {service_names}."
|
|
306
|
+
|
|
307
|
+
level = logging.INFO
|
|
308
|
+
if msg.type == WSMsgType.CLOSED:
|
|
309
|
+
message = f"Connection closed. {closing_message}"
|
|
310
|
+
elif msg.type == WSMsgType.CLOSING:
|
|
311
|
+
message = f"Connection closing. {closing_message}"
|
|
312
|
+
elif msg.type == WSMsgType.CLOSE:
|
|
313
|
+
message = f"Connection close requested. {closing_message}"
|
|
314
|
+
elif msg.type == WSMsgType.ERROR:
|
|
315
|
+
level = logging.ERROR
|
|
316
|
+
message = f"Websocket exception. {closing_message} {ws.exception()}"
|
|
317
|
+
else:
|
|
318
|
+
level = logging.ERROR
|
|
319
|
+
message = f"Unexpected message type. {closing_message} {msg.type}"
|
|
320
|
+
|
|
321
|
+
self.log.log(level=level, msg=message)
|
|
322
|
+
|
|
323
|
+
await ws.close()
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
return ws
|
|
327
|
+
|
|
328
|
+
async def send_all_responses(self, connections: set[WebSocketResponse], response: str) -> None:
|
|
329
|
+
for connection in connections.copy():
|
|
330
|
+
try:
|
|
331
|
+
await connection.send_str(response)
|
|
332
|
+
except Exception as e:
|
|
333
|
+
service_names = self.remove_connection(connection)
|
|
334
|
+
if len(service_names) == 0:
|
|
335
|
+
service_names = ["Unknown"]
|
|
336
|
+
|
|
337
|
+
if isinstance(e, ConnectionResetError):
|
|
338
|
+
self.log.info(f"Peer disconnected. Closing websocket with {service_names}")
|
|
339
|
+
else:
|
|
340
|
+
tb = traceback.format_exc()
|
|
341
|
+
self.log.error(f"Unexpected exception trying to send to {service_names} (websocket: {e} {tb})")
|
|
342
|
+
self.log.info(f"Closing websocket with {service_names}")
|
|
343
|
+
|
|
344
|
+
await connection.close()
|
|
345
|
+
|
|
346
|
+
def remove_connection(self, websocket: WebSocketResponse) -> list[str]:
|
|
347
|
+
"""Returns a list of service names from which the connection was removed"""
|
|
348
|
+
service_names = []
|
|
349
|
+
for service_name, connections in self.connections.items():
|
|
350
|
+
try:
|
|
351
|
+
connections.remove(websocket)
|
|
352
|
+
except KeyError:
|
|
353
|
+
continue
|
|
354
|
+
service_names.append(service_name)
|
|
355
|
+
return service_names
|
|
356
|
+
|
|
357
|
+
async def ping_task(self) -> None:
|
|
358
|
+
with log_exceptions(
|
|
359
|
+
log=self.log,
|
|
360
|
+
consume=True,
|
|
361
|
+
message="Ping task received Cancel",
|
|
362
|
+
level=logging.DEBUG,
|
|
363
|
+
show_traceback=False,
|
|
364
|
+
exceptions_to_process=asyncio.CancelledError,
|
|
365
|
+
):
|
|
366
|
+
while True:
|
|
367
|
+
with log_exceptions(
|
|
368
|
+
log=self.log,
|
|
369
|
+
consume=True,
|
|
370
|
+
message="Unexpected exception, continuing:",
|
|
371
|
+
):
|
|
372
|
+
await asyncio.sleep(30)
|
|
373
|
+
|
|
374
|
+
for service_name, connections in self.connections.items():
|
|
375
|
+
if service_name == service_plotter:
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
for connection in connections.copy():
|
|
379
|
+
self.log.debug(f"About to ping: {service_name}")
|
|
380
|
+
try:
|
|
381
|
+
with log_exceptions(
|
|
382
|
+
log=self.log,
|
|
383
|
+
message=f"Ping error to {service_name}, closing connection.",
|
|
384
|
+
level=logging.WARNING,
|
|
385
|
+
show_traceback=False,
|
|
386
|
+
):
|
|
387
|
+
await connection.ping()
|
|
388
|
+
except: # noqa E722
|
|
389
|
+
self.remove_connection(connection)
|
|
390
|
+
await connection.close()
|
|
391
|
+
|
|
392
|
+
async def handle_message(
|
|
393
|
+
self, websocket: WebSocketResponse, message: WsRpcMessage
|
|
394
|
+
) -> Optional[tuple[str, set[WebSocketResponse]]]:
|
|
395
|
+
"""
|
|
396
|
+
This function gets called when new message is received via websocket.
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
command = message["command"]
|
|
400
|
+
destination = message["destination"]
|
|
401
|
+
if destination != "daemon":
|
|
402
|
+
if destination in self.connections:
|
|
403
|
+
sockets = self.connections[destination]
|
|
404
|
+
return dict_to_json_str(message), sockets
|
|
405
|
+
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
data = message["data"]
|
|
409
|
+
commands_with_data = [
|
|
410
|
+
"start_service",
|
|
411
|
+
"start_plotting",
|
|
412
|
+
"stop_plotting",
|
|
413
|
+
"stop_service",
|
|
414
|
+
"is_running",
|
|
415
|
+
"register_service",
|
|
416
|
+
]
|
|
417
|
+
if len(data) == 0 and command in commands_with_data:
|
|
418
|
+
response = {"success": False, "error": f'{command} requires "data"'}
|
|
419
|
+
# Keychain commands should be handled by KeychainServer
|
|
420
|
+
elif command in keychain_commands:
|
|
421
|
+
response = await self.keychain_server.handle_command(command, data)
|
|
422
|
+
elif command == "ping":
|
|
423
|
+
response = await ping()
|
|
424
|
+
else:
|
|
425
|
+
command_mapping = self.get_command_mapping()
|
|
426
|
+
if command in command_mapping:
|
|
427
|
+
response = await command_mapping[command](websocket=websocket, request=data)
|
|
428
|
+
else:
|
|
429
|
+
self.log.error(f"UK>> {message}")
|
|
430
|
+
response = {"success": False, "error": f"unknown_command {command}"}
|
|
431
|
+
|
|
432
|
+
full_response = format_response(message, response)
|
|
433
|
+
return full_response, {websocket}
|
|
434
|
+
|
|
435
|
+
def get_command_mapping(self) -> dict[str, Command]:
|
|
436
|
+
"""
|
|
437
|
+
Returns a mapping of commands to their respective function calls.
|
|
438
|
+
"""
|
|
439
|
+
return {
|
|
440
|
+
"start_service": self.start_service,
|
|
441
|
+
"start_plotting": self.start_plotting,
|
|
442
|
+
"stop_plotting": self.stop_plotting,
|
|
443
|
+
"stop_service": self.stop_service,
|
|
444
|
+
"is_running": self.is_running_command,
|
|
445
|
+
"running_services": self.running_services_command,
|
|
446
|
+
"is_keyring_locked": self.is_keyring_locked,
|
|
447
|
+
"keyring_status": self.keyring_status_command,
|
|
448
|
+
"unlock_keyring": self.unlock_keyring,
|
|
449
|
+
"validate_keyring_passphrase": self.validate_keyring_passphrase,
|
|
450
|
+
"set_keyring_passphrase": self.set_keyring_passphrase,
|
|
451
|
+
"remove_keyring_passphrase": self.remove_keyring_passphrase,
|
|
452
|
+
"exit": self.stop_command,
|
|
453
|
+
"register_service": self.register_service,
|
|
454
|
+
"get_status": self.get_status,
|
|
455
|
+
"get_version": self.get_version,
|
|
456
|
+
"get_plotters": self.get_plotters,
|
|
457
|
+
"get_routes": self.get_routes,
|
|
458
|
+
"get_wallet_addresses": self.get_wallet_addresses,
|
|
459
|
+
"get_keys_for_plotting": self.get_keys_for_plotting,
|
|
460
|
+
"get_network_info": self.get_network_info,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async def get_network_info(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
464
|
+
network_name = self.net_config["selected_network"]
|
|
465
|
+
address_prefix = self.net_config["network_overrides"]["config"][network_name]["address_prefix"]
|
|
466
|
+
genesis_challenge = self.net_config["network_overrides"]["constants"][network_name]["GENESIS_CHALLENGE"]
|
|
467
|
+
response: dict[str, Any] = {
|
|
468
|
+
"success": True,
|
|
469
|
+
"network_name": network_name,
|
|
470
|
+
"network_prefix": address_prefix,
|
|
471
|
+
"genesis_challenge": genesis_challenge,
|
|
472
|
+
}
|
|
473
|
+
return response
|
|
474
|
+
|
|
475
|
+
async def is_keyring_locked(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
476
|
+
locked: bool = Keychain.is_keyring_locked()
|
|
477
|
+
response: dict[str, Any] = {"success": True, "is_keyring_locked": locked}
|
|
478
|
+
return response
|
|
479
|
+
|
|
480
|
+
async def keyring_status_command(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
481
|
+
return await self.keyring_status()
|
|
482
|
+
|
|
483
|
+
async def keyring_status(self) -> dict[str, Any]:
|
|
484
|
+
can_save_passphrase: bool = supports_os_passphrase_storage()
|
|
485
|
+
user_passphrase_is_set: bool = Keychain.has_master_passphrase() and not using_default_passphrase()
|
|
486
|
+
locked: bool = Keychain.is_keyring_locked()
|
|
487
|
+
can_set_passphrase_hint: bool = True
|
|
488
|
+
passphrase_hint: str = Keychain.get_master_passphrase_hint() or ""
|
|
489
|
+
requirements: dict[str, Any] = passphrase_requirements()
|
|
490
|
+
response: dict[str, Any] = {
|
|
491
|
+
"success": True,
|
|
492
|
+
"is_keyring_locked": locked,
|
|
493
|
+
"can_save_passphrase": can_save_passphrase,
|
|
494
|
+
"user_passphrase_is_set": user_passphrase_is_set,
|
|
495
|
+
"can_set_passphrase_hint": can_set_passphrase_hint,
|
|
496
|
+
"passphrase_hint": passphrase_hint,
|
|
497
|
+
"passphrase_requirements": requirements,
|
|
498
|
+
}
|
|
499
|
+
# Help diagnose GUI launch issues
|
|
500
|
+
self.log.debug(f"Keyring status: {response}")
|
|
501
|
+
return response
|
|
502
|
+
|
|
503
|
+
async def unlock_keyring(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
504
|
+
success: bool = False
|
|
505
|
+
error: Optional[str] = None
|
|
506
|
+
key: Optional[str] = request.get("key", None)
|
|
507
|
+
if type(key) is not str:
|
|
508
|
+
return {"success": False, "error": "missing key"}
|
|
509
|
+
|
|
510
|
+
try:
|
|
511
|
+
if Keychain.master_passphrase_is_valid(key, force_reload=True):
|
|
512
|
+
Keychain.set_cached_master_passphrase(key)
|
|
513
|
+
success = True
|
|
514
|
+
# Inform the GUI of keyring status changes
|
|
515
|
+
self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
|
|
516
|
+
else:
|
|
517
|
+
error = "bad passphrase"
|
|
518
|
+
except Exception as e:
|
|
519
|
+
tb = traceback.format_exc()
|
|
520
|
+
self.log.error(f"Keyring passphrase validation failed: {e} {tb}")
|
|
521
|
+
error = "validation exception"
|
|
522
|
+
|
|
523
|
+
if success and self.run_check_keys_on_unlock:
|
|
524
|
+
try:
|
|
525
|
+
self.log.info("Running check_keys now that the keyring is unlocked")
|
|
526
|
+
check_keys(self.root_path)
|
|
527
|
+
self.run_check_keys_on_unlock = False
|
|
528
|
+
except Exception as e:
|
|
529
|
+
tb = traceback.format_exc()
|
|
530
|
+
self.log.error(f"check_keys failed after unlocking keyring: {e} {tb}")
|
|
531
|
+
|
|
532
|
+
response: dict[str, Any] = {"success": success, "error": error}
|
|
533
|
+
return response
|
|
534
|
+
|
|
535
|
+
async def validate_keyring_passphrase(
|
|
536
|
+
self,
|
|
537
|
+
websocket: WebSocketResponse,
|
|
538
|
+
request: dict[str, Any],
|
|
539
|
+
) -> dict[str, Any]:
|
|
540
|
+
success: bool = False
|
|
541
|
+
error: Optional[str] = None
|
|
542
|
+
key: Optional[str] = request.get("key", None)
|
|
543
|
+
if type(key) is not str:
|
|
544
|
+
return {"success": False, "error": "missing key"}
|
|
545
|
+
|
|
546
|
+
try:
|
|
547
|
+
success = Keychain.master_passphrase_is_valid(key, force_reload=True)
|
|
548
|
+
except Exception as e:
|
|
549
|
+
tb = traceback.format_exc()
|
|
550
|
+
self.log.error(f"Keyring passphrase validation failed: {e} {tb}")
|
|
551
|
+
error = "validation exception"
|
|
552
|
+
|
|
553
|
+
response: dict[str, Any] = {"success": success, "error": error}
|
|
554
|
+
return response
|
|
555
|
+
|
|
556
|
+
async def set_keyring_passphrase(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
557
|
+
success: bool = False
|
|
558
|
+
error: Optional[str] = None
|
|
559
|
+
current_passphrase: Optional[str] = None
|
|
560
|
+
new_passphrase: Optional[str] = None
|
|
561
|
+
passphrase_hint: Optional[str] = request.get("passphrase_hint", None)
|
|
562
|
+
save_passphrase: bool = request.get("save_passphrase", False)
|
|
563
|
+
|
|
564
|
+
if using_default_passphrase():
|
|
565
|
+
current_passphrase = default_passphrase()
|
|
566
|
+
|
|
567
|
+
if Keychain.has_master_passphrase() and not current_passphrase:
|
|
568
|
+
current_passphrase = request.get("current_passphrase", None)
|
|
569
|
+
if type(current_passphrase) is not str:
|
|
570
|
+
return {"success": False, "error": "missing current_passphrase"}
|
|
571
|
+
|
|
572
|
+
new_passphrase = request.get("new_passphrase", None)
|
|
573
|
+
if type(new_passphrase) is not str:
|
|
574
|
+
return {"success": False, "error": "missing new_passphrase"}
|
|
575
|
+
|
|
576
|
+
if not Keychain.passphrase_meets_requirements(new_passphrase):
|
|
577
|
+
return {"success": False, "error": "passphrase doesn't satisfy requirements"}
|
|
578
|
+
|
|
579
|
+
try:
|
|
580
|
+
assert new_passphrase is not None # mypy, I love you
|
|
581
|
+
Keychain.set_master_passphrase(
|
|
582
|
+
current_passphrase,
|
|
583
|
+
new_passphrase,
|
|
584
|
+
passphrase_hint=passphrase_hint,
|
|
585
|
+
save_passphrase=save_passphrase,
|
|
586
|
+
)
|
|
587
|
+
except KeychainCurrentPassphraseIsInvalid:
|
|
588
|
+
error = "current passphrase is invalid"
|
|
589
|
+
except Exception as e:
|
|
590
|
+
tb = traceback.format_exc()
|
|
591
|
+
self.log.error(f"Failed to set keyring passphrase: {e} {tb}")
|
|
592
|
+
else:
|
|
593
|
+
success = True
|
|
594
|
+
# Inform the GUI of keyring status changes
|
|
595
|
+
self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
|
|
596
|
+
|
|
597
|
+
response: dict[str, Any] = {"success": success, "error": error}
|
|
598
|
+
return response
|
|
599
|
+
|
|
600
|
+
async def remove_keyring_passphrase(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
601
|
+
success: bool = False
|
|
602
|
+
error: Optional[str] = None
|
|
603
|
+
current_passphrase: Optional[str] = None
|
|
604
|
+
|
|
605
|
+
if not Keychain.has_master_passphrase():
|
|
606
|
+
return {"success": False, "error": "passphrase not set"}
|
|
607
|
+
|
|
608
|
+
current_passphrase = request.get("current_passphrase", None)
|
|
609
|
+
if type(current_passphrase) is not str:
|
|
610
|
+
return {"success": False, "error": "missing current_passphrase"}
|
|
611
|
+
|
|
612
|
+
try:
|
|
613
|
+
Keychain.remove_master_passphrase(current_passphrase)
|
|
614
|
+
except KeychainCurrentPassphraseIsInvalid:
|
|
615
|
+
error = "current passphrase is invalid"
|
|
616
|
+
except Exception as e:
|
|
617
|
+
tb = traceback.format_exc()
|
|
618
|
+
self.log.error(f"Failed to remove keyring passphrase: {e} {tb}")
|
|
619
|
+
else:
|
|
620
|
+
success = True
|
|
621
|
+
# Inform the GUI of keyring status changes
|
|
622
|
+
self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
|
|
623
|
+
|
|
624
|
+
response: dict[str, Any] = {"success": success, "error": error}
|
|
625
|
+
return response
|
|
626
|
+
|
|
627
|
+
async def get_status(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
628
|
+
response = {"success": True, "genesis_initialized": True}
|
|
629
|
+
return response
|
|
630
|
+
|
|
631
|
+
async def get_version(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
632
|
+
response = {"success": True, "version": __version__}
|
|
633
|
+
return response
|
|
634
|
+
|
|
635
|
+
async def get_plotters(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
636
|
+
plotters: dict[str, Any] = get_available_plotters(self.root_path)
|
|
637
|
+
response: dict[str, Any] = {"success": True, "plotters": plotters}
|
|
638
|
+
return response
|
|
639
|
+
|
|
640
|
+
async def get_routes(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
641
|
+
routes = list(self.get_command_mapping().keys())
|
|
642
|
+
response: dict[str, Any] = {"success": True, "routes": routes}
|
|
643
|
+
return response
|
|
644
|
+
|
|
645
|
+
async def get_wallet_addresses(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
646
|
+
fingerprints = request.get("fingerprints", None)
|
|
647
|
+
keys, missing_fingerprints = _get_keys_by_fingerprints(fingerprints)
|
|
648
|
+
if len(missing_fingerprints) > 0:
|
|
649
|
+
return {"success": False, "error": f"key(s) not found for fingerprint(s) {missing_fingerprints}"}
|
|
650
|
+
|
|
651
|
+
index = request.get("index", 0)
|
|
652
|
+
count = request.get("count", 1)
|
|
653
|
+
non_observer_derivation = request.get("non_observer_derivation", False)
|
|
654
|
+
|
|
655
|
+
selected = self.net_config["selected_network"]
|
|
656
|
+
prefix = self.net_config["network_overrides"]["config"][selected]["address_prefix"]
|
|
657
|
+
|
|
658
|
+
wallet_addresses_by_fingerprint = {}
|
|
659
|
+
for key in keys:
|
|
660
|
+
address_entries = []
|
|
661
|
+
|
|
662
|
+
# we require access to the private key to generate wallet addresses for non observer
|
|
663
|
+
if key.secrets is None and non_observer_derivation:
|
|
664
|
+
return {"success": False, "error": f"missing private key for key with fingerprint {key.fingerprint}"}
|
|
665
|
+
|
|
666
|
+
for i in range(index, index + count):
|
|
667
|
+
if non_observer_derivation:
|
|
668
|
+
sk = master_sk_to_wallet_sk(key.private_key, uint32(i))
|
|
669
|
+
pk = sk.get_g1()
|
|
670
|
+
else:
|
|
671
|
+
pk = master_pk_to_wallet_pk_unhardened(key.public_key, uint32(i))
|
|
672
|
+
wallet_address = encode_puzzle_hash(create_puzzlehash_for_pk(pk), prefix)
|
|
673
|
+
if non_observer_derivation:
|
|
674
|
+
hd_path = f"m/12381n/8444n/2n/{i}n"
|
|
675
|
+
else:
|
|
676
|
+
hd_path = f"m/12381/8444/2/{i}"
|
|
677
|
+
|
|
678
|
+
address_entries.append({"address": wallet_address, "hd_path": hd_path})
|
|
679
|
+
|
|
680
|
+
wallet_addresses_by_fingerprint[key.fingerprint] = address_entries
|
|
681
|
+
|
|
682
|
+
response: dict[str, Any] = {"success": True, "wallet_addresses": wallet_addresses_by_fingerprint}
|
|
683
|
+
return response
|
|
684
|
+
|
|
685
|
+
async def get_keys_for_plotting(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
686
|
+
fingerprints = request.get("fingerprints", None)
|
|
687
|
+
keys, missing_fingerprints = _get_keys_by_fingerprints(fingerprints)
|
|
688
|
+
if len(missing_fingerprints) > 0:
|
|
689
|
+
return {"success": False, "error": f"key(s) not found for fingerprint(s) {missing_fingerprints}"}
|
|
690
|
+
|
|
691
|
+
keys_for_plot: dict[uint32, Any] = {}
|
|
692
|
+
for key in keys:
|
|
693
|
+
if key.secrets is None:
|
|
694
|
+
continue
|
|
695
|
+
sk = key.private_key
|
|
696
|
+
farmer_public_key: G1Element = master_sk_to_farmer_sk(sk).get_g1()
|
|
697
|
+
pool_public_key: G1Element = master_sk_to_pool_sk(sk).get_g1()
|
|
698
|
+
keys_for_plot[key.fingerprint] = {
|
|
699
|
+
"farmer_public_key": bytes(farmer_public_key).hex(),
|
|
700
|
+
"pool_public_key": bytes(pool_public_key).hex(),
|
|
701
|
+
}
|
|
702
|
+
response: dict[str, Any] = {
|
|
703
|
+
"success": True,
|
|
704
|
+
"keys": keys_for_plot,
|
|
705
|
+
}
|
|
706
|
+
return response
|
|
707
|
+
|
|
708
|
+
def plot_queue_to_payload(self, plot_queue_item, send_full_log: bool) -> dict[str, Any]:
|
|
709
|
+
error = plot_queue_item.get("error")
|
|
710
|
+
has_error = error is not None
|
|
711
|
+
|
|
712
|
+
item = {
|
|
713
|
+
"id": plot_queue_item["id"],
|
|
714
|
+
"queue": plot_queue_item["queue"],
|
|
715
|
+
"size": plot_queue_item["size"],
|
|
716
|
+
"parallel": plot_queue_item["parallel"],
|
|
717
|
+
"delay": plot_queue_item["delay"],
|
|
718
|
+
"state": plot_queue_item["state"],
|
|
719
|
+
"error": str(error) if has_error else None,
|
|
720
|
+
"deleted": plot_queue_item["deleted"],
|
|
721
|
+
"log_new": plot_queue_item.get("log_new"),
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if send_full_log:
|
|
725
|
+
item["log"] = plot_queue_item.get("log")
|
|
726
|
+
return item
|
|
727
|
+
|
|
728
|
+
def prepare_plot_state_message(self, state: PlotEvent, id):
|
|
729
|
+
message = {
|
|
730
|
+
"state": state,
|
|
731
|
+
"queue": self.extract_plot_queue(id),
|
|
732
|
+
}
|
|
733
|
+
return message
|
|
734
|
+
|
|
735
|
+
def extract_plot_queue(self, id=None) -> list[dict]:
|
|
736
|
+
send_full_log = id is None
|
|
737
|
+
data = []
|
|
738
|
+
for item in self.plots_queue:
|
|
739
|
+
if id is None or item["id"] == id:
|
|
740
|
+
data.append(self.plot_queue_to_payload(item, send_full_log))
|
|
741
|
+
return data
|
|
742
|
+
|
|
743
|
+
async def _process_state_changed_queue(self) -> None:
|
|
744
|
+
with log_exceptions(
|
|
745
|
+
log=self.log,
|
|
746
|
+
consume=True,
|
|
747
|
+
message="State changed task received Cancel",
|
|
748
|
+
level=logging.DEBUG,
|
|
749
|
+
show_traceback=False,
|
|
750
|
+
exceptions_to_process=asyncio.CancelledError,
|
|
751
|
+
):
|
|
752
|
+
while True:
|
|
753
|
+
with log_exceptions(
|
|
754
|
+
log=self.log,
|
|
755
|
+
consume=True,
|
|
756
|
+
message="Unexpected exception, continuing:",
|
|
757
|
+
):
|
|
758
|
+
message = await self.state_changed_msg_queue.get()
|
|
759
|
+
await self._state_changed(message)
|
|
760
|
+
|
|
761
|
+
async def _state_changed(self, message: StatusMessage) -> None:
|
|
762
|
+
"""If id is None, send the whole state queue"""
|
|
763
|
+
if message.service not in self.connections:
|
|
764
|
+
return None
|
|
765
|
+
|
|
766
|
+
websockets = self.connections[message.service]
|
|
767
|
+
for websocket in websockets.copy():
|
|
768
|
+
try:
|
|
769
|
+
await websocket.send_str(message.create_payload())
|
|
770
|
+
except Exception as e:
|
|
771
|
+
tb = traceback.format_exc()
|
|
772
|
+
self.log.error(f"Unexpected exception trying to send to websocket: {e} {tb}")
|
|
773
|
+
websockets.remove(websocket)
|
|
774
|
+
await websocket.close()
|
|
775
|
+
|
|
776
|
+
def state_changed(self, service: str, message: dict[str, Any]) -> None:
|
|
777
|
+
self.state_changed_msg_queue.put_nowait(
|
|
778
|
+
StatusMessage(
|
|
779
|
+
service=service, command="state_changed", destination="wallet_ui", origin=service, data=message
|
|
780
|
+
)
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
def keyring_status_changed(self, keyring_status: dict[str, Any], destination: str) -> None:
|
|
784
|
+
self.state_changed_msg_queue.put_nowait(
|
|
785
|
+
StatusMessage(
|
|
786
|
+
service="wallet_ui",
|
|
787
|
+
command="keyring_status_changed",
|
|
788
|
+
destination=destination,
|
|
789
|
+
origin="daemon",
|
|
790
|
+
data=keyring_status,
|
|
791
|
+
)
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
async def _watch_file_changes(self, config, fp: TextIO, loop: asyncio.AbstractEventLoop):
|
|
795
|
+
id: str = config["id"]
|
|
796
|
+
plotter: str = config["plotter"]
|
|
797
|
+
final_words: list[str] = []
|
|
798
|
+
|
|
799
|
+
if plotter == "chiapos":
|
|
800
|
+
final_words = ["Renamed final file"]
|
|
801
|
+
elif plotter == "bladebit":
|
|
802
|
+
if "cudaplot" in config["command_args"]:
|
|
803
|
+
final_words = ["Completed writing plot"]
|
|
804
|
+
else:
|
|
805
|
+
final_words = ["Finished plotting in"]
|
|
806
|
+
elif plotter == "madmax":
|
|
807
|
+
temp_dir = config["temp_dir"]
|
|
808
|
+
final_dir = config["final_dir"]
|
|
809
|
+
if temp_dir == final_dir:
|
|
810
|
+
final_words = ["Total plot creation time was"]
|
|
811
|
+
else:
|
|
812
|
+
# "Renamed final plot" if moving to a final dir on the same volume
|
|
813
|
+
# "Copy to <path> finished, took..." if copying to another volume
|
|
814
|
+
final_words = ["Renamed final plot", "finished, took"]
|
|
815
|
+
|
|
816
|
+
while True:
|
|
817
|
+
new_data = await loop.run_in_executor(io_pool_exc, fp.readline)
|
|
818
|
+
|
|
819
|
+
if config["state"] is not PlotState.RUNNING:
|
|
820
|
+
return None
|
|
821
|
+
|
|
822
|
+
if new_data not in {None, ""}:
|
|
823
|
+
config["log"] = new_data if config["log"] is None else config["log"] + new_data
|
|
824
|
+
config["log_new"] = new_data
|
|
825
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.LOG_CHANGED, id))
|
|
826
|
+
|
|
827
|
+
if new_data:
|
|
828
|
+
for word in final_words:
|
|
829
|
+
if word in new_data:
|
|
830
|
+
return None
|
|
831
|
+
else:
|
|
832
|
+
await asyncio.sleep(0.5)
|
|
833
|
+
|
|
834
|
+
async def _track_plotting_progress(self, config, loop: asyncio.AbstractEventLoop):
|
|
835
|
+
file_path = config["out_file"]
|
|
836
|
+
with open(file_path) as fp:
|
|
837
|
+
await self._watch_file_changes(config, fp, loop)
|
|
838
|
+
|
|
839
|
+
def _common_plotting_command_args(self, request: Any, ignoreCount: bool) -> list[str]:
|
|
840
|
+
n = 1 if ignoreCount else request["n"] # Plot count
|
|
841
|
+
d = request["d"] # Final directory
|
|
842
|
+
r = request["r"] # Threads
|
|
843
|
+
f = request.get("f") # Farmer pubkey
|
|
844
|
+
p = request.get("p") # Pool pubkey
|
|
845
|
+
c = request.get("c") # Pool contract address
|
|
846
|
+
|
|
847
|
+
command_args: list[str] = ["-n", str(n), "-d", d, "-r", str(r)]
|
|
848
|
+
|
|
849
|
+
if f is not None:
|
|
850
|
+
command_args.append("-f")
|
|
851
|
+
command_args.append(str(f))
|
|
852
|
+
if p is not None:
|
|
853
|
+
command_args.append("-p")
|
|
854
|
+
command_args.append(str(p))
|
|
855
|
+
if c is not None:
|
|
856
|
+
command_args.append("-c")
|
|
857
|
+
command_args.append(str(c))
|
|
858
|
+
|
|
859
|
+
return command_args
|
|
860
|
+
|
|
861
|
+
def _chiapos_plotting_command_args(self, request: Any, ignoreCount: bool) -> list[str]:
|
|
862
|
+
k = request["k"] # Plot size
|
|
863
|
+
t = request["t"] # Temp directory
|
|
864
|
+
t2 = request.get("t2") # Temp2 directory
|
|
865
|
+
b = request["b"] # Buffer size
|
|
866
|
+
u = request["u"] # Buckets
|
|
867
|
+
a = request.get("a") # Fingerprint
|
|
868
|
+
e = request["e"] # Disable bitfield
|
|
869
|
+
x = request["x"] # Exclude final directory
|
|
870
|
+
override_k = request["overrideK"] # Force plot sizes < k32
|
|
871
|
+
|
|
872
|
+
command_args: list[str] = ["-k", str(k), "-t", t, "-b", str(b), "-u", str(u)]
|
|
873
|
+
|
|
874
|
+
if t2 is not None:
|
|
875
|
+
command_args.append("-2")
|
|
876
|
+
command_args.append(str(t2))
|
|
877
|
+
if a is not None:
|
|
878
|
+
command_args.append("-a")
|
|
879
|
+
command_args.append(str(a))
|
|
880
|
+
if e is True:
|
|
881
|
+
command_args.append("-e")
|
|
882
|
+
if x is True:
|
|
883
|
+
command_args.append("-x")
|
|
884
|
+
if override_k is True:
|
|
885
|
+
command_args.append("--override-k")
|
|
886
|
+
|
|
887
|
+
return command_args
|
|
888
|
+
|
|
889
|
+
def _bladebit_plotting_command_args(self, request: Any, ignoreCount: bool) -> list[str]:
|
|
890
|
+
plot_type = request["plot_type"]
|
|
891
|
+
if plot_type not in {"ramplot", "diskplot", "cudaplot"}:
|
|
892
|
+
raise ValueError(f"Unknown plot_type: {plot_type}")
|
|
893
|
+
|
|
894
|
+
command_args: list[str] = []
|
|
895
|
+
|
|
896
|
+
# Common options among diskplot, ramplot, cudaplot
|
|
897
|
+
w = request.get("w", False) # Warm start
|
|
898
|
+
m = request.get("m", False) # Disable NUMA
|
|
899
|
+
no_cpu_affinity = request.get("no_cpu_affinity", False)
|
|
900
|
+
compress = request.get("compress", None) # Compression level
|
|
901
|
+
|
|
902
|
+
if w is True:
|
|
903
|
+
command_args.append("--warmstart")
|
|
904
|
+
if m is True:
|
|
905
|
+
command_args.append("--nonuma")
|
|
906
|
+
if no_cpu_affinity is True:
|
|
907
|
+
command_args.append("--no-cpu-affinity")
|
|
908
|
+
if compress is not None and str(compress).isdigit():
|
|
909
|
+
command_args.append("--compress")
|
|
910
|
+
command_args.append(str(compress))
|
|
911
|
+
|
|
912
|
+
# ramplot don't accept any more options
|
|
913
|
+
if plot_type == "ramplot":
|
|
914
|
+
return command_args
|
|
915
|
+
|
|
916
|
+
# Options only applicable for cudaplot
|
|
917
|
+
if plot_type == "cudaplot":
|
|
918
|
+
device_index = request.get("device", None)
|
|
919
|
+
t1 = request.get("t", None) # Temp directory
|
|
920
|
+
t2 = request.get("t2", None) # Temp2 directory
|
|
921
|
+
disk_128 = request.get("disk_128", False)
|
|
922
|
+
disk_16 = request.get("disk_16", False)
|
|
923
|
+
|
|
924
|
+
if device_index is not None and str(device_index).isdigit():
|
|
925
|
+
command_args.append("--device")
|
|
926
|
+
command_args.append(str(device_index))
|
|
927
|
+
if t1 is not None:
|
|
928
|
+
command_args.append("-t")
|
|
929
|
+
command_args.append(t1)
|
|
930
|
+
if t2 is not None:
|
|
931
|
+
command_args.append("-2")
|
|
932
|
+
command_args.append(t2)
|
|
933
|
+
if disk_128:
|
|
934
|
+
command_args.append("--disk-128")
|
|
935
|
+
if disk_16:
|
|
936
|
+
command_args.append("--disk-16")
|
|
937
|
+
return command_args
|
|
938
|
+
|
|
939
|
+
# if plot_type == "diskplot"
|
|
940
|
+
# memo = request["memo"]
|
|
941
|
+
t1 = request["t"] # Temp directory
|
|
942
|
+
t2 = request.get("t2") # Temp2 directory
|
|
943
|
+
u = request.get("u") # Buckets
|
|
944
|
+
cache = request.get("cache")
|
|
945
|
+
f1_threads = request.get("f1_threads")
|
|
946
|
+
fp_threads = request.get("fp_threads")
|
|
947
|
+
c_threads = request.get("c_threads")
|
|
948
|
+
p2_threads = request.get("p2_threads")
|
|
949
|
+
p3_threads = request.get("p3_threads")
|
|
950
|
+
alternate = request.get("alternate", False)
|
|
951
|
+
no_t1_direct = request.get("no_t1_direct", False)
|
|
952
|
+
no_t2_direct = request.get("no_t2_direct", False)
|
|
953
|
+
|
|
954
|
+
command_args.append("-t")
|
|
955
|
+
command_args.append(t1)
|
|
956
|
+
if t2 is not None:
|
|
957
|
+
command_args.append("-2")
|
|
958
|
+
command_args.append(t2)
|
|
959
|
+
if u is not None:
|
|
960
|
+
command_args.append("-u")
|
|
961
|
+
command_args.append(str(u))
|
|
962
|
+
if cache is not None:
|
|
963
|
+
command_args.append("--cache")
|
|
964
|
+
command_args.append(str(cache))
|
|
965
|
+
if f1_threads is not None:
|
|
966
|
+
command_args.append("--f1-threads")
|
|
967
|
+
command_args.append(str(f1_threads))
|
|
968
|
+
if fp_threads is not None:
|
|
969
|
+
command_args.append("--fp-threads")
|
|
970
|
+
command_args.append(str(fp_threads))
|
|
971
|
+
if c_threads is not None:
|
|
972
|
+
command_args.append("--c-threads")
|
|
973
|
+
command_args.append(str(c_threads))
|
|
974
|
+
if p2_threads is not None:
|
|
975
|
+
command_args.append("--p2-threads")
|
|
976
|
+
command_args.append(str(p2_threads))
|
|
977
|
+
if p3_threads is not None:
|
|
978
|
+
command_args.append("--p3-threads")
|
|
979
|
+
command_args.append(str(p3_threads))
|
|
980
|
+
if alternate is not None:
|
|
981
|
+
command_args.append("--alternate")
|
|
982
|
+
if no_t1_direct is not None:
|
|
983
|
+
command_args.append("--no-t1-direct")
|
|
984
|
+
if no_t2_direct is not None:
|
|
985
|
+
command_args.append("--no-t2-direct")
|
|
986
|
+
|
|
987
|
+
return command_args
|
|
988
|
+
|
|
989
|
+
def _madmax_plotting_command_args(self, request: Any, ignoreCount: bool, index: int) -> list[str]:
|
|
990
|
+
k = request["k"] # Plot size
|
|
991
|
+
t = request["t"] # Temp directory
|
|
992
|
+
t2 = request["t2"] # Temp2 directory
|
|
993
|
+
u = request["u"] # Buckets
|
|
994
|
+
v = request["v"] # Buckets for phase 3 & 4
|
|
995
|
+
K = request.get("K", 1) # Thread multiplier for phase 2
|
|
996
|
+
G = request.get("G", False) # Alternate tmpdir/tmp2dir
|
|
997
|
+
|
|
998
|
+
command_args: list[str] = []
|
|
999
|
+
command_args.append(f"-k{k}")
|
|
1000
|
+
command_args.append(f"-u{u}")
|
|
1001
|
+
command_args.append(f"-v{v}")
|
|
1002
|
+
command_args.append(f"-K{K}")
|
|
1003
|
+
|
|
1004
|
+
# Handle madmax's tmptoggle option ourselves when managing GUI plotting
|
|
1005
|
+
if G is True and t != t2 and index % 2:
|
|
1006
|
+
# Swap tmp and tmp2
|
|
1007
|
+
command_args.append(f"-t{t2}")
|
|
1008
|
+
command_args.append(f"-2{t}")
|
|
1009
|
+
else:
|
|
1010
|
+
command_args.append(f"-t{t}")
|
|
1011
|
+
command_args.append(f"-2{t2}")
|
|
1012
|
+
|
|
1013
|
+
return command_args
|
|
1014
|
+
|
|
1015
|
+
def _build_plotting_command_args(self, request: Any, ignoreCount: bool, index: int) -> list[str]:
|
|
1016
|
+
plotter: str = request.get("plotter", "chiapos")
|
|
1017
|
+
command_args: list[str] = ["chia", "plotters", plotter]
|
|
1018
|
+
|
|
1019
|
+
if plotter == "bladebit":
|
|
1020
|
+
# plotter command must be either
|
|
1021
|
+
# 'chia plotters bladebit ramplot' or 'chia plotters bladebit diskplot'
|
|
1022
|
+
plot_type = request["plot_type"]
|
|
1023
|
+
assert plot_type in {"diskplot", "ramplot", "cudaplot"}
|
|
1024
|
+
command_args.append(plot_type)
|
|
1025
|
+
|
|
1026
|
+
command_args.extend(self._common_plotting_command_args(request, ignoreCount))
|
|
1027
|
+
|
|
1028
|
+
if plotter == "chiapos":
|
|
1029
|
+
command_args.extend(self._chiapos_plotting_command_args(request, ignoreCount))
|
|
1030
|
+
elif plotter == "madmax":
|
|
1031
|
+
command_args.extend(self._madmax_plotting_command_args(request, ignoreCount, index))
|
|
1032
|
+
elif plotter == "bladebit":
|
|
1033
|
+
command_args.extend(self._bladebit_plotting_command_args(request, ignoreCount))
|
|
1034
|
+
|
|
1035
|
+
return command_args
|
|
1036
|
+
|
|
1037
|
+
def _is_serial_plotting_running(self, queue: str = "default") -> bool:
|
|
1038
|
+
response = False
|
|
1039
|
+
for item in self.plots_queue:
|
|
1040
|
+
if item["queue"] == queue and item["parallel"] is False and item["state"] is PlotState.RUNNING:
|
|
1041
|
+
response = True
|
|
1042
|
+
return response
|
|
1043
|
+
|
|
1044
|
+
def _get_plots_queue_item(self, id: str):
|
|
1045
|
+
config = next(item for item in self.plots_queue if item["id"] == id)
|
|
1046
|
+
return config
|
|
1047
|
+
|
|
1048
|
+
def _run_next_serial_plotting(self, loop: asyncio.AbstractEventLoop, queue: str = "default"):
|
|
1049
|
+
next_plot_id = None
|
|
1050
|
+
|
|
1051
|
+
if self._is_serial_plotting_running(queue) is True:
|
|
1052
|
+
return None
|
|
1053
|
+
|
|
1054
|
+
for item in self.plots_queue:
|
|
1055
|
+
if item["queue"] == queue and item["state"] is PlotState.SUBMITTED and item["parallel"] is False:
|
|
1056
|
+
next_plot_id = item["id"]
|
|
1057
|
+
break
|
|
1058
|
+
|
|
1059
|
+
if next_plot_id is not None:
|
|
1060
|
+
create_referenced_task(self._start_plotting(next_plot_id, loop, queue))
|
|
1061
|
+
|
|
1062
|
+
def _post_process_plotting_job(self, job: dict[str, Any]):
|
|
1063
|
+
id: str = job["id"]
|
|
1064
|
+
final_dir: str = job["final_dir"]
|
|
1065
|
+
exclude_final_dir: bool = job["exclude_final_dir"]
|
|
1066
|
+
log.info(f"Post-processing plotter job with ID {id}") # lgtm [py/clear-text-logging-sensitive-data]
|
|
1067
|
+
if not exclude_final_dir:
|
|
1068
|
+
try:
|
|
1069
|
+
add_plot_directory(self.root_path, final_dir)
|
|
1070
|
+
except ValueError as e:
|
|
1071
|
+
log.warning(f"_post_process_plotting_job: {e}")
|
|
1072
|
+
|
|
1073
|
+
async def _start_plotting(self, id: str, loop: asyncio.AbstractEventLoop, queue: str = "default"):
|
|
1074
|
+
current_process = None
|
|
1075
|
+
try:
|
|
1076
|
+
log.info(f"Starting plotting with ID {id}") # lgtm [py/clear-text-logging-sensitive-data]
|
|
1077
|
+
config = self._get_plots_queue_item(id)
|
|
1078
|
+
|
|
1079
|
+
if config is None:
|
|
1080
|
+
raise Exception(f"Plot queue config with ID {id} does not exist")
|
|
1081
|
+
|
|
1082
|
+
state = config["state"]
|
|
1083
|
+
if state is not PlotState.SUBMITTED:
|
|
1084
|
+
raise Exception(f"Plot with ID {id} has no state submitted")
|
|
1085
|
+
|
|
1086
|
+
assert id == config["id"]
|
|
1087
|
+
delay = config["delay"]
|
|
1088
|
+
await asyncio.sleep(delay)
|
|
1089
|
+
|
|
1090
|
+
if config["state"] is not PlotState.SUBMITTED:
|
|
1091
|
+
return None
|
|
1092
|
+
|
|
1093
|
+
service_name = config["service_name"]
|
|
1094
|
+
command_args = config["command_args"]
|
|
1095
|
+
|
|
1096
|
+
# Set the -D/--connect_to_daemon flag to signify that the child should connect
|
|
1097
|
+
# to the daemon to access the keychain
|
|
1098
|
+
command_args.append("-D")
|
|
1099
|
+
|
|
1100
|
+
self.log.debug(f"command_args before launch_plotter are {command_args}")
|
|
1101
|
+
self.log.debug(f"self.root_path before launch_plotter is {self.root_path}")
|
|
1102
|
+
process, _pid_path = launch_plotter(self.root_path, service_name, command_args, id)
|
|
1103
|
+
|
|
1104
|
+
current_process = process
|
|
1105
|
+
|
|
1106
|
+
config["state"] = PlotState.RUNNING
|
|
1107
|
+
config["out_file"] = plotter_log_path(self.root_path, id).absolute()
|
|
1108
|
+
config["process"] = process
|
|
1109
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1110
|
+
|
|
1111
|
+
if service_name not in self.services:
|
|
1112
|
+
self.services[service_name] = []
|
|
1113
|
+
|
|
1114
|
+
self.services[service_name].append(process)
|
|
1115
|
+
|
|
1116
|
+
await self._track_plotting_progress(config, loop)
|
|
1117
|
+
|
|
1118
|
+
self.log.debug("finished tracking plotting progress. setting state to FINISHED")
|
|
1119
|
+
|
|
1120
|
+
config["state"] = PlotState.FINISHED
|
|
1121
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1122
|
+
|
|
1123
|
+
self._post_process_plotting_job(config)
|
|
1124
|
+
|
|
1125
|
+
except (subprocess.SubprocessError, OSError):
|
|
1126
|
+
log.exception(f"problem starting {service_name}") # lgtm [py/clear-text-logging-sensitive-data]
|
|
1127
|
+
error = Exception("Start plotting failed")
|
|
1128
|
+
config["state"] = PlotState.FINISHED
|
|
1129
|
+
config["error"] = error
|
|
1130
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1131
|
+
raise error
|
|
1132
|
+
|
|
1133
|
+
finally:
|
|
1134
|
+
if current_process is not None:
|
|
1135
|
+
try:
|
|
1136
|
+
self.services[service_name].remove(current_process)
|
|
1137
|
+
except KeyError:
|
|
1138
|
+
pass
|
|
1139
|
+
current_process.wait() # prevent zombies
|
|
1140
|
+
self._run_next_serial_plotting(loop, queue)
|
|
1141
|
+
|
|
1142
|
+
async def start_plotting(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1143
|
+
service_name = request["service"]
|
|
1144
|
+
|
|
1145
|
+
plotter = request.get("plotter", "chiapos")
|
|
1146
|
+
delay = int(request.get("delay", 0))
|
|
1147
|
+
parallel = request.get("parallel", False)
|
|
1148
|
+
size = request.get("k")
|
|
1149
|
+
temp_dir = request.get("t")
|
|
1150
|
+
final_dir = request.get("d")
|
|
1151
|
+
exclude_final_dir = request.get("x", False)
|
|
1152
|
+
count = int(request.get("n", 1))
|
|
1153
|
+
queue = request.get("queue", "default")
|
|
1154
|
+
|
|
1155
|
+
if ("p" in request) and ("c" in request):
|
|
1156
|
+
response = {
|
|
1157
|
+
"success": False,
|
|
1158
|
+
"service_name": service_name,
|
|
1159
|
+
"error": "Choose one of pool_contract_address and pool_public_key",
|
|
1160
|
+
}
|
|
1161
|
+
return response
|
|
1162
|
+
|
|
1163
|
+
ids: list[str] = []
|
|
1164
|
+
for k in range(count):
|
|
1165
|
+
id = str(uuid.uuid4())
|
|
1166
|
+
ids.append(id)
|
|
1167
|
+
config = {
|
|
1168
|
+
"id": id, # lgtm [py/clear-text-logging-sensitive-data]
|
|
1169
|
+
"size": size,
|
|
1170
|
+
"queue": queue,
|
|
1171
|
+
"plotter": plotter,
|
|
1172
|
+
"service_name": service_name,
|
|
1173
|
+
"command_args": self._build_plotting_command_args(request, True, k),
|
|
1174
|
+
"parallel": parallel,
|
|
1175
|
+
"delay": delay * k if parallel is True else delay,
|
|
1176
|
+
"state": PlotState.SUBMITTED,
|
|
1177
|
+
"deleted": False,
|
|
1178
|
+
"error": None,
|
|
1179
|
+
"log": None,
|
|
1180
|
+
"process": None,
|
|
1181
|
+
"temp_dir": temp_dir,
|
|
1182
|
+
"final_dir": final_dir,
|
|
1183
|
+
"exclude_final_dir": exclude_final_dir,
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
self.plots_queue.append(config)
|
|
1187
|
+
|
|
1188
|
+
# notify GUI about new plot queue item
|
|
1189
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1190
|
+
|
|
1191
|
+
# only the first item can start when user selected serial plotting
|
|
1192
|
+
can_start_serial_plotting = k == 0 and self._is_serial_plotting_running(queue) is False
|
|
1193
|
+
|
|
1194
|
+
if parallel is True or can_start_serial_plotting:
|
|
1195
|
+
log.info(f"Plotting will start in {config['delay']} seconds")
|
|
1196
|
+
# TODO: loop gets passed down a lot, review for potential removal
|
|
1197
|
+
loop = asyncio.get_running_loop()
|
|
1198
|
+
# TODO: stop dropping tasks on the floor
|
|
1199
|
+
create_referenced_task(self._start_plotting(id, loop, queue))
|
|
1200
|
+
else:
|
|
1201
|
+
log.info("Plotting will start automatically when previous plotting finish")
|
|
1202
|
+
|
|
1203
|
+
response = {
|
|
1204
|
+
"success": True,
|
|
1205
|
+
"ids": ids,
|
|
1206
|
+
"service_name": service_name,
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return response
|
|
1210
|
+
|
|
1211
|
+
async def stop_plotting(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1212
|
+
id = request["id"]
|
|
1213
|
+
config = self._get_plots_queue_item(id)
|
|
1214
|
+
if config is None:
|
|
1215
|
+
return {"success": False}
|
|
1216
|
+
|
|
1217
|
+
id = config["id"]
|
|
1218
|
+
state = config["state"]
|
|
1219
|
+
process = config["process"]
|
|
1220
|
+
queue = config["queue"]
|
|
1221
|
+
|
|
1222
|
+
if config["state"] is PlotState.REMOVING:
|
|
1223
|
+
return {"success": False}
|
|
1224
|
+
|
|
1225
|
+
try:
|
|
1226
|
+
run_next = False
|
|
1227
|
+
if process is not None and state == PlotState.RUNNING:
|
|
1228
|
+
run_next = True
|
|
1229
|
+
config["state"] = PlotState.REMOVING
|
|
1230
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1231
|
+
await kill_processes([process], self.root_path, service_plotter, id)
|
|
1232
|
+
|
|
1233
|
+
config["state"] = PlotState.FINISHED
|
|
1234
|
+
config["deleted"] = True
|
|
1235
|
+
|
|
1236
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1237
|
+
|
|
1238
|
+
self.plots_queue.remove(config)
|
|
1239
|
+
|
|
1240
|
+
if run_next:
|
|
1241
|
+
# TODO: review to see if we can remove this
|
|
1242
|
+
loop = asyncio.get_running_loop()
|
|
1243
|
+
self._run_next_serial_plotting(loop, queue)
|
|
1244
|
+
|
|
1245
|
+
return {"success": True}
|
|
1246
|
+
except Exception as e:
|
|
1247
|
+
log.error(f"Error during killing the plot process: {e}")
|
|
1248
|
+
config["state"] = PlotState.FINISHED
|
|
1249
|
+
config["error"] = str(e)
|
|
1250
|
+
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
|
1251
|
+
return {"success": False}
|
|
1252
|
+
|
|
1253
|
+
async def start_service(self, websocket: WebSocketResponse, request: dict[str, Any]):
|
|
1254
|
+
service_command = request["service"]
|
|
1255
|
+
|
|
1256
|
+
error = None
|
|
1257
|
+
success = False
|
|
1258
|
+
testing = False
|
|
1259
|
+
already_running = False
|
|
1260
|
+
if "testing" in request:
|
|
1261
|
+
testing = request["testing"]
|
|
1262
|
+
|
|
1263
|
+
if not validate_service(service_command):
|
|
1264
|
+
error = "unknown service"
|
|
1265
|
+
|
|
1266
|
+
if service_command in self.services:
|
|
1267
|
+
processes = self.services[service_command]
|
|
1268
|
+
if all(process.poll() is not None for process in processes):
|
|
1269
|
+
self.services.pop(service_command)
|
|
1270
|
+
error = None
|
|
1271
|
+
else:
|
|
1272
|
+
self.log.info(f"Service {service_command} already running")
|
|
1273
|
+
already_running = True
|
|
1274
|
+
elif len(self.connections.get(service_command, [])) > 0:
|
|
1275
|
+
# If the service was started manually (not launched by the daemon), we should
|
|
1276
|
+
# have a connection to it.
|
|
1277
|
+
self.log.info(f"Service {service_command} already registered")
|
|
1278
|
+
already_running = True
|
|
1279
|
+
|
|
1280
|
+
if already_running:
|
|
1281
|
+
success = True
|
|
1282
|
+
elif error is None:
|
|
1283
|
+
try:
|
|
1284
|
+
exe_command = service_command
|
|
1285
|
+
if testing is True:
|
|
1286
|
+
exe_command = f"{service_command} --testing=true"
|
|
1287
|
+
process, _pid_path = launch_service(self.root_path, exe_command)
|
|
1288
|
+
self.services[service_command] = [process]
|
|
1289
|
+
success = True
|
|
1290
|
+
except (subprocess.SubprocessError, OSError):
|
|
1291
|
+
log.exception(f"problem starting {service_command}")
|
|
1292
|
+
error = "start failed"
|
|
1293
|
+
|
|
1294
|
+
response = {"success": success, "service": service_command, "error": error}
|
|
1295
|
+
return response
|
|
1296
|
+
|
|
1297
|
+
async def stop_service(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1298
|
+
service_name = request["service"]
|
|
1299
|
+
result = await kill_service(self.root_path, self.services, service_name)
|
|
1300
|
+
response = {"success": result, "service_name": service_name}
|
|
1301
|
+
return response
|
|
1302
|
+
|
|
1303
|
+
def is_service_running(self, service_name: str) -> bool:
|
|
1304
|
+
processes: list[subprocess.Popen]
|
|
1305
|
+
if service_name == service_plotter:
|
|
1306
|
+
processes = self.services.get(service_name, [])
|
|
1307
|
+
is_running = len(processes) > 0
|
|
1308
|
+
else:
|
|
1309
|
+
processes = self.services.get(service_name, [])
|
|
1310
|
+
is_running = any(process.poll() is None for process in processes)
|
|
1311
|
+
if not is_running:
|
|
1312
|
+
# Check if we have a connection to the requested service. This might be the
|
|
1313
|
+
# case if the service was started manually (i.e. not started by the daemon).
|
|
1314
|
+
service_connections = self.connections.get(service_name)
|
|
1315
|
+
if service_connections is not None:
|
|
1316
|
+
is_running = len(service_connections) > 0
|
|
1317
|
+
return is_running
|
|
1318
|
+
|
|
1319
|
+
async def running_services_command(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1320
|
+
return await self.running_services()
|
|
1321
|
+
|
|
1322
|
+
async def running_services(self) -> dict[str, Any]:
|
|
1323
|
+
services = list({*self.services.keys(), *self.connections.keys()})
|
|
1324
|
+
running_services = [service_name for service_name in services if self.is_service_running(service_name)]
|
|
1325
|
+
|
|
1326
|
+
return {"success": True, "running_services": running_services}
|
|
1327
|
+
|
|
1328
|
+
async def is_running_command(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1329
|
+
return await self.is_running(request=request)
|
|
1330
|
+
|
|
1331
|
+
async def is_running(self, request: dict[str, Any]) -> dict[str, Any]:
|
|
1332
|
+
service_name = request["service"]
|
|
1333
|
+
is_running = self.is_service_running(service_name)
|
|
1334
|
+
return {"success": True, "service_name": service_name, "is_running": is_running}
|
|
1335
|
+
|
|
1336
|
+
async def exit(self) -> None:
|
|
1337
|
+
if self.webserver is not None:
|
|
1338
|
+
self.webserver.close()
|
|
1339
|
+
await self.webserver.await_closed()
|
|
1340
|
+
log.info("chia daemon exiting")
|
|
1341
|
+
|
|
1342
|
+
async def register_service(self, websocket: WebSocketResponse, request: dict[str, Any]) -> dict[str, Any]:
|
|
1343
|
+
self.log.info(f"Register service {request}")
|
|
1344
|
+
service = request.get("service")
|
|
1345
|
+
if service is None:
|
|
1346
|
+
self.log.error("Service Name missing from request to 'register_service'")
|
|
1347
|
+
return {"success": False}
|
|
1348
|
+
if service not in self.connections:
|
|
1349
|
+
self.connections[service] = set()
|
|
1350
|
+
self.connections[service].add(websocket)
|
|
1351
|
+
|
|
1352
|
+
response: dict[str, Any] = {"success": True}
|
|
1353
|
+
if service == service_plotter:
|
|
1354
|
+
response = {
|
|
1355
|
+
"success": True,
|
|
1356
|
+
"service": service,
|
|
1357
|
+
"queue": self.extract_plot_queue(),
|
|
1358
|
+
}
|
|
1359
|
+
else:
|
|
1360
|
+
if self.ping_job is None:
|
|
1361
|
+
self.ping_job = create_referenced_task(self.ping_task())
|
|
1362
|
+
self.log.info(f"registered for service {service}")
|
|
1363
|
+
log.info(f"{response}")
|
|
1364
|
+
return response
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
def daemon_launch_lock_path(root_path: Path) -> Path:
|
|
1368
|
+
"""
|
|
1369
|
+
A path to a file that is lock when a daemon is launching but not yet started.
|
|
1370
|
+
This prevents multiple instances from launching.
|
|
1371
|
+
"""
|
|
1372
|
+
return service_launch_lock_path(root_path, "daemon")
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
def service_launch_lock_path(root_path: Path, service: str) -> Path:
|
|
1376
|
+
"""
|
|
1377
|
+
A path that is locked when a service is running.
|
|
1378
|
+
"""
|
|
1379
|
+
service_name = service.replace(" ", "-").replace("/", "-")
|
|
1380
|
+
return root_path / "run" / service_name
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
def pid_path_for_service(root_path: Path, service: str, id: str = "") -> Path:
|
|
1384
|
+
"""
|
|
1385
|
+
Generate a path for a PID file for the given service name.
|
|
1386
|
+
"""
|
|
1387
|
+
pid_name = service.replace(" ", "-").replace("/", "-")
|
|
1388
|
+
return root_path / "run" / f"{pid_name}{id}.pid"
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
def plotter_log_path(root_path: Path, id: str):
|
|
1392
|
+
return root_path / "plotter" / f"plotter_log_{id}.txt"
|
|
1393
|
+
|
|
1394
|
+
|
|
1395
|
+
def launch_plotter(
|
|
1396
|
+
root_path: Path, service_name: str, service_array: list[str], id: str
|
|
1397
|
+
) -> tuple[subprocess.Popen, Path]:
|
|
1398
|
+
# we need to pass on the possibly altered CHIA_ROOT
|
|
1399
|
+
os.environ["CHIA_ROOT"] = str(root_path)
|
|
1400
|
+
service_executable = executable_for_service(service_array[0])
|
|
1401
|
+
|
|
1402
|
+
# Swap service name with name of executable
|
|
1403
|
+
service_array[0] = service_executable
|
|
1404
|
+
startupinfo = None
|
|
1405
|
+
creationflags = 0
|
|
1406
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
1407
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
1408
|
+
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
1409
|
+
# If the current process group is used, CTRL_C_EVENT will kill the parent and everyone in the group!
|
|
1410
|
+
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
1411
|
+
|
|
1412
|
+
plotter_path = plotter_log_path(root_path, id)
|
|
1413
|
+
|
|
1414
|
+
if plotter_path.parent.exists():
|
|
1415
|
+
if plotter_path.exists():
|
|
1416
|
+
plotter_path.unlink()
|
|
1417
|
+
else:
|
|
1418
|
+
plotter_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1419
|
+
outfile = open(plotter_path.resolve(), "w")
|
|
1420
|
+
log.info(f"Service array: {service_array}") # lgtm [py/clear-text-logging-sensitive-data]
|
|
1421
|
+
process = subprocess.Popen(
|
|
1422
|
+
service_array,
|
|
1423
|
+
shell=False,
|
|
1424
|
+
stderr=outfile,
|
|
1425
|
+
stdout=outfile,
|
|
1426
|
+
startupinfo=startupinfo,
|
|
1427
|
+
creationflags=creationflags,
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
pid_path = pid_path_for_service(root_path, service_name, id)
|
|
1431
|
+
try:
|
|
1432
|
+
pid_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1433
|
+
with open(pid_path, "w") as f:
|
|
1434
|
+
f.write(f"{process.pid}\n")
|
|
1435
|
+
except Exception:
|
|
1436
|
+
pass
|
|
1437
|
+
return process, pid_path
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
def launch_service(root_path: Path, service_command) -> tuple[subprocess.Popen, Path]:
|
|
1441
|
+
"""
|
|
1442
|
+
Launch a child process.
|
|
1443
|
+
"""
|
|
1444
|
+
# set up CHIA_ROOT
|
|
1445
|
+
# invoke correct script
|
|
1446
|
+
# save away PID
|
|
1447
|
+
|
|
1448
|
+
# we need to pass on the possibly altered CHIA_ROOT
|
|
1449
|
+
os.environ["CHIA_ROOT"] = str(root_path)
|
|
1450
|
+
|
|
1451
|
+
# Insert proper e
|
|
1452
|
+
service_array = service_command.split()
|
|
1453
|
+
service_executable = executable_for_service(service_array[0])
|
|
1454
|
+
service_array[0] = service_executable
|
|
1455
|
+
|
|
1456
|
+
startupinfo = None
|
|
1457
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
1458
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
1459
|
+
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
1460
|
+
|
|
1461
|
+
log.debug(f"Launching service {service_array} with CHIA_ROOT: {os.environ['CHIA_ROOT']}")
|
|
1462
|
+
|
|
1463
|
+
# CREATE_NEW_PROCESS_GROUP allows graceful shutdown on windows, by CTRL_BREAK_EVENT signal
|
|
1464
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
1465
|
+
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
1466
|
+
else:
|
|
1467
|
+
creationflags = 0
|
|
1468
|
+
environ_copy = os.environ.copy()
|
|
1469
|
+
process = subprocess.Popen(
|
|
1470
|
+
service_array, shell=False, startupinfo=startupinfo, creationflags=creationflags, env=environ_copy
|
|
1471
|
+
)
|
|
1472
|
+
|
|
1473
|
+
pid_path = pid_path_for_service(root_path, service_command)
|
|
1474
|
+
try:
|
|
1475
|
+
pid_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1476
|
+
with open(pid_path, "w") as f:
|
|
1477
|
+
f.write(f"{process.pid}\n")
|
|
1478
|
+
except Exception:
|
|
1479
|
+
pass
|
|
1480
|
+
return process, pid_path
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
async def kill_processes(
|
|
1484
|
+
processes: list[subprocess.Popen],
|
|
1485
|
+
root_path: Path,
|
|
1486
|
+
service_name: str,
|
|
1487
|
+
id: str,
|
|
1488
|
+
delay_before_kill: int = 15,
|
|
1489
|
+
) -> bool:
|
|
1490
|
+
pid_path = pid_path_for_service(root_path, service_name, id)
|
|
1491
|
+
|
|
1492
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
1493
|
+
log.info("sending CTRL_BREAK_EVENT signal to %s", service_name)
|
|
1494
|
+
|
|
1495
|
+
for process in processes:
|
|
1496
|
+
kill(process.pid, signal.SIGBREAK)
|
|
1497
|
+
else:
|
|
1498
|
+
log.info("sending term signal to %s", service_name)
|
|
1499
|
+
for process in processes:
|
|
1500
|
+
process.terminate()
|
|
1501
|
+
|
|
1502
|
+
count: float = 0
|
|
1503
|
+
while count < delay_before_kill:
|
|
1504
|
+
if all(process.poll() is not None for process in processes):
|
|
1505
|
+
break
|
|
1506
|
+
await asyncio.sleep(0.5)
|
|
1507
|
+
count += 0.5
|
|
1508
|
+
else:
|
|
1509
|
+
for process in processes:
|
|
1510
|
+
process.kill()
|
|
1511
|
+
log.info("sending kill signal to %s", service_name)
|
|
1512
|
+
for process in processes:
|
|
1513
|
+
r = process.wait()
|
|
1514
|
+
log.info("process %s returned %d", service_name, r)
|
|
1515
|
+
|
|
1516
|
+
try:
|
|
1517
|
+
pid_path_killed = pid_path.with_suffix(".pid-killed")
|
|
1518
|
+
if pid_path_killed.exists():
|
|
1519
|
+
pid_path_killed.unlink()
|
|
1520
|
+
os.rename(pid_path, pid_path_killed)
|
|
1521
|
+
except Exception:
|
|
1522
|
+
pass
|
|
1523
|
+
|
|
1524
|
+
return True
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
async def kill_service(
|
|
1528
|
+
root_path: Path, services: dict[str, list[subprocess.Popen]], service_name: str, delay_before_kill: int = 15
|
|
1529
|
+
) -> bool:
|
|
1530
|
+
processes = services.get(service_name)
|
|
1531
|
+
if processes is None:
|
|
1532
|
+
return False
|
|
1533
|
+
del services[service_name]
|
|
1534
|
+
result = await kill_processes(processes, root_path, service_name, "", delay_before_kill)
|
|
1535
|
+
return result
|
|
1536
|
+
|
|
1537
|
+
|
|
1538
|
+
def is_running(services: dict[str, subprocess.Popen], service_name: str) -> bool:
|
|
1539
|
+
process = services.get(service_name)
|
|
1540
|
+
return process is not None and process.poll() is None
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
async def async_run_daemon(root_path: Path, wait_for_unlock: bool = False) -> int:
|
|
1544
|
+
# When wait_for_unlock is true, we want to skip the check_keys() call in chia_init
|
|
1545
|
+
# since it might be necessary to wait for the GUI to unlock the keyring first.
|
|
1546
|
+
chia_init(root_path, should_check_keys=(not wait_for_unlock))
|
|
1547
|
+
config = load_config(root_path, "config.yaml")
|
|
1548
|
+
setproctitle("chia_daemon")
|
|
1549
|
+
initialize_service_logging("daemon", config, root_path=root_path)
|
|
1550
|
+
crt_path = root_path / config["daemon_ssl"]["private_crt"]
|
|
1551
|
+
key_path = root_path / config["daemon_ssl"]["private_key"]
|
|
1552
|
+
ca_crt_path = root_path / config["private_ssl_ca"]["crt"]
|
|
1553
|
+
ca_key_path = root_path / config["private_ssl_ca"]["key"]
|
|
1554
|
+
sys.stdout.flush()
|
|
1555
|
+
try:
|
|
1556
|
+
with Lockfile.create(daemon_launch_lock_path(root_path), timeout=1):
|
|
1557
|
+
log.info(f"chia-blockchain version: {chia_short_version()}")
|
|
1558
|
+
|
|
1559
|
+
beta_metrics = None
|
|
1560
|
+
if config.get("beta", {}).get("enabled", False):
|
|
1561
|
+
from chia.util.beta_metrics import BetaMetricsLogger
|
|
1562
|
+
|
|
1563
|
+
beta_metrics = BetaMetricsLogger(root_path)
|
|
1564
|
+
beta_metrics.start_logging()
|
|
1565
|
+
|
|
1566
|
+
ws_server = WebSocketServer(
|
|
1567
|
+
root_path,
|
|
1568
|
+
ca_crt_path,
|
|
1569
|
+
ca_key_path,
|
|
1570
|
+
crt_path,
|
|
1571
|
+
key_path,
|
|
1572
|
+
run_check_keys_on_unlock=wait_for_unlock,
|
|
1573
|
+
)
|
|
1574
|
+
async with SignalHandlers.manage() as signal_handlers:
|
|
1575
|
+
await ws_server.setup_process_global_state(signal_handlers=signal_handlers)
|
|
1576
|
+
async with ws_server.run():
|
|
1577
|
+
await ws_server.shutdown_event.wait()
|
|
1578
|
+
|
|
1579
|
+
if beta_metrics is not None:
|
|
1580
|
+
await beta_metrics.stop_logging()
|
|
1581
|
+
|
|
1582
|
+
log.info("Daemon WebSocketServer closed")
|
|
1583
|
+
sys.stdout.close()
|
|
1584
|
+
return 0
|
|
1585
|
+
except LockfileError:
|
|
1586
|
+
print("daemon: already launching")
|
|
1587
|
+
return 2
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
def run_daemon(root_path: Path, wait_for_unlock: bool = False) -> int:
|
|
1591
|
+
result = asyncio.run(async_run_daemon(root_path, wait_for_unlock))
|
|
1592
|
+
return result
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
def main() -> int:
|
|
1596
|
+
from chia.util.default_root import resolve_root_path
|
|
1597
|
+
from chia.util.keychain import Keychain
|
|
1598
|
+
|
|
1599
|
+
root_path = resolve_root_path(override=None)
|
|
1600
|
+
|
|
1601
|
+
wait_for_unlock = "--wait-for-unlock" in sys.argv[1:] and Keychain.is_keyring_locked()
|
|
1602
|
+
return run_daemon(root_path, wait_for_unlock)
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
if __name__ == "__main__":
|
|
1606
|
+
main()
|