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.
Files changed (1042) hide show
  1. chia/__init__.py +10 -0
  2. chia/__main__.py +5 -0
  3. chia/_tests/README.md +53 -0
  4. chia/_tests/__init__.py +0 -0
  5. chia/_tests/blockchain/__init__.py +0 -0
  6. chia/_tests/blockchain/blockchain_test_utils.py +195 -0
  7. chia/_tests/blockchain/config.py +4 -0
  8. chia/_tests/blockchain/test_augmented_chain.py +145 -0
  9. chia/_tests/blockchain/test_blockchain.py +4202 -0
  10. chia/_tests/blockchain/test_blockchain_transactions.py +1031 -0
  11. chia/_tests/blockchain/test_build_chains.py +59 -0
  12. chia/_tests/blockchain/test_get_block_generator.py +72 -0
  13. chia/_tests/blockchain/test_lookup_fork_chain.py +194 -0
  14. chia/_tests/build-init-files.py +92 -0
  15. chia/_tests/build-job-matrix.py +204 -0
  16. chia/_tests/check_pytest_monitor_output.py +34 -0
  17. chia/_tests/check_sql_statements.py +72 -0
  18. chia/_tests/chia-start-sim +42 -0
  19. chia/_tests/clvm/__init__.py +0 -0
  20. chia/_tests/clvm/benchmark_costs.py +23 -0
  21. chia/_tests/clvm/coin_store.py +149 -0
  22. chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
  23. chia/_tests/clvm/test_clvm_step.py +37 -0
  24. chia/_tests/clvm/test_condition_codes.py +13 -0
  25. chia/_tests/clvm/test_curry_and_treehash.py +55 -0
  26. chia/_tests/clvm/test_message_conditions.py +184 -0
  27. chia/_tests/clvm/test_program.py +150 -0
  28. chia/_tests/clvm/test_puzzle_compression.py +143 -0
  29. chia/_tests/clvm/test_puzzle_drivers.py +45 -0
  30. chia/_tests/clvm/test_puzzles.py +242 -0
  31. chia/_tests/clvm/test_singletons.py +540 -0
  32. chia/_tests/clvm/test_spend_sim.py +181 -0
  33. chia/_tests/cmds/__init__.py +0 -0
  34. chia/_tests/cmds/cmd_test_utils.py +469 -0
  35. chia/_tests/cmds/config.py +3 -0
  36. chia/_tests/cmds/conftest.py +23 -0
  37. chia/_tests/cmds/test_click_types.py +200 -0
  38. chia/_tests/cmds/test_cmd_framework.py +620 -0
  39. chia/_tests/cmds/test_cmds_util.py +97 -0
  40. chia/_tests/cmds/test_daemon.py +92 -0
  41. chia/_tests/cmds/test_dev_gh.py +131 -0
  42. chia/_tests/cmds/test_farm_cmd.py +66 -0
  43. chia/_tests/cmds/test_show.py +116 -0
  44. chia/_tests/cmds/test_sim.py +207 -0
  45. chia/_tests/cmds/test_timelock_args.py +75 -0
  46. chia/_tests/cmds/test_tx_config_args.py +154 -0
  47. chia/_tests/cmds/testing_classes.py +59 -0
  48. chia/_tests/cmds/wallet/__init__.py +0 -0
  49. chia/_tests/cmds/wallet/test_consts.py +47 -0
  50. chia/_tests/cmds/wallet/test_dao.py +565 -0
  51. chia/_tests/cmds/wallet/test_did.py +403 -0
  52. chia/_tests/cmds/wallet/test_nft.py +471 -0
  53. chia/_tests/cmds/wallet/test_notifications.py +124 -0
  54. chia/_tests/cmds/wallet/test_offer.toffer +1 -0
  55. chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
  56. chia/_tests/cmds/wallet/test_vcs.py +400 -0
  57. chia/_tests/cmds/wallet/test_wallet.py +1125 -0
  58. chia/_tests/cmds/wallet/test_wallet_check.py +109 -0
  59. chia/_tests/conftest.py +1419 -0
  60. chia/_tests/connection_utils.py +125 -0
  61. chia/_tests/core/__init__.py +0 -0
  62. chia/_tests/core/cmds/__init__.py +0 -0
  63. chia/_tests/core/cmds/test_beta.py +382 -0
  64. chia/_tests/core/cmds/test_keys.py +1734 -0
  65. chia/_tests/core/cmds/test_wallet.py +126 -0
  66. chia/_tests/core/config.py +3 -0
  67. chia/_tests/core/consensus/__init__.py +0 -0
  68. chia/_tests/core/consensus/test_block_creation.py +54 -0
  69. chia/_tests/core/consensus/test_pot_iterations.py +117 -0
  70. chia/_tests/core/custom_types/__init__.py +0 -0
  71. chia/_tests/core/custom_types/test_coin.py +107 -0
  72. chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
  73. chia/_tests/core/custom_types/test_spend_bundle.py +70 -0
  74. chia/_tests/core/daemon/__init__.py +0 -0
  75. chia/_tests/core/daemon/config.py +4 -0
  76. chia/_tests/core/daemon/test_daemon.py +2128 -0
  77. chia/_tests/core/daemon/test_daemon_register.py +109 -0
  78. chia/_tests/core/daemon/test_keychain_proxy.py +101 -0
  79. chia/_tests/core/data_layer/__init__.py +0 -0
  80. chia/_tests/core/data_layer/config.py +5 -0
  81. chia/_tests/core/data_layer/conftest.py +106 -0
  82. chia/_tests/core/data_layer/test_data_cli.py +56 -0
  83. chia/_tests/core/data_layer/test_data_layer.py +83 -0
  84. chia/_tests/core/data_layer/test_data_layer_util.py +218 -0
  85. chia/_tests/core/data_layer/test_data_rpc.py +3847 -0
  86. chia/_tests/core/data_layer/test_data_store.py +2424 -0
  87. chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
  88. chia/_tests/core/data_layer/test_plugin.py +91 -0
  89. chia/_tests/core/data_layer/util.py +233 -0
  90. chia/_tests/core/farmer/__init__.py +0 -0
  91. chia/_tests/core/farmer/config.py +3 -0
  92. chia/_tests/core/farmer/test_farmer_api.py +103 -0
  93. chia/_tests/core/full_node/__init__.py +0 -0
  94. chia/_tests/core/full_node/config.py +4 -0
  95. chia/_tests/core/full_node/dos/__init__.py +0 -0
  96. chia/_tests/core/full_node/dos/config.py +3 -0
  97. chia/_tests/core/full_node/full_sync/__init__.py +0 -0
  98. chia/_tests/core/full_node/full_sync/config.py +4 -0
  99. chia/_tests/core/full_node/full_sync/test_full_sync.py +443 -0
  100. chia/_tests/core/full_node/ram_db.py +27 -0
  101. chia/_tests/core/full_node/stores/__init__.py +0 -0
  102. chia/_tests/core/full_node/stores/config.py +4 -0
  103. chia/_tests/core/full_node/stores/test_block_store.py +590 -0
  104. chia/_tests/core/full_node/stores/test_coin_store.py +897 -0
  105. chia/_tests/core/full_node/stores/test_full_node_store.py +1219 -0
  106. chia/_tests/core/full_node/stores/test_hint_store.py +229 -0
  107. chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
  108. chia/_tests/core/full_node/test_address_manager.py +588 -0
  109. chia/_tests/core/full_node/test_block_height_map.py +556 -0
  110. chia/_tests/core/full_node/test_conditions.py +556 -0
  111. chia/_tests/core/full_node/test_full_node.py +2700 -0
  112. chia/_tests/core/full_node/test_generator_tools.py +82 -0
  113. chia/_tests/core/full_node/test_hint_management.py +104 -0
  114. chia/_tests/core/full_node/test_node_load.py +34 -0
  115. chia/_tests/core/full_node/test_performance.py +179 -0
  116. chia/_tests/core/full_node/test_subscriptions.py +492 -0
  117. chia/_tests/core/full_node/test_transactions.py +203 -0
  118. chia/_tests/core/full_node/test_tx_processing_queue.py +155 -0
  119. chia/_tests/core/large_block.py +2388 -0
  120. chia/_tests/core/make_block_generator.py +70 -0
  121. chia/_tests/core/mempool/__init__.py +0 -0
  122. chia/_tests/core/mempool/config.py +4 -0
  123. chia/_tests/core/mempool/test_mempool.py +3255 -0
  124. chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
  125. chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
  126. chia/_tests/core/mempool/test_mempool_item_queries.py +190 -0
  127. chia/_tests/core/mempool/test_mempool_manager.py +2084 -0
  128. chia/_tests/core/mempool/test_mempool_performance.py +64 -0
  129. chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
  130. chia/_tests/core/node_height.py +28 -0
  131. chia/_tests/core/server/__init__.py +0 -0
  132. chia/_tests/core/server/config.py +3 -0
  133. chia/_tests/core/server/flood.py +84 -0
  134. chia/_tests/core/server/serve.py +135 -0
  135. chia/_tests/core/server/test_api_protocol.py +21 -0
  136. chia/_tests/core/server/test_capabilities.py +66 -0
  137. chia/_tests/core/server/test_dos.py +319 -0
  138. chia/_tests/core/server/test_event_loop.py +109 -0
  139. chia/_tests/core/server/test_loop.py +294 -0
  140. chia/_tests/core/server/test_node_discovery.py +73 -0
  141. chia/_tests/core/server/test_rate_limits.py +482 -0
  142. chia/_tests/core/server/test_server.py +226 -0
  143. chia/_tests/core/server/test_upnp.py +8 -0
  144. chia/_tests/core/services/__init__.py +0 -0
  145. chia/_tests/core/services/config.py +3 -0
  146. chia/_tests/core/services/test_services.py +188 -0
  147. chia/_tests/core/ssl/__init__.py +0 -0
  148. chia/_tests/core/ssl/config.py +3 -0
  149. chia/_tests/core/ssl/test_ssl.py +202 -0
  150. chia/_tests/core/test_coins.py +33 -0
  151. chia/_tests/core/test_cost_calculation.py +313 -0
  152. chia/_tests/core/test_crawler.py +175 -0
  153. chia/_tests/core/test_crawler_rpc.py +53 -0
  154. chia/_tests/core/test_daemon_rpc.py +24 -0
  155. chia/_tests/core/test_db_conversion.py +130 -0
  156. chia/_tests/core/test_db_validation.py +162 -0
  157. chia/_tests/core/test_farmer_harvester_rpc.py +505 -0
  158. chia/_tests/core/test_filter.py +35 -0
  159. chia/_tests/core/test_full_node_rpc.py +768 -0
  160. chia/_tests/core/test_merkle_set.py +343 -0
  161. chia/_tests/core/test_program.py +47 -0
  162. chia/_tests/core/test_rpc_util.py +86 -0
  163. chia/_tests/core/test_seeder.py +420 -0
  164. chia/_tests/core/test_setproctitle.py +13 -0
  165. chia/_tests/core/util/__init__.py +0 -0
  166. chia/_tests/core/util/config.py +4 -0
  167. chia/_tests/core/util/test_block_cache.py +44 -0
  168. chia/_tests/core/util/test_cached_bls.py +57 -0
  169. chia/_tests/core/util/test_config.py +337 -0
  170. chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
  171. chia/_tests/core/util/test_files.py +391 -0
  172. chia/_tests/core/util/test_jsonify.py +146 -0
  173. chia/_tests/core/util/test_keychain.py +522 -0
  174. chia/_tests/core/util/test_keyring_wrapper.py +491 -0
  175. chia/_tests/core/util/test_lockfile.py +380 -0
  176. chia/_tests/core/util/test_log_exceptions.py +187 -0
  177. chia/_tests/core/util/test_lru_cache.py +56 -0
  178. chia/_tests/core/util/test_significant_bits.py +40 -0
  179. chia/_tests/core/util/test_streamable.py +883 -0
  180. chia/_tests/db/__init__.py +0 -0
  181. chia/_tests/db/test_db_wrapper.py +566 -0
  182. chia/_tests/environments/__init__.py +0 -0
  183. chia/_tests/environments/common.py +35 -0
  184. chia/_tests/environments/full_node.py +47 -0
  185. chia/_tests/environments/wallet.py +429 -0
  186. chia/_tests/ether.py +19 -0
  187. chia/_tests/farmer_harvester/__init__.py +0 -0
  188. chia/_tests/farmer_harvester/config.py +3 -0
  189. chia/_tests/farmer_harvester/test_farmer.py +1264 -0
  190. chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
  191. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +131 -0
  192. chia/_tests/farmer_harvester/test_third_party_harvesters.py +528 -0
  193. chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
  194. chia/_tests/fee_estimation/__init__.py +0 -0
  195. chia/_tests/fee_estimation/config.py +3 -0
  196. chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
  197. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
  198. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +144 -0
  199. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
  200. chia/_tests/generator/__init__.py +0 -0
  201. chia/_tests/generator/puzzles/__init__.py +0 -0
  202. chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
  203. chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
  204. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
  205. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
  206. chia/_tests/generator/test_compression.py +201 -0
  207. chia/_tests/generator/test_generator_types.py +44 -0
  208. chia/_tests/generator/test_rom.py +180 -0
  209. chia/_tests/plot_sync/__init__.py +0 -0
  210. chia/_tests/plot_sync/config.py +3 -0
  211. chia/_tests/plot_sync/test_delta.py +101 -0
  212. chia/_tests/plot_sync/test_plot_sync.py +618 -0
  213. chia/_tests/plot_sync/test_receiver.py +451 -0
  214. chia/_tests/plot_sync/test_sender.py +116 -0
  215. chia/_tests/plot_sync/test_sync_simulated.py +451 -0
  216. chia/_tests/plot_sync/util.py +68 -0
  217. chia/_tests/plotting/__init__.py +0 -0
  218. chia/_tests/plotting/config.py +3 -0
  219. chia/_tests/plotting/test_plot_manager.py +781 -0
  220. chia/_tests/plotting/util.py +12 -0
  221. chia/_tests/pools/__init__.py +0 -0
  222. chia/_tests/pools/config.py +5 -0
  223. chia/_tests/pools/test_pool_cli_parsing.py +128 -0
  224. chia/_tests/pools/test_pool_cmdline.py +1001 -0
  225. chia/_tests/pools/test_pool_config.py +42 -0
  226. chia/_tests/pools/test_pool_puzzles_lifecycle.py +397 -0
  227. chia/_tests/pools/test_pool_rpc.py +1123 -0
  228. chia/_tests/pools/test_pool_wallet.py +205 -0
  229. chia/_tests/pools/test_wallet_pool_store.py +161 -0
  230. chia/_tests/process_junit.py +348 -0
  231. chia/_tests/rpc/__init__.py +0 -0
  232. chia/_tests/rpc/test_rpc_client.py +138 -0
  233. chia/_tests/rpc/test_rpc_server.py +183 -0
  234. chia/_tests/simulation/__init__.py +0 -0
  235. chia/_tests/simulation/config.py +6 -0
  236. chia/_tests/simulation/test_simulation.py +501 -0
  237. chia/_tests/simulation/test_simulator.py +232 -0
  238. chia/_tests/simulation/test_start_simulator.py +107 -0
  239. chia/_tests/testconfig.py +13 -0
  240. chia/_tests/timelord/__init__.py +0 -0
  241. chia/_tests/timelord/config.py +3 -0
  242. chia/_tests/timelord/test_new_peak.py +437 -0
  243. chia/_tests/timelord/test_timelord.py +11 -0
  244. chia/_tests/tools/1315537.json +170 -0
  245. chia/_tests/tools/1315544.json +160 -0
  246. chia/_tests/tools/1315630.json +150 -0
  247. chia/_tests/tools/300000.json +105 -0
  248. chia/_tests/tools/442734.json +140 -0
  249. chia/_tests/tools/466212.json +130 -0
  250. chia/_tests/tools/__init__.py +0 -0
  251. chia/_tests/tools/config.py +5 -0
  252. chia/_tests/tools/test-blockchain-db.sqlite +0 -0
  253. chia/_tests/tools/test_full_sync.py +30 -0
  254. chia/_tests/tools/test_legacy_keyring.py +82 -0
  255. chia/_tests/tools/test_run_block.py +128 -0
  256. chia/_tests/tools/test_virtual_project.py +591 -0
  257. chia/_tests/util/__init__.py +0 -0
  258. chia/_tests/util/benchmark_cost.py +170 -0
  259. chia/_tests/util/benchmarks.py +153 -0
  260. chia/_tests/util/bip39_test_vectors.json +148 -0
  261. chia/_tests/util/blockchain.py +134 -0
  262. chia/_tests/util/blockchain_mock.py +132 -0
  263. chia/_tests/util/build_network_protocol_files.py +302 -0
  264. chia/_tests/util/clvm_generator.bin +0 -0
  265. chia/_tests/util/config.py +3 -0
  266. chia/_tests/util/constants.py +20 -0
  267. chia/_tests/util/db_connection.py +37 -0
  268. chia/_tests/util/full_sync.py +253 -0
  269. chia/_tests/util/gen_ssl_certs.py +114 -0
  270. chia/_tests/util/generator_tools_testing.py +45 -0
  271. chia/_tests/util/get_name_puzzle_conditions.py +52 -0
  272. chia/_tests/util/key_tool.py +36 -0
  273. chia/_tests/util/misc.py +675 -0
  274. chia/_tests/util/network_protocol_data.py +1072 -0
  275. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  276. chia/_tests/util/protocol_messages_json.py +2701 -0
  277. chia/_tests/util/rpc.py +26 -0
  278. chia/_tests/util/run_block.py +163 -0
  279. chia/_tests/util/setup_nodes.py +481 -0
  280. chia/_tests/util/spend_sim.py +492 -0
  281. chia/_tests/util/split_managers.py +102 -0
  282. chia/_tests/util/temp_file.py +14 -0
  283. chia/_tests/util/test_action_scope.py +144 -0
  284. chia/_tests/util/test_async_pool.py +366 -0
  285. chia/_tests/util/test_build_job_matrix.py +42 -0
  286. chia/_tests/util/test_build_network_protocol_files.py +7 -0
  287. chia/_tests/util/test_chia_version.py +50 -0
  288. chia/_tests/util/test_collection.py +11 -0
  289. chia/_tests/util/test_condition_tools.py +229 -0
  290. chia/_tests/util/test_config.py +426 -0
  291. chia/_tests/util/test_dump_keyring.py +60 -0
  292. chia/_tests/util/test_errors.py +10 -0
  293. chia/_tests/util/test_full_block_utils.py +279 -0
  294. chia/_tests/util/test_installed.py +20 -0
  295. chia/_tests/util/test_limited_semaphore.py +53 -0
  296. chia/_tests/util/test_logging_filter.py +42 -0
  297. chia/_tests/util/test_misc.py +445 -0
  298. chia/_tests/util/test_network.py +73 -0
  299. chia/_tests/util/test_network_protocol_files.py +578 -0
  300. chia/_tests/util/test_network_protocol_json.py +267 -0
  301. chia/_tests/util/test_network_protocol_test.py +256 -0
  302. chia/_tests/util/test_paginator.py +71 -0
  303. chia/_tests/util/test_pprint.py +17 -0
  304. chia/_tests/util/test_priority_mutex.py +488 -0
  305. chia/_tests/util/test_recursive_replace.py +116 -0
  306. chia/_tests/util/test_replace_str_to_bytes.py +137 -0
  307. chia/_tests/util/test_service_groups.py +15 -0
  308. chia/_tests/util/test_ssl_check.py +31 -0
  309. chia/_tests/util/test_testnet_overrides.py +19 -0
  310. chia/_tests/util/test_tests_misc.py +38 -0
  311. chia/_tests/util/test_timing.py +37 -0
  312. chia/_tests/util/test_trusted_peer.py +51 -0
  313. chia/_tests/util/time_out_assert.py +191 -0
  314. chia/_tests/wallet/__init__.py +0 -0
  315. chia/_tests/wallet/cat_wallet/__init__.py +0 -0
  316. chia/_tests/wallet/cat_wallet/config.py +4 -0
  317. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
  318. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
  319. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1826 -0
  320. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
  321. chia/_tests/wallet/cat_wallet/test_trades.py +2600 -0
  322. chia/_tests/wallet/clawback/__init__.py +0 -0
  323. chia/_tests/wallet/clawback/config.py +3 -0
  324. chia/_tests/wallet/clawback/test_clawback_decorator.py +78 -0
  325. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
  326. chia/_tests/wallet/clawback/test_clawback_metadata.py +50 -0
  327. chia/_tests/wallet/config.py +4 -0
  328. chia/_tests/wallet/conftest.py +278 -0
  329. chia/_tests/wallet/dao_wallet/__init__.py +0 -0
  330. chia/_tests/wallet/dao_wallet/config.py +3 -0
  331. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1330 -0
  332. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
  333. chia/_tests/wallet/db_wallet/__init__.py +0 -0
  334. chia/_tests/wallet/db_wallet/config.py +3 -0
  335. chia/_tests/wallet/db_wallet/test_db_graftroot.py +141 -0
  336. chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
  337. chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
  338. chia/_tests/wallet/did_wallet/__init__.py +0 -0
  339. chia/_tests/wallet/did_wallet/config.py +4 -0
  340. chia/_tests/wallet/did_wallet/test_did.py +2284 -0
  341. chia/_tests/wallet/nft_wallet/__init__.py +0 -0
  342. chia/_tests/wallet/nft_wallet/config.py +4 -0
  343. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1493 -0
  344. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1024 -0
  345. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +375 -0
  346. chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
  347. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
  348. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2584 -0
  349. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
  350. chia/_tests/wallet/rpc/__init__.py +0 -0
  351. chia/_tests/wallet/rpc/config.py +4 -0
  352. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +285 -0
  353. chia/_tests/wallet/rpc/test_wallet_rpc.py +3153 -0
  354. chia/_tests/wallet/simple_sync/__init__.py +0 -0
  355. chia/_tests/wallet/simple_sync/config.py +3 -0
  356. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +718 -0
  357. chia/_tests/wallet/sync/__init__.py +0 -0
  358. chia/_tests/wallet/sync/config.py +4 -0
  359. chia/_tests/wallet/sync/test_wallet_sync.py +1692 -0
  360. chia/_tests/wallet/test_address_type.py +189 -0
  361. chia/_tests/wallet/test_bech32m.py +45 -0
  362. chia/_tests/wallet/test_clvm_streamable.py +244 -0
  363. chia/_tests/wallet/test_coin_management.py +354 -0
  364. chia/_tests/wallet/test_coin_selection.py +588 -0
  365. chia/_tests/wallet/test_conditions.py +400 -0
  366. chia/_tests/wallet/test_debug_spend_bundle.py +218 -0
  367. chia/_tests/wallet/test_new_wallet_protocol.py +1174 -0
  368. chia/_tests/wallet/test_nft_store.py +192 -0
  369. chia/_tests/wallet/test_notifications.py +196 -0
  370. chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
  371. chia/_tests/wallet/test_puzzle_store.py +132 -0
  372. chia/_tests/wallet/test_sign_coin_spends.py +159 -0
  373. chia/_tests/wallet/test_signer_protocol.py +947 -0
  374. chia/_tests/wallet/test_singleton.py +122 -0
  375. chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
  376. chia/_tests/wallet/test_singleton_store.py +152 -0
  377. chia/_tests/wallet/test_taproot.py +19 -0
  378. chia/_tests/wallet/test_transaction_store.py +945 -0
  379. chia/_tests/wallet/test_util.py +185 -0
  380. chia/_tests/wallet/test_wallet.py +2139 -0
  381. chia/_tests/wallet/test_wallet_action_scope.py +85 -0
  382. chia/_tests/wallet/test_wallet_blockchain.py +111 -0
  383. chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
  384. chia/_tests/wallet/test_wallet_interested_store.py +43 -0
  385. chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
  386. chia/_tests/wallet/test_wallet_node.py +780 -0
  387. chia/_tests/wallet/test_wallet_retry.py +95 -0
  388. chia/_tests/wallet/test_wallet_state_manager.py +259 -0
  389. chia/_tests/wallet/test_wallet_test_framework.py +275 -0
  390. chia/_tests/wallet/test_wallet_trade_store.py +218 -0
  391. chia/_tests/wallet/test_wallet_user_store.py +34 -0
  392. chia/_tests/wallet/test_wallet_utils.py +156 -0
  393. chia/_tests/wallet/vc_wallet/__init__.py +0 -0
  394. chia/_tests/wallet/vc_wallet/config.py +3 -0
  395. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
  396. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
  397. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +830 -0
  398. chia/_tests/wallet/wallet_block_tools.py +327 -0
  399. chia/_tests/weight_proof/__init__.py +0 -0
  400. chia/_tests/weight_proof/config.py +3 -0
  401. chia/_tests/weight_proof/test_weight_proof.py +528 -0
  402. chia/apis.py +19 -0
  403. chia/clvm/__init__.py +0 -0
  404. chia/cmds/__init__.py +0 -0
  405. chia/cmds/beta.py +184 -0
  406. chia/cmds/beta_funcs.py +137 -0
  407. chia/cmds/check_wallet_db.py +420 -0
  408. chia/cmds/chia.py +151 -0
  409. chia/cmds/cmd_classes.py +323 -0
  410. chia/cmds/cmd_helpers.py +242 -0
  411. chia/cmds/cmds_util.py +488 -0
  412. chia/cmds/coin_funcs.py +275 -0
  413. chia/cmds/coins.py +182 -0
  414. chia/cmds/completion.py +49 -0
  415. chia/cmds/configure.py +332 -0
  416. chia/cmds/dao.py +1064 -0
  417. chia/cmds/dao_funcs.py +598 -0
  418. chia/cmds/data.py +708 -0
  419. chia/cmds/data_funcs.py +385 -0
  420. chia/cmds/db.py +87 -0
  421. chia/cmds/db_backup_func.py +77 -0
  422. chia/cmds/db_upgrade_func.py +452 -0
  423. chia/cmds/db_validate_func.py +184 -0
  424. chia/cmds/dev.py +18 -0
  425. chia/cmds/farm.py +100 -0
  426. chia/cmds/farm_funcs.py +200 -0
  427. chia/cmds/gh.py +275 -0
  428. chia/cmds/init.py +63 -0
  429. chia/cmds/init_funcs.py +367 -0
  430. chia/cmds/installers.py +131 -0
  431. chia/cmds/keys.py +527 -0
  432. chia/cmds/keys_funcs.py +863 -0
  433. chia/cmds/netspace.py +50 -0
  434. chia/cmds/netspace_funcs.py +54 -0
  435. chia/cmds/options.py +32 -0
  436. chia/cmds/param_types.py +238 -0
  437. chia/cmds/passphrase.py +131 -0
  438. chia/cmds/passphrase_funcs.py +292 -0
  439. chia/cmds/peer.py +51 -0
  440. chia/cmds/peer_funcs.py +129 -0
  441. chia/cmds/plotnft.py +260 -0
  442. chia/cmds/plotnft_funcs.py +405 -0
  443. chia/cmds/plots.py +230 -0
  444. chia/cmds/plotters.py +18 -0
  445. chia/cmds/rpc.py +208 -0
  446. chia/cmds/show.py +72 -0
  447. chia/cmds/show_funcs.py +215 -0
  448. chia/cmds/signer.py +296 -0
  449. chia/cmds/sim.py +225 -0
  450. chia/cmds/sim_funcs.py +509 -0
  451. chia/cmds/start.py +24 -0
  452. chia/cmds/start_funcs.py +109 -0
  453. chia/cmds/stop.py +62 -0
  454. chia/cmds/units.py +9 -0
  455. chia/cmds/wallet.py +1901 -0
  456. chia/cmds/wallet_funcs.py +1874 -0
  457. chia/consensus/__init__.py +0 -0
  458. chia/consensus/block_body_validation.py +562 -0
  459. chia/consensus/block_creation.py +546 -0
  460. chia/consensus/block_header_validation.py +1059 -0
  461. chia/consensus/block_record.py +31 -0
  462. chia/consensus/block_rewards.py +53 -0
  463. chia/consensus/blockchain.py +1087 -0
  464. chia/consensus/blockchain_interface.py +56 -0
  465. chia/consensus/coinbase.py +30 -0
  466. chia/consensus/condition_costs.py +9 -0
  467. chia/consensus/constants.py +49 -0
  468. chia/consensus/cost_calculator.py +15 -0
  469. chia/consensus/default_constants.py +89 -0
  470. chia/consensus/deficit.py +55 -0
  471. chia/consensus/difficulty_adjustment.py +412 -0
  472. chia/consensus/find_fork_point.py +111 -0
  473. chia/consensus/full_block_to_block_record.py +167 -0
  474. chia/consensus/get_block_challenge.py +106 -0
  475. chia/consensus/get_block_generator.py +27 -0
  476. chia/consensus/make_sub_epoch_summary.py +210 -0
  477. chia/consensus/multiprocess_validation.py +268 -0
  478. chia/consensus/pos_quality.py +19 -0
  479. chia/consensus/pot_iterations.py +67 -0
  480. chia/consensus/puzzles/__init__.py +0 -0
  481. chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
  482. chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
  483. chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
  484. chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
  485. chia/consensus/vdf_info_computation.py +156 -0
  486. chia/daemon/__init__.py +0 -0
  487. chia/daemon/client.py +252 -0
  488. chia/daemon/keychain_proxy.py +502 -0
  489. chia/daemon/keychain_server.py +365 -0
  490. chia/daemon/server.py +1606 -0
  491. chia/daemon/windows_signal.py +56 -0
  492. chia/data_layer/__init__.py +0 -0
  493. chia/data_layer/data_layer.py +1291 -0
  494. chia/data_layer/data_layer_api.py +33 -0
  495. chia/data_layer/data_layer_errors.py +50 -0
  496. chia/data_layer/data_layer_server.py +170 -0
  497. chia/data_layer/data_layer_util.py +985 -0
  498. chia/data_layer/data_layer_wallet.py +1311 -0
  499. chia/data_layer/data_store.py +2267 -0
  500. chia/data_layer/dl_wallet_store.py +407 -0
  501. chia/data_layer/download_data.py +389 -0
  502. chia/data_layer/puzzles/__init__.py +0 -0
  503. chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
  504. chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
  505. chia/data_layer/s3_plugin_config.yml +33 -0
  506. chia/data_layer/s3_plugin_service.py +468 -0
  507. chia/data_layer/util/__init__.py +0 -0
  508. chia/data_layer/util/benchmark.py +107 -0
  509. chia/data_layer/util/plugin.py +40 -0
  510. chia/farmer/__init__.py +0 -0
  511. chia/farmer/farmer.py +923 -0
  512. chia/farmer/farmer_api.py +820 -0
  513. chia/full_node/__init__.py +0 -0
  514. chia/full_node/bitcoin_fee_estimator.py +85 -0
  515. chia/full_node/block_height_map.py +271 -0
  516. chia/full_node/block_store.py +576 -0
  517. chia/full_node/bundle_tools.py +19 -0
  518. chia/full_node/coin_store.py +647 -0
  519. chia/full_node/fee_estimate.py +54 -0
  520. chia/full_node/fee_estimate_store.py +24 -0
  521. chia/full_node/fee_estimation.py +92 -0
  522. chia/full_node/fee_estimator.py +90 -0
  523. chia/full_node/fee_estimator_constants.py +38 -0
  524. chia/full_node/fee_estimator_interface.py +42 -0
  525. chia/full_node/fee_history.py +25 -0
  526. chia/full_node/fee_tracker.py +564 -0
  527. chia/full_node/full_node.py +3327 -0
  528. chia/full_node/full_node_api.py +2025 -0
  529. chia/full_node/full_node_store.py +1033 -0
  530. chia/full_node/hint_management.py +56 -0
  531. chia/full_node/hint_store.py +93 -0
  532. chia/full_node/mempool.py +589 -0
  533. chia/full_node/mempool_check_conditions.py +146 -0
  534. chia/full_node/mempool_manager.py +853 -0
  535. chia/full_node/pending_tx_cache.py +112 -0
  536. chia/full_node/puzzles/__init__.py +0 -0
  537. chia/full_node/puzzles/block_program_zero.clsp +14 -0
  538. chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
  539. chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
  540. chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
  541. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
  542. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
  543. chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
  544. chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
  545. chia/full_node/signage_point.py +16 -0
  546. chia/full_node/subscriptions.py +247 -0
  547. chia/full_node/sync_store.py +146 -0
  548. chia/full_node/tx_processing_queue.py +78 -0
  549. chia/full_node/util/__init__.py +0 -0
  550. chia/full_node/weight_proof.py +1720 -0
  551. chia/harvester/__init__.py +0 -0
  552. chia/harvester/harvester.py +272 -0
  553. chia/harvester/harvester_api.py +380 -0
  554. chia/introducer/__init__.py +0 -0
  555. chia/introducer/introducer.py +122 -0
  556. chia/introducer/introducer_api.py +70 -0
  557. chia/legacy/__init__.py +0 -0
  558. chia/legacy/keyring.py +155 -0
  559. chia/plot_sync/__init__.py +0 -0
  560. chia/plot_sync/delta.py +61 -0
  561. chia/plot_sync/exceptions.py +56 -0
  562. chia/plot_sync/receiver.py +386 -0
  563. chia/plot_sync/sender.py +340 -0
  564. chia/plot_sync/util.py +43 -0
  565. chia/plotters/__init__.py +0 -0
  566. chia/plotters/bladebit.py +388 -0
  567. chia/plotters/chiapos.py +63 -0
  568. chia/plotters/madmax.py +224 -0
  569. chia/plotters/plotters.py +577 -0
  570. chia/plotters/plotters_util.py +133 -0
  571. chia/plotting/__init__.py +0 -0
  572. chia/plotting/cache.py +213 -0
  573. chia/plotting/check_plots.py +283 -0
  574. chia/plotting/create_plots.py +278 -0
  575. chia/plotting/manager.py +436 -0
  576. chia/plotting/util.py +336 -0
  577. chia/pools/__init__.py +0 -0
  578. chia/pools/pool_config.py +110 -0
  579. chia/pools/pool_puzzles.py +459 -0
  580. chia/pools/pool_wallet.py +933 -0
  581. chia/pools/pool_wallet_info.py +118 -0
  582. chia/pools/puzzles/__init__.py +0 -0
  583. chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
  584. chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
  585. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
  586. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
  587. chia/protocols/__init__.py +0 -0
  588. chia/protocols/farmer_protocol.py +102 -0
  589. chia/protocols/full_node_protocol.py +219 -0
  590. chia/protocols/harvester_protocol.py +216 -0
  591. chia/protocols/introducer_protocol.py +25 -0
  592. chia/protocols/pool_protocol.py +177 -0
  593. chia/protocols/protocol_message_types.py +139 -0
  594. chia/protocols/protocol_state_machine.py +87 -0
  595. chia/protocols/protocol_timing.py +8 -0
  596. chia/protocols/shared_protocol.py +86 -0
  597. chia/protocols/timelord_protocol.py +93 -0
  598. chia/protocols/wallet_protocol.py +401 -0
  599. chia/py.typed +0 -0
  600. chia/rpc/__init__.py +0 -0
  601. chia/rpc/crawler_rpc_api.py +80 -0
  602. chia/rpc/data_layer_rpc_api.py +644 -0
  603. chia/rpc/data_layer_rpc_client.py +188 -0
  604. chia/rpc/data_layer_rpc_util.py +58 -0
  605. chia/rpc/farmer_rpc_api.py +365 -0
  606. chia/rpc/farmer_rpc_client.py +86 -0
  607. chia/rpc/full_node_rpc_api.py +959 -0
  608. chia/rpc/full_node_rpc_client.py +292 -0
  609. chia/rpc/harvester_rpc_api.py +141 -0
  610. chia/rpc/harvester_rpc_client.py +54 -0
  611. chia/rpc/rpc_client.py +164 -0
  612. chia/rpc/rpc_server.py +521 -0
  613. chia/rpc/timelord_rpc_api.py +32 -0
  614. chia/rpc/util.py +93 -0
  615. chia/rpc/wallet_request_types.py +904 -0
  616. chia/rpc/wallet_rpc_api.py +4943 -0
  617. chia/rpc/wallet_rpc_client.py +1814 -0
  618. chia/seeder/__init__.py +0 -0
  619. chia/seeder/crawl_store.py +425 -0
  620. chia/seeder/crawler.py +410 -0
  621. chia/seeder/crawler_api.py +135 -0
  622. chia/seeder/dns_server.py +593 -0
  623. chia/seeder/peer_record.py +146 -0
  624. chia/seeder/start_crawler.py +92 -0
  625. chia/server/__init__.py +0 -0
  626. chia/server/address_manager.py +658 -0
  627. chia/server/address_manager_store.py +237 -0
  628. chia/server/api_protocol.py +116 -0
  629. chia/server/capabilities.py +24 -0
  630. chia/server/chia_policy.py +346 -0
  631. chia/server/introducer_peers.py +76 -0
  632. chia/server/node_discovery.py +714 -0
  633. chia/server/outbound_message.py +33 -0
  634. chia/server/rate_limit_numbers.py +214 -0
  635. chia/server/rate_limits.py +153 -0
  636. chia/server/server.py +741 -0
  637. chia/server/signal_handlers.py +120 -0
  638. chia/server/ssl_context.py +32 -0
  639. chia/server/start_data_layer.py +151 -0
  640. chia/server/start_farmer.py +98 -0
  641. chia/server/start_full_node.py +112 -0
  642. chia/server/start_harvester.py +93 -0
  643. chia/server/start_introducer.py +81 -0
  644. chia/server/start_service.py +316 -0
  645. chia/server/start_timelord.py +89 -0
  646. chia/server/start_wallet.py +113 -0
  647. chia/server/upnp.py +118 -0
  648. chia/server/ws_connection.py +766 -0
  649. chia/simulator/__init__.py +0 -0
  650. chia/simulator/add_blocks_in_batches.py +54 -0
  651. chia/simulator/block_tools.py +2054 -0
  652. chia/simulator/full_node_simulator.py +794 -0
  653. chia/simulator/keyring.py +128 -0
  654. chia/simulator/setup_services.py +506 -0
  655. chia/simulator/simulator_constants.py +13 -0
  656. chia/simulator/simulator_full_node_rpc_api.py +99 -0
  657. chia/simulator/simulator_full_node_rpc_client.py +60 -0
  658. chia/simulator/simulator_protocol.py +29 -0
  659. chia/simulator/simulator_test_tools.py +164 -0
  660. chia/simulator/socket.py +24 -0
  661. chia/simulator/ssl_certs.py +114 -0
  662. chia/simulator/ssl_certs_1.py +697 -0
  663. chia/simulator/ssl_certs_10.py +697 -0
  664. chia/simulator/ssl_certs_2.py +697 -0
  665. chia/simulator/ssl_certs_3.py +697 -0
  666. chia/simulator/ssl_certs_4.py +697 -0
  667. chia/simulator/ssl_certs_5.py +697 -0
  668. chia/simulator/ssl_certs_6.py +697 -0
  669. chia/simulator/ssl_certs_7.py +697 -0
  670. chia/simulator/ssl_certs_8.py +697 -0
  671. chia/simulator/ssl_certs_9.py +697 -0
  672. chia/simulator/start_simulator.py +143 -0
  673. chia/simulator/wallet_tools.py +246 -0
  674. chia/ssl/__init__.py +0 -0
  675. chia/ssl/chia_ca.crt +19 -0
  676. chia/ssl/chia_ca.key +28 -0
  677. chia/ssl/create_ssl.py +249 -0
  678. chia/ssl/dst_root_ca.pem +20 -0
  679. chia/timelord/__init__.py +0 -0
  680. chia/timelord/iters_from_block.py +50 -0
  681. chia/timelord/timelord.py +1226 -0
  682. chia/timelord/timelord_api.py +138 -0
  683. chia/timelord/timelord_launcher.py +190 -0
  684. chia/timelord/timelord_state.py +244 -0
  685. chia/timelord/types.py +22 -0
  686. chia/types/__init__.py +0 -0
  687. chia/types/aliases.py +35 -0
  688. chia/types/block_protocol.py +20 -0
  689. chia/types/blockchain_format/__init__.py +0 -0
  690. chia/types/blockchain_format/classgroup.py +5 -0
  691. chia/types/blockchain_format/coin.py +28 -0
  692. chia/types/blockchain_format/foliage.py +8 -0
  693. chia/types/blockchain_format/pool_target.py +5 -0
  694. chia/types/blockchain_format/program.py +269 -0
  695. chia/types/blockchain_format/proof_of_space.py +135 -0
  696. chia/types/blockchain_format/reward_chain_block.py +6 -0
  697. chia/types/blockchain_format/serialized_program.py +5 -0
  698. chia/types/blockchain_format/sized_bytes.py +11 -0
  699. chia/types/blockchain_format/slots.py +9 -0
  700. chia/types/blockchain_format/sub_epoch_summary.py +5 -0
  701. chia/types/blockchain_format/tree_hash.py +72 -0
  702. chia/types/blockchain_format/vdf.py +86 -0
  703. chia/types/clvm_cost.py +13 -0
  704. chia/types/coin_record.py +43 -0
  705. chia/types/coin_spend.py +115 -0
  706. chia/types/condition_opcodes.py +73 -0
  707. chia/types/condition_with_args.py +16 -0
  708. chia/types/eligible_coin_spends.py +365 -0
  709. chia/types/end_of_slot_bundle.py +5 -0
  710. chia/types/fee_rate.py +38 -0
  711. chia/types/full_block.py +5 -0
  712. chia/types/generator_types.py +13 -0
  713. chia/types/header_block.py +5 -0
  714. chia/types/internal_mempool_item.py +18 -0
  715. chia/types/mempool_inclusion_status.py +9 -0
  716. chia/types/mempool_item.py +85 -0
  717. chia/types/mempool_submission_status.py +30 -0
  718. chia/types/mojos.py +7 -0
  719. chia/types/peer_info.py +64 -0
  720. chia/types/signing_mode.py +29 -0
  721. chia/types/spend_bundle.py +30 -0
  722. chia/types/spend_bundle_conditions.py +7 -0
  723. chia/types/transaction_queue_entry.py +55 -0
  724. chia/types/unfinished_block.py +5 -0
  725. chia/types/unfinished_header_block.py +37 -0
  726. chia/types/validation_state.py +14 -0
  727. chia/types/weight_proof.py +49 -0
  728. chia/util/__init__.py +0 -0
  729. chia/util/action_scope.py +168 -0
  730. chia/util/async_pool.py +226 -0
  731. chia/util/augmented_chain.py +134 -0
  732. chia/util/batches.py +42 -0
  733. chia/util/bech32m.py +126 -0
  734. chia/util/beta_metrics.py +119 -0
  735. chia/util/block_cache.py +56 -0
  736. chia/util/byte_types.py +12 -0
  737. chia/util/check_fork_next_block.py +33 -0
  738. chia/util/chia_logging.py +144 -0
  739. chia/util/chia_version.py +33 -0
  740. chia/util/collection.py +17 -0
  741. chia/util/condition_tools.py +201 -0
  742. chia/util/config.py +367 -0
  743. chia/util/cpu.py +22 -0
  744. chia/util/db_synchronous.py +23 -0
  745. chia/util/db_version.py +32 -0
  746. chia/util/db_wrapper.py +430 -0
  747. chia/util/default_root.py +27 -0
  748. chia/util/dump_keyring.py +93 -0
  749. chia/util/english.txt +2048 -0
  750. chia/util/errors.py +353 -0
  751. chia/util/file_keyring.py +469 -0
  752. chia/util/files.py +97 -0
  753. chia/util/full_block_utils.py +345 -0
  754. chia/util/generator_tools.py +72 -0
  755. chia/util/hash.py +31 -0
  756. chia/util/initial-config.yaml +694 -0
  757. chia/util/inline_executor.py +26 -0
  758. chia/util/ints.py +19 -0
  759. chia/util/ip_address.py +39 -0
  760. chia/util/json_util.py +37 -0
  761. chia/util/keychain.py +676 -0
  762. chia/util/keyring_wrapper.py +327 -0
  763. chia/util/limited_semaphore.py +41 -0
  764. chia/util/lock.py +49 -0
  765. chia/util/log_exceptions.py +32 -0
  766. chia/util/logging.py +36 -0
  767. chia/util/lru_cache.py +31 -0
  768. chia/util/math.py +20 -0
  769. chia/util/network.py +182 -0
  770. chia/util/paginator.py +48 -0
  771. chia/util/path.py +31 -0
  772. chia/util/permissions.py +20 -0
  773. chia/util/prev_transaction_block.py +21 -0
  774. chia/util/priority_mutex.py +95 -0
  775. chia/util/profiler.py +197 -0
  776. chia/util/recursive_replace.py +24 -0
  777. chia/util/safe_cancel_task.py +16 -0
  778. chia/util/service_groups.py +47 -0
  779. chia/util/setproctitle.py +22 -0
  780. chia/util/significant_bits.py +32 -0
  781. chia/util/ssl_check.py +213 -0
  782. chia/util/streamable.py +642 -0
  783. chia/util/task_referencer.py +59 -0
  784. chia/util/task_timing.py +382 -0
  785. chia/util/timing.py +67 -0
  786. chia/util/vdf_prover.py +30 -0
  787. chia/util/virtual_project_analysis.py +540 -0
  788. chia/util/ws_message.py +66 -0
  789. chia/wallet/__init__.py +0 -0
  790. chia/wallet/cat_wallet/__init__.py +0 -0
  791. chia/wallet/cat_wallet/cat_constants.py +75 -0
  792. chia/wallet/cat_wallet/cat_info.py +47 -0
  793. chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
  794. chia/wallet/cat_wallet/cat_utils.py +164 -0
  795. chia/wallet/cat_wallet/cat_wallet.py +855 -0
  796. chia/wallet/cat_wallet/dao_cat_info.py +28 -0
  797. chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
  798. chia/wallet/cat_wallet/lineage_store.py +74 -0
  799. chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
  800. chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
  801. chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
  802. chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
  803. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
  804. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
  805. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
  806. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
  807. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
  808. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
  809. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
  810. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
  811. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
  812. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
  813. chia/wallet/coin_selection.py +188 -0
  814. chia/wallet/conditions.py +1512 -0
  815. chia/wallet/dao_wallet/__init__.py +0 -0
  816. chia/wallet/dao_wallet/dao_info.py +61 -0
  817. chia/wallet/dao_wallet/dao_utils.py +811 -0
  818. chia/wallet/dao_wallet/dao_wallet.py +2119 -0
  819. chia/wallet/db_wallet/__init__.py +0 -0
  820. chia/wallet/db_wallet/db_wallet_puzzles.py +111 -0
  821. chia/wallet/derivation_record.py +30 -0
  822. chia/wallet/derive_keys.py +146 -0
  823. chia/wallet/did_wallet/__init__.py +0 -0
  824. chia/wallet/did_wallet/did_info.py +39 -0
  825. chia/wallet/did_wallet/did_wallet.py +1494 -0
  826. chia/wallet/did_wallet/did_wallet_puzzles.py +221 -0
  827. chia/wallet/did_wallet/puzzles/__init__.py +0 -0
  828. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
  829. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
  830. chia/wallet/driver_protocol.py +26 -0
  831. chia/wallet/key_val_store.py +55 -0
  832. chia/wallet/lineage_proof.py +58 -0
  833. chia/wallet/nft_wallet/__init__.py +0 -0
  834. chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
  835. chia/wallet/nft_wallet/nft_info.py +120 -0
  836. chia/wallet/nft_wallet/nft_puzzles.py +305 -0
  837. chia/wallet/nft_wallet/nft_wallet.py +1687 -0
  838. chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
  839. chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
  840. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
  841. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
  842. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
  843. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
  844. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
  845. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
  846. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
  847. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
  848. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
  849. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
  850. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
  851. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
  852. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
  853. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
  854. chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
  855. chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
  856. chia/wallet/nft_wallet/uncurry_nft.py +217 -0
  857. chia/wallet/notification_manager.py +117 -0
  858. chia/wallet/notification_store.py +178 -0
  859. chia/wallet/outer_puzzles.py +84 -0
  860. chia/wallet/payment.py +33 -0
  861. chia/wallet/puzzle_drivers.py +118 -0
  862. chia/wallet/puzzles/__init__.py +0 -0
  863. chia/wallet/puzzles/augmented_condition.clsp +13 -0
  864. chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
  865. chia/wallet/puzzles/clawback/__init__.py +0 -0
  866. chia/wallet/puzzles/clawback/drivers.py +188 -0
  867. chia/wallet/puzzles/clawback/metadata.py +38 -0
  868. chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
  869. chia/wallet/puzzles/condition_codes.clib +77 -0
  870. chia/wallet/puzzles/curry-and-treehash.clib +102 -0
  871. chia/wallet/puzzles/curry.clib +135 -0
  872. chia/wallet/puzzles/curry_by_index.clib +16 -0
  873. chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
  874. chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
  875. chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
  876. chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
  877. chia/wallet/puzzles/dao_finished_state.clsp +35 -0
  878. chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
  879. chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
  880. chia/wallet/puzzles/dao_lockup.clsp +288 -0
  881. chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
  882. chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
  883. chia/wallet/puzzles/dao_proposal.clsp +377 -0
  884. chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
  885. chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
  886. chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
  887. chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
  888. chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
  889. chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
  890. chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
  891. chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
  892. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
  893. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
  894. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
  895. chia/wallet/puzzles/dao_treasury.clsp +115 -0
  896. chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
  897. chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
  898. chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
  899. chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
  900. chia/wallet/puzzles/json.clib +25 -0
  901. chia/wallet/puzzles/load_clvm.py +161 -0
  902. chia/wallet/puzzles/merkle_utils.clib +18 -0
  903. chia/wallet/puzzles/notification.clsp +7 -0
  904. chia/wallet/puzzles/notification.clsp.hex +1 -0
  905. chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
  906. chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
  907. chia/wallet/puzzles/p2_conditions.clsp +3 -0
  908. chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
  909. chia/wallet/puzzles/p2_conditions.py +26 -0
  910. chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
  911. chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
  912. chia/wallet/puzzles/p2_delegated_conditions.py +21 -0
  913. chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
  914. chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
  915. chia/wallet/puzzles/p2_delegated_puzzle.py +34 -0
  916. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
  917. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
  918. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +160 -0
  919. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
  920. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
  921. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +21 -0
  922. chia/wallet/puzzles/p2_parent.clsp +19 -0
  923. chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
  924. chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
  925. chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
  926. chia/wallet/puzzles/p2_puzzle_hash.py +27 -0
  927. chia/wallet/puzzles/p2_singleton.clsp +30 -0
  928. chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
  929. chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
  930. chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
  931. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
  932. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
  933. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
  934. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
  935. chia/wallet/puzzles/puzzle_utils.py +34 -0
  936. chia/wallet/puzzles/settlement_payments.clsp +49 -0
  937. chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
  938. chia/wallet/puzzles/sha256tree.clib +11 -0
  939. chia/wallet/puzzles/singleton_launcher.clsp +16 -0
  940. chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
  941. chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
  942. chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
  943. chia/wallet/puzzles/singleton_top_layer.py +296 -0
  944. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
  945. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
  946. chia/wallet/puzzles/singleton_top_layer_v1_1.py +345 -0
  947. chia/wallet/puzzles/singleton_truths.clib +21 -0
  948. chia/wallet/puzzles/tails.py +348 -0
  949. chia/wallet/puzzles/utility_macros.clib +48 -0
  950. chia/wallet/signer_protocol.py +125 -0
  951. chia/wallet/singleton.py +106 -0
  952. chia/wallet/singleton_record.py +30 -0
  953. chia/wallet/trade_manager.py +1102 -0
  954. chia/wallet/trade_record.py +67 -0
  955. chia/wallet/trading/__init__.py +0 -0
  956. chia/wallet/trading/offer.py +702 -0
  957. chia/wallet/trading/trade_status.py +13 -0
  958. chia/wallet/trading/trade_store.py +526 -0
  959. chia/wallet/transaction_record.py +158 -0
  960. chia/wallet/transaction_sorting.py +14 -0
  961. chia/wallet/uncurried_puzzle.py +17 -0
  962. chia/wallet/util/__init__.py +0 -0
  963. chia/wallet/util/address_type.py +55 -0
  964. chia/wallet/util/blind_signer_tl.py +164 -0
  965. chia/wallet/util/clvm_streamable.py +203 -0
  966. chia/wallet/util/compute_hints.py +66 -0
  967. chia/wallet/util/compute_memos.py +43 -0
  968. chia/wallet/util/curry_and_treehash.py +91 -0
  969. chia/wallet/util/debug_spend_bundle.py +232 -0
  970. chia/wallet/util/merkle_tree.py +100 -0
  971. chia/wallet/util/merkle_utils.py +102 -0
  972. chia/wallet/util/new_peak_queue.py +82 -0
  973. chia/wallet/util/notifications.py +12 -0
  974. chia/wallet/util/peer_request_cache.py +174 -0
  975. chia/wallet/util/pprint.py +39 -0
  976. chia/wallet/util/puzzle_compression.py +95 -0
  977. chia/wallet/util/puzzle_decorator.py +100 -0
  978. chia/wallet/util/puzzle_decorator_type.py +7 -0
  979. chia/wallet/util/query_filter.py +59 -0
  980. chia/wallet/util/transaction_type.py +23 -0
  981. chia/wallet/util/tx_config.py +158 -0
  982. chia/wallet/util/wallet_sync_utils.py +351 -0
  983. chia/wallet/util/wallet_types.py +72 -0
  984. chia/wallet/vc_wallet/__init__.py +0 -0
  985. chia/wallet/vc_wallet/cr_cat_drivers.py +664 -0
  986. chia/wallet/vc_wallet/cr_cat_wallet.py +877 -0
  987. chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
  988. chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
  989. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
  990. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
  991. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
  992. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
  993. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
  994. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
  995. chia/wallet/vc_wallet/vc_drivers.py +838 -0
  996. chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
  997. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
  998. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
  999. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
  1000. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
  1001. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
  1002. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
  1003. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
  1004. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
  1005. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
  1006. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
  1007. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
  1008. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
  1009. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
  1010. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
  1011. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
  1012. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
  1013. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
  1014. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
  1015. chia/wallet/vc_wallet/vc_store.py +263 -0
  1016. chia/wallet/vc_wallet/vc_wallet.py +638 -0
  1017. chia/wallet/wallet.py +698 -0
  1018. chia/wallet/wallet_action_scope.py +96 -0
  1019. chia/wallet/wallet_blockchain.py +244 -0
  1020. chia/wallet/wallet_coin_record.py +72 -0
  1021. chia/wallet/wallet_coin_store.py +351 -0
  1022. chia/wallet/wallet_info.py +35 -0
  1023. chia/wallet/wallet_interested_store.py +188 -0
  1024. chia/wallet/wallet_nft_store.py +279 -0
  1025. chia/wallet/wallet_node.py +1765 -0
  1026. chia/wallet/wallet_node_api.py +207 -0
  1027. chia/wallet/wallet_pool_store.py +119 -0
  1028. chia/wallet/wallet_protocol.py +90 -0
  1029. chia/wallet/wallet_puzzle_store.py +396 -0
  1030. chia/wallet/wallet_retry_store.py +70 -0
  1031. chia/wallet/wallet_singleton_store.py +259 -0
  1032. chia/wallet/wallet_spend_bundle.py +25 -0
  1033. chia/wallet/wallet_state_manager.py +2819 -0
  1034. chia/wallet/wallet_transaction_store.py +496 -0
  1035. chia/wallet/wallet_user_store.py +110 -0
  1036. chia/wallet/wallet_weight_proof_handler.py +126 -0
  1037. chia_blockchain-2.5.1rc1.dist-info/LICENSE +201 -0
  1038. chia_blockchain-2.5.1rc1.dist-info/METADATA +156 -0
  1039. chia_blockchain-2.5.1rc1.dist-info/RECORD +1042 -0
  1040. chia_blockchain-2.5.1rc1.dist-info/WHEEL +4 -0
  1041. chia_blockchain-2.5.1rc1.dist-info/entry_points.txt +17 -0
  1042. 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()