chia-blockchain 2.4.4__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 (1028) 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 +197 -0
  7. chia/_tests/blockchain/config.py +4 -0
  8. chia/_tests/blockchain/test_augmented_chain.py +147 -0
  9. chia/_tests/blockchain/test_blockchain.py +4100 -0
  10. chia/_tests/blockchain/test_blockchain_transactions.py +1050 -0
  11. chia/_tests/blockchain/test_build_chains.py +61 -0
  12. chia/_tests/blockchain/test_get_block_generator.py +72 -0
  13. chia/_tests/blockchain/test_lookup_fork_chain.py +195 -0
  14. chia/_tests/build-init-files.py +93 -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 +73 -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 +147 -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 +57 -0
  26. chia/_tests/clvm/test_program.py +150 -0
  27. chia/_tests/clvm/test_puzzle_compression.py +144 -0
  28. chia/_tests/clvm/test_puzzle_drivers.py +45 -0
  29. chia/_tests/clvm/test_puzzles.py +247 -0
  30. chia/_tests/clvm/test_singletons.py +540 -0
  31. chia/_tests/clvm/test_spend_sim.py +181 -0
  32. chia/_tests/cmds/__init__.py +0 -0
  33. chia/_tests/cmds/cmd_test_utils.py +472 -0
  34. chia/_tests/cmds/config.py +3 -0
  35. chia/_tests/cmds/conftest.py +23 -0
  36. chia/_tests/cmds/test_click_types.py +195 -0
  37. chia/_tests/cmds/test_cmd_framework.py +400 -0
  38. chia/_tests/cmds/test_cmds_util.py +97 -0
  39. chia/_tests/cmds/test_daemon.py +92 -0
  40. chia/_tests/cmds/test_farm_cmd.py +67 -0
  41. chia/_tests/cmds/test_show.py +116 -0
  42. chia/_tests/cmds/test_sim.py +207 -0
  43. chia/_tests/cmds/test_timelock_args.py +75 -0
  44. chia/_tests/cmds/test_tx_config_args.py +153 -0
  45. chia/_tests/cmds/testing_classes.py +59 -0
  46. chia/_tests/cmds/wallet/__init__.py +0 -0
  47. chia/_tests/cmds/wallet/test_coins.py +195 -0
  48. chia/_tests/cmds/wallet/test_consts.py +47 -0
  49. chia/_tests/cmds/wallet/test_dao.py +565 -0
  50. chia/_tests/cmds/wallet/test_did.py +403 -0
  51. chia/_tests/cmds/wallet/test_nft.py +470 -0
  52. chia/_tests/cmds/wallet/test_notifications.py +124 -0
  53. chia/_tests/cmds/wallet/test_offer.toffer +1 -0
  54. chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
  55. chia/_tests/cmds/wallet/test_vcs.py +376 -0
  56. chia/_tests/cmds/wallet/test_wallet.py +1126 -0
  57. chia/_tests/cmds/wallet/test_wallet_check.py +111 -0
  58. chia/_tests/conftest.py +1304 -0
  59. chia/_tests/connection_utils.py +124 -0
  60. chia/_tests/core/__init__.py +0 -0
  61. chia/_tests/core/cmds/__init__.py +0 -0
  62. chia/_tests/core/cmds/test_beta.py +382 -0
  63. chia/_tests/core/cmds/test_keys.py +1734 -0
  64. chia/_tests/core/cmds/test_wallet.py +126 -0
  65. chia/_tests/core/config.py +3 -0
  66. chia/_tests/core/consensus/__init__.py +0 -0
  67. chia/_tests/core/consensus/test_block_creation.py +56 -0
  68. chia/_tests/core/consensus/test_pot_iterations.py +117 -0
  69. chia/_tests/core/custom_types/__init__.py +0 -0
  70. chia/_tests/core/custom_types/test_coin.py +109 -0
  71. chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
  72. chia/_tests/core/custom_types/test_spend_bundle.py +71 -0
  73. chia/_tests/core/daemon/__init__.py +0 -0
  74. chia/_tests/core/daemon/config.py +4 -0
  75. chia/_tests/core/daemon/test_daemon.py +2128 -0
  76. chia/_tests/core/daemon/test_daemon_register.py +109 -0
  77. chia/_tests/core/daemon/test_keychain_proxy.py +100 -0
  78. chia/_tests/core/data_layer/__init__.py +0 -0
  79. chia/_tests/core/data_layer/config.py +5 -0
  80. chia/_tests/core/data_layer/conftest.py +105 -0
  81. chia/_tests/core/data_layer/test_data_cli.py +57 -0
  82. chia/_tests/core/data_layer/test_data_layer.py +83 -0
  83. chia/_tests/core/data_layer/test_data_layer_util.py +219 -0
  84. chia/_tests/core/data_layer/test_data_rpc.py +3865 -0
  85. chia/_tests/core/data_layer/test_data_store.py +2423 -0
  86. chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
  87. chia/_tests/core/data_layer/test_plugin.py +91 -0
  88. chia/_tests/core/data_layer/util.py +232 -0
  89. chia/_tests/core/farmer/__init__.py +0 -0
  90. chia/_tests/core/farmer/config.py +3 -0
  91. chia/_tests/core/farmer/test_farmer_api.py +101 -0
  92. chia/_tests/core/full_node/__init__.py +0 -0
  93. chia/_tests/core/full_node/config.py +4 -0
  94. chia/_tests/core/full_node/dos/__init__.py +0 -0
  95. chia/_tests/core/full_node/dos/config.py +3 -0
  96. chia/_tests/core/full_node/full_sync/__init__.py +0 -0
  97. chia/_tests/core/full_node/full_sync/config.py +4 -0
  98. chia/_tests/core/full_node/full_sync/test_full_sync.py +448 -0
  99. chia/_tests/core/full_node/ram_db.py +27 -0
  100. chia/_tests/core/full_node/stores/__init__.py +0 -0
  101. chia/_tests/core/full_node/stores/config.py +4 -0
  102. chia/_tests/core/full_node/stores/test_block_store.py +488 -0
  103. chia/_tests/core/full_node/stores/test_coin_store.py +888 -0
  104. chia/_tests/core/full_node/stores/test_full_node_store.py +1215 -0
  105. chia/_tests/core/full_node/stores/test_hint_store.py +230 -0
  106. chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
  107. chia/_tests/core/full_node/test_address_manager.py +588 -0
  108. chia/_tests/core/full_node/test_block_height_map.py +556 -0
  109. chia/_tests/core/full_node/test_conditions.py +558 -0
  110. chia/_tests/core/full_node/test_full_node.py +2445 -0
  111. chia/_tests/core/full_node/test_generator_tools.py +82 -0
  112. chia/_tests/core/full_node/test_hint_management.py +104 -0
  113. chia/_tests/core/full_node/test_node_load.py +34 -0
  114. chia/_tests/core/full_node/test_performance.py +182 -0
  115. chia/_tests/core/full_node/test_subscriptions.py +492 -0
  116. chia/_tests/core/full_node/test_transactions.py +203 -0
  117. chia/_tests/core/full_node/test_tx_processing_queue.py +154 -0
  118. chia/_tests/core/large_block.py +2388 -0
  119. chia/_tests/core/make_block_generator.py +72 -0
  120. chia/_tests/core/mempool/__init__.py +0 -0
  121. chia/_tests/core/mempool/config.py +4 -0
  122. chia/_tests/core/mempool/test_mempool.py +3180 -0
  123. chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
  124. chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
  125. chia/_tests/core/mempool/test_mempool_item_queries.py +192 -0
  126. chia/_tests/core/mempool/test_mempool_manager.py +2054 -0
  127. chia/_tests/core/mempool/test_mempool_performance.py +65 -0
  128. chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
  129. chia/_tests/core/node_height.py +28 -0
  130. chia/_tests/core/server/__init__.py +0 -0
  131. chia/_tests/core/server/config.py +3 -0
  132. chia/_tests/core/server/flood.py +82 -0
  133. chia/_tests/core/server/serve.py +132 -0
  134. chia/_tests/core/server/test_capabilities.py +68 -0
  135. chia/_tests/core/server/test_dos.py +320 -0
  136. chia/_tests/core/server/test_event_loop.py +109 -0
  137. chia/_tests/core/server/test_loop.py +290 -0
  138. chia/_tests/core/server/test_node_discovery.py +74 -0
  139. chia/_tests/core/server/test_rate_limits.py +370 -0
  140. chia/_tests/core/server/test_server.py +225 -0
  141. chia/_tests/core/server/test_upnp.py +8 -0
  142. chia/_tests/core/services/__init__.py +0 -0
  143. chia/_tests/core/services/config.py +3 -0
  144. chia/_tests/core/services/test_services.py +166 -0
  145. chia/_tests/core/ssl/__init__.py +0 -0
  146. chia/_tests/core/ssl/config.py +3 -0
  147. chia/_tests/core/ssl/test_ssl.py +198 -0
  148. chia/_tests/core/test_coins.py +33 -0
  149. chia/_tests/core/test_cost_calculation.py +314 -0
  150. chia/_tests/core/test_crawler.py +175 -0
  151. chia/_tests/core/test_crawler_rpc.py +53 -0
  152. chia/_tests/core/test_daemon_rpc.py +24 -0
  153. chia/_tests/core/test_db_conversion.py +129 -0
  154. chia/_tests/core/test_db_validation.py +161 -0
  155. chia/_tests/core/test_farmer_harvester_rpc.py +504 -0
  156. chia/_tests/core/test_filter.py +37 -0
  157. chia/_tests/core/test_full_node_rpc.py +794 -0
  158. chia/_tests/core/test_merkle_set.py +343 -0
  159. chia/_tests/core/test_program.py +49 -0
  160. chia/_tests/core/test_rpc_util.py +87 -0
  161. chia/_tests/core/test_seeder.py +308 -0
  162. chia/_tests/core/test_setproctitle.py +13 -0
  163. chia/_tests/core/util/__init__.py +0 -0
  164. chia/_tests/core/util/config.py +4 -0
  165. chia/_tests/core/util/test_block_cache.py +44 -0
  166. chia/_tests/core/util/test_cached_bls.py +57 -0
  167. chia/_tests/core/util/test_config.py +337 -0
  168. chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
  169. chia/_tests/core/util/test_files.py +391 -0
  170. chia/_tests/core/util/test_jsonify.py +146 -0
  171. chia/_tests/core/util/test_keychain.py +514 -0
  172. chia/_tests/core/util/test_keyring_wrapper.py +490 -0
  173. chia/_tests/core/util/test_lockfile.py +380 -0
  174. chia/_tests/core/util/test_log_exceptions.py +187 -0
  175. chia/_tests/core/util/test_lru_cache.py +56 -0
  176. chia/_tests/core/util/test_significant_bits.py +40 -0
  177. chia/_tests/core/util/test_streamable.py +883 -0
  178. chia/_tests/db/__init__.py +0 -0
  179. chia/_tests/db/test_db_wrapper.py +565 -0
  180. chia/_tests/environments/__init__.py +0 -0
  181. chia/_tests/environments/common.py +35 -0
  182. chia/_tests/environments/full_node.py +47 -0
  183. chia/_tests/environments/wallet.py +368 -0
  184. chia/_tests/ether.py +19 -0
  185. chia/_tests/farmer_harvester/__init__.py +0 -0
  186. chia/_tests/farmer_harvester/config.py +3 -0
  187. chia/_tests/farmer_harvester/test_farmer.py +1264 -0
  188. chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
  189. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +130 -0
  190. chia/_tests/farmer_harvester/test_third_party_harvesters.py +501 -0
  191. chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
  192. chia/_tests/fee_estimation/__init__.py +0 -0
  193. chia/_tests/fee_estimation/config.py +3 -0
  194. chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
  195. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
  196. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +145 -0
  197. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
  198. chia/_tests/generator/__init__.py +0 -0
  199. chia/_tests/generator/puzzles/__init__.py +0 -0
  200. chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
  201. chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
  202. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
  203. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
  204. chia/_tests/generator/test_compression.py +218 -0
  205. chia/_tests/generator/test_generator_types.py +44 -0
  206. chia/_tests/generator/test_rom.py +182 -0
  207. chia/_tests/plot_sync/__init__.py +0 -0
  208. chia/_tests/plot_sync/config.py +3 -0
  209. chia/_tests/plot_sync/test_delta.py +102 -0
  210. chia/_tests/plot_sync/test_plot_sync.py +617 -0
  211. chia/_tests/plot_sync/test_receiver.py +451 -0
  212. chia/_tests/plot_sync/test_sender.py +116 -0
  213. chia/_tests/plot_sync/test_sync_simulated.py +450 -0
  214. chia/_tests/plot_sync/util.py +67 -0
  215. chia/_tests/plotting/__init__.py +0 -0
  216. chia/_tests/plotting/config.py +3 -0
  217. chia/_tests/plotting/test_plot_manager.py +738 -0
  218. chia/_tests/plotting/util.py +13 -0
  219. chia/_tests/pools/__init__.py +0 -0
  220. chia/_tests/pools/config.py +5 -0
  221. chia/_tests/pools/test_pool_cmdline.py +23 -0
  222. chia/_tests/pools/test_pool_config.py +44 -0
  223. chia/_tests/pools/test_pool_puzzles_lifecycle.py +398 -0
  224. chia/_tests/pools/test_pool_rpc.py +1010 -0
  225. chia/_tests/pools/test_pool_wallet.py +201 -0
  226. chia/_tests/pools/test_wallet_pool_store.py +161 -0
  227. chia/_tests/process_junit.py +349 -0
  228. chia/_tests/rpc/__init__.py +0 -0
  229. chia/_tests/rpc/test_rpc_client.py +81 -0
  230. chia/_tests/simulation/__init__.py +0 -0
  231. chia/_tests/simulation/config.py +6 -0
  232. chia/_tests/simulation/test_simulation.py +501 -0
  233. chia/_tests/simulation/test_simulator.py +234 -0
  234. chia/_tests/simulation/test_start_simulator.py +106 -0
  235. chia/_tests/testconfig.py +13 -0
  236. chia/_tests/timelord/__init__.py +0 -0
  237. chia/_tests/timelord/config.py +3 -0
  238. chia/_tests/timelord/test_new_peak.py +437 -0
  239. chia/_tests/timelord/test_timelord.py +11 -0
  240. chia/_tests/tools/1315537.json +170 -0
  241. chia/_tests/tools/1315544.json +160 -0
  242. chia/_tests/tools/1315630.json +150 -0
  243. chia/_tests/tools/300000.json +105 -0
  244. chia/_tests/tools/442734.json +140 -0
  245. chia/_tests/tools/466212.json +130 -0
  246. chia/_tests/tools/__init__.py +0 -0
  247. chia/_tests/tools/config.py +5 -0
  248. chia/_tests/tools/test-blockchain-db.sqlite +0 -0
  249. chia/_tests/tools/test_full_sync.py +30 -0
  250. chia/_tests/tools/test_legacy_keyring.py +82 -0
  251. chia/_tests/tools/test_run_block.py +129 -0
  252. chia/_tests/util/__init__.py +0 -0
  253. chia/_tests/util/benchmark_cost.py +170 -0
  254. chia/_tests/util/benchmarks.py +154 -0
  255. chia/_tests/util/bip39_test_vectors.json +148 -0
  256. chia/_tests/util/blockchain.py +133 -0
  257. chia/_tests/util/blockchain_mock.py +132 -0
  258. chia/_tests/util/build_network_protocol_files.py +302 -0
  259. chia/_tests/util/clvm_generator.bin +0 -0
  260. chia/_tests/util/config.py +3 -0
  261. chia/_tests/util/constants.py +20 -0
  262. chia/_tests/util/db_connection.py +36 -0
  263. chia/_tests/util/full_sync.py +245 -0
  264. chia/_tests/util/gen_ssl_certs.py +115 -0
  265. chia/_tests/util/generator_tools_testing.py +47 -0
  266. chia/_tests/util/key_tool.py +37 -0
  267. chia/_tests/util/misc.py +722 -0
  268. chia/_tests/util/network_protocol_data.py +1074 -0
  269. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  270. chia/_tests/util/protocol_messages_json.py +2700 -0
  271. chia/_tests/util/rpc.py +23 -0
  272. chia/_tests/util/run_block.py +163 -0
  273. chia/_tests/util/setup_nodes.py +479 -0
  274. chia/_tests/util/split_managers.py +99 -0
  275. chia/_tests/util/temp_file.py +14 -0
  276. chia/_tests/util/test_action_scope.py +143 -0
  277. chia/_tests/util/test_async_pool.py +366 -0
  278. chia/_tests/util/test_build_job_matrix.py +43 -0
  279. chia/_tests/util/test_build_network_protocol_files.py +7 -0
  280. chia/_tests/util/test_chia_version.py +50 -0
  281. chia/_tests/util/test_collection.py +11 -0
  282. chia/_tests/util/test_condition_tools.py +231 -0
  283. chia/_tests/util/test_config.py +426 -0
  284. chia/_tests/util/test_dump_keyring.py +60 -0
  285. chia/_tests/util/test_errors.py +10 -0
  286. chia/_tests/util/test_full_block_utils.py +271 -0
  287. chia/_tests/util/test_installed.py +20 -0
  288. chia/_tests/util/test_limited_semaphore.py +52 -0
  289. chia/_tests/util/test_logging_filter.py +43 -0
  290. chia/_tests/util/test_misc.py +444 -0
  291. chia/_tests/util/test_network.py +74 -0
  292. chia/_tests/util/test_network_protocol_files.py +579 -0
  293. chia/_tests/util/test_network_protocol_json.py +266 -0
  294. chia/_tests/util/test_network_protocol_test.py +257 -0
  295. chia/_tests/util/test_paginator.py +72 -0
  296. chia/_tests/util/test_pprint.py +17 -0
  297. chia/_tests/util/test_priority_mutex.py +487 -0
  298. chia/_tests/util/test_recursive_replace.py +116 -0
  299. chia/_tests/util/test_replace_str_to_bytes.py +137 -0
  300. chia/_tests/util/test_service_groups.py +15 -0
  301. chia/_tests/util/test_ssl_check.py +31 -0
  302. chia/_tests/util/test_testnet_overrides.py +19 -0
  303. chia/_tests/util/test_tests_misc.py +38 -0
  304. chia/_tests/util/test_timing.py +37 -0
  305. chia/_tests/util/test_trusted_peer.py +51 -0
  306. chia/_tests/util/time_out_assert.py +154 -0
  307. chia/_tests/wallet/__init__.py +0 -0
  308. chia/_tests/wallet/cat_wallet/__init__.py +0 -0
  309. chia/_tests/wallet/cat_wallet/config.py +4 -0
  310. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
  311. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
  312. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1738 -0
  313. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
  314. chia/_tests/wallet/cat_wallet/test_trades.py +2578 -0
  315. chia/_tests/wallet/clawback/__init__.py +0 -0
  316. chia/_tests/wallet/clawback/config.py +3 -0
  317. chia/_tests/wallet/clawback/test_clawback_decorator.py +80 -0
  318. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
  319. chia/_tests/wallet/clawback/test_clawback_metadata.py +51 -0
  320. chia/_tests/wallet/config.py +4 -0
  321. chia/_tests/wallet/conftest.py +217 -0
  322. chia/_tests/wallet/dao_wallet/__init__.py +0 -0
  323. chia/_tests/wallet/dao_wallet/config.py +3 -0
  324. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1322 -0
  325. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
  326. chia/_tests/wallet/db_wallet/__init__.py +0 -0
  327. chia/_tests/wallet/db_wallet/config.py +3 -0
  328. chia/_tests/wallet/db_wallet/test_db_graftroot.py +143 -0
  329. chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
  330. chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
  331. chia/_tests/wallet/did_wallet/__init__.py +0 -0
  332. chia/_tests/wallet/did_wallet/config.py +4 -0
  333. chia/_tests/wallet/did_wallet/test_did.py +1481 -0
  334. chia/_tests/wallet/nft_wallet/__init__.py +0 -0
  335. chia/_tests/wallet/nft_wallet/config.py +4 -0
  336. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1492 -0
  337. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1014 -0
  338. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +376 -0
  339. chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
  340. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
  341. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2558 -0
  342. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
  343. chia/_tests/wallet/rpc/__init__.py +0 -0
  344. chia/_tests/wallet/rpc/config.py +4 -0
  345. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +287 -0
  346. chia/_tests/wallet/rpc/test_wallet_rpc.py +3106 -0
  347. chia/_tests/wallet/simple_sync/__init__.py +0 -0
  348. chia/_tests/wallet/simple_sync/config.py +3 -0
  349. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +719 -0
  350. chia/_tests/wallet/sync/__init__.py +0 -0
  351. chia/_tests/wallet/sync/config.py +4 -0
  352. chia/_tests/wallet/sync/test_wallet_sync.py +1529 -0
  353. chia/_tests/wallet/test_address_type.py +189 -0
  354. chia/_tests/wallet/test_bech32m.py +45 -0
  355. chia/_tests/wallet/test_clvm_streamable.py +244 -0
  356. chia/_tests/wallet/test_coin_selection.py +589 -0
  357. chia/_tests/wallet/test_conditions.py +388 -0
  358. chia/_tests/wallet/test_debug_spend_bundle.py +76 -0
  359. chia/_tests/wallet/test_new_wallet_protocol.py +1176 -0
  360. chia/_tests/wallet/test_nft_store.py +193 -0
  361. chia/_tests/wallet/test_notifications.py +196 -0
  362. chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
  363. chia/_tests/wallet/test_puzzle_store.py +133 -0
  364. chia/_tests/wallet/test_sign_coin_spends.py +159 -0
  365. chia/_tests/wallet/test_signer_protocol.py +948 -0
  366. chia/_tests/wallet/test_singleton.py +122 -0
  367. chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
  368. chia/_tests/wallet/test_singleton_store.py +152 -0
  369. chia/_tests/wallet/test_taproot.py +19 -0
  370. chia/_tests/wallet/test_transaction_store.py +941 -0
  371. chia/_tests/wallet/test_util.py +181 -0
  372. chia/_tests/wallet/test_wallet.py +2139 -0
  373. chia/_tests/wallet/test_wallet_action_scope.py +85 -0
  374. chia/_tests/wallet/test_wallet_blockchain.py +113 -0
  375. chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
  376. chia/_tests/wallet/test_wallet_interested_store.py +43 -0
  377. chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
  378. chia/_tests/wallet/test_wallet_node.py +783 -0
  379. chia/_tests/wallet/test_wallet_retry.py +95 -0
  380. chia/_tests/wallet/test_wallet_state_manager.py +252 -0
  381. chia/_tests/wallet/test_wallet_test_framework.py +275 -0
  382. chia/_tests/wallet/test_wallet_trade_store.py +218 -0
  383. chia/_tests/wallet/test_wallet_user_store.py +34 -0
  384. chia/_tests/wallet/test_wallet_utils.py +155 -0
  385. chia/_tests/wallet/vc_wallet/__init__.py +0 -0
  386. chia/_tests/wallet/vc_wallet/config.py +3 -0
  387. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
  388. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
  389. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +801 -0
  390. chia/_tests/wallet/wallet_block_tools.py +327 -0
  391. chia/_tests/weight_proof/__init__.py +0 -0
  392. chia/_tests/weight_proof/config.py +3 -0
  393. chia/_tests/weight_proof/test_weight_proof.py +528 -0
  394. chia/clvm/__init__.py +0 -0
  395. chia/clvm/spend_sim.py +488 -0
  396. chia/cmds/__init__.py +0 -0
  397. chia/cmds/beta.py +183 -0
  398. chia/cmds/beta_funcs.py +133 -0
  399. chia/cmds/check_wallet_db.py +418 -0
  400. chia/cmds/chia.py +143 -0
  401. chia/cmds/cmd_classes.py +315 -0
  402. chia/cmds/cmds_util.py +498 -0
  403. chia/cmds/coin_funcs.py +260 -0
  404. chia/cmds/coins.py +220 -0
  405. chia/cmds/completion.py +49 -0
  406. chia/cmds/configure.py +331 -0
  407. chia/cmds/dao.py +1008 -0
  408. chia/cmds/dao_funcs.py +576 -0
  409. chia/cmds/data.py +707 -0
  410. chia/cmds/data_funcs.py +380 -0
  411. chia/cmds/db.py +86 -0
  412. chia/cmds/db_backup_func.py +77 -0
  413. chia/cmds/db_upgrade_func.py +452 -0
  414. chia/cmds/db_validate_func.py +184 -0
  415. chia/cmds/dev.py +16 -0
  416. chia/cmds/farm.py +87 -0
  417. chia/cmds/farm_funcs.py +207 -0
  418. chia/cmds/init.py +70 -0
  419. chia/cmds/init_funcs.py +367 -0
  420. chia/cmds/installers.py +129 -0
  421. chia/cmds/keys.py +510 -0
  422. chia/cmds/keys_funcs.py +864 -0
  423. chia/cmds/netspace.py +47 -0
  424. chia/cmds/netspace_funcs.py +53 -0
  425. chia/cmds/options.py +32 -0
  426. chia/cmds/param_types.py +228 -0
  427. chia/cmds/passphrase.py +130 -0
  428. chia/cmds/passphrase_funcs.py +346 -0
  429. chia/cmds/peer.py +50 -0
  430. chia/cmds/peer_funcs.py +129 -0
  431. chia/cmds/plotnft.py +206 -0
  432. chia/cmds/plotnft_funcs.py +374 -0
  433. chia/cmds/plots.py +222 -0
  434. chia/cmds/plotters.py +17 -0
  435. chia/cmds/rpc.py +188 -0
  436. chia/cmds/show.py +71 -0
  437. chia/cmds/show_funcs.py +214 -0
  438. chia/cmds/signer.py +304 -0
  439. chia/cmds/sim.py +217 -0
  440. chia/cmds/sim_funcs.py +509 -0
  441. chia/cmds/start.py +24 -0
  442. chia/cmds/start_funcs.py +112 -0
  443. chia/cmds/stop.py +61 -0
  444. chia/cmds/units.py +11 -0
  445. chia/cmds/wallet.py +1745 -0
  446. chia/cmds/wallet_funcs.py +1800 -0
  447. chia/consensus/__init__.py +0 -0
  448. chia/consensus/block_body_validation.py +515 -0
  449. chia/consensus/block_creation.py +525 -0
  450. chia/consensus/block_header_validation.py +1064 -0
  451. chia/consensus/block_record.py +32 -0
  452. chia/consensus/block_rewards.py +53 -0
  453. chia/consensus/block_root_validation.py +46 -0
  454. chia/consensus/blockchain.py +1100 -0
  455. chia/consensus/blockchain_interface.py +56 -0
  456. chia/consensus/coinbase.py +30 -0
  457. chia/consensus/condition_costs.py +9 -0
  458. chia/consensus/constants.py +49 -0
  459. chia/consensus/cost_calculator.py +15 -0
  460. chia/consensus/default_constants.py +90 -0
  461. chia/consensus/deficit.py +55 -0
  462. chia/consensus/difficulty_adjustment.py +412 -0
  463. chia/consensus/find_fork_point.py +111 -0
  464. chia/consensus/full_block_to_block_record.py +167 -0
  465. chia/consensus/get_block_challenge.py +106 -0
  466. chia/consensus/get_block_generator.py +26 -0
  467. chia/consensus/make_sub_epoch_summary.py +210 -0
  468. chia/consensus/multiprocess_validation.py +365 -0
  469. chia/consensus/pos_quality.py +19 -0
  470. chia/consensus/pot_iterations.py +67 -0
  471. chia/consensus/puzzles/__init__.py +0 -0
  472. chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
  473. chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
  474. chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
  475. chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
  476. chia/consensus/vdf_info_computation.py +156 -0
  477. chia/daemon/__init__.py +0 -0
  478. chia/daemon/client.py +233 -0
  479. chia/daemon/keychain_proxy.py +501 -0
  480. chia/daemon/keychain_server.py +365 -0
  481. chia/daemon/server.py +1616 -0
  482. chia/daemon/windows_signal.py +56 -0
  483. chia/data_layer/__init__.py +0 -0
  484. chia/data_layer/data_layer.py +1303 -0
  485. chia/data_layer/data_layer_api.py +25 -0
  486. chia/data_layer/data_layer_errors.py +50 -0
  487. chia/data_layer/data_layer_server.py +170 -0
  488. chia/data_layer/data_layer_util.py +985 -0
  489. chia/data_layer/data_layer_wallet.py +1315 -0
  490. chia/data_layer/data_store.py +2267 -0
  491. chia/data_layer/dl_wallet_store.py +407 -0
  492. chia/data_layer/download_data.py +389 -0
  493. chia/data_layer/puzzles/__init__.py +0 -0
  494. chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
  495. chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
  496. chia/data_layer/s3_plugin_config.yml +33 -0
  497. chia/data_layer/s3_plugin_service.py +468 -0
  498. chia/data_layer/util/__init__.py +0 -0
  499. chia/data_layer/util/benchmark.py +108 -0
  500. chia/data_layer/util/plugin.py +41 -0
  501. chia/farmer/__init__.py +0 -0
  502. chia/farmer/farmer.py +920 -0
  503. chia/farmer/farmer_api.py +814 -0
  504. chia/full_node/__init__.py +0 -0
  505. chia/full_node/bitcoin_fee_estimator.py +85 -0
  506. chia/full_node/block_height_map.py +271 -0
  507. chia/full_node/block_store.py +570 -0
  508. chia/full_node/bundle_tools.py +19 -0
  509. chia/full_node/coin_store.py +646 -0
  510. chia/full_node/fee_estimate.py +54 -0
  511. chia/full_node/fee_estimate_store.py +24 -0
  512. chia/full_node/fee_estimation.py +93 -0
  513. chia/full_node/fee_estimator.py +90 -0
  514. chia/full_node/fee_estimator_constants.py +38 -0
  515. chia/full_node/fee_estimator_interface.py +42 -0
  516. chia/full_node/fee_history.py +26 -0
  517. chia/full_node/fee_tracker.py +564 -0
  518. chia/full_node/full_node.py +3052 -0
  519. chia/full_node/full_node_api.py +1974 -0
  520. chia/full_node/full_node_store.py +1033 -0
  521. chia/full_node/hint_management.py +56 -0
  522. chia/full_node/hint_store.py +94 -0
  523. chia/full_node/mempool.py +583 -0
  524. chia/full_node/mempool_check_conditions.py +177 -0
  525. chia/full_node/mempool_manager.py +858 -0
  526. chia/full_node/pending_tx_cache.py +112 -0
  527. chia/full_node/puzzles/__init__.py +0 -0
  528. chia/full_node/puzzles/block_program_zero.clsp +14 -0
  529. chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
  530. chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
  531. chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
  532. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
  533. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
  534. chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
  535. chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
  536. chia/full_node/signage_point.py +16 -0
  537. chia/full_node/subscriptions.py +248 -0
  538. chia/full_node/sync_store.py +145 -0
  539. chia/full_node/tx_processing_queue.py +78 -0
  540. chia/full_node/weight_proof.py +1719 -0
  541. chia/harvester/__init__.py +0 -0
  542. chia/harvester/harvester.py +271 -0
  543. chia/harvester/harvester_api.py +374 -0
  544. chia/introducer/__init__.py +0 -0
  545. chia/introducer/introducer.py +120 -0
  546. chia/introducer/introducer_api.py +64 -0
  547. chia/legacy/__init__.py +0 -0
  548. chia/legacy/keyring.py +154 -0
  549. chia/plot_sync/__init__.py +0 -0
  550. chia/plot_sync/delta.py +61 -0
  551. chia/plot_sync/exceptions.py +56 -0
  552. chia/plot_sync/receiver.py +385 -0
  553. chia/plot_sync/sender.py +337 -0
  554. chia/plot_sync/util.py +43 -0
  555. chia/plotters/__init__.py +0 -0
  556. chia/plotters/bladebit.py +388 -0
  557. chia/plotters/chiapos.py +63 -0
  558. chia/plotters/madmax.py +224 -0
  559. chia/plotters/plotters.py +577 -0
  560. chia/plotters/plotters_util.py +131 -0
  561. chia/plotting/__init__.py +0 -0
  562. chia/plotting/cache.py +212 -0
  563. chia/plotting/check_plots.py +283 -0
  564. chia/plotting/create_plots.py +278 -0
  565. chia/plotting/manager.py +436 -0
  566. chia/plotting/util.py +324 -0
  567. chia/pools/__init__.py +0 -0
  568. chia/pools/pool_config.py +110 -0
  569. chia/pools/pool_puzzles.py +459 -0
  570. chia/pools/pool_wallet.py +926 -0
  571. chia/pools/pool_wallet_info.py +118 -0
  572. chia/pools/puzzles/__init__.py +0 -0
  573. chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
  574. chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
  575. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
  576. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
  577. chia/protocols/__init__.py +0 -0
  578. chia/protocols/farmer_protocol.py +102 -0
  579. chia/protocols/full_node_protocol.py +219 -0
  580. chia/protocols/harvester_protocol.py +216 -0
  581. chia/protocols/introducer_protocol.py +26 -0
  582. chia/protocols/pool_protocol.py +177 -0
  583. chia/protocols/protocol_message_types.py +139 -0
  584. chia/protocols/protocol_state_machine.py +87 -0
  585. chia/protocols/protocol_timing.py +7 -0
  586. chia/protocols/shared_protocol.py +86 -0
  587. chia/protocols/timelord_protocol.py +93 -0
  588. chia/protocols/wallet_protocol.py +401 -0
  589. chia/py.typed +0 -0
  590. chia/rpc/__init__.py +0 -0
  591. chia/rpc/crawler_rpc_api.py +75 -0
  592. chia/rpc/data_layer_rpc_api.py +639 -0
  593. chia/rpc/data_layer_rpc_client.py +188 -0
  594. chia/rpc/data_layer_rpc_util.py +62 -0
  595. chia/rpc/farmer_rpc_api.py +360 -0
  596. chia/rpc/farmer_rpc_client.py +86 -0
  597. chia/rpc/full_node_rpc_api.py +954 -0
  598. chia/rpc/full_node_rpc_client.py +292 -0
  599. chia/rpc/harvester_rpc_api.py +136 -0
  600. chia/rpc/harvester_rpc_client.py +54 -0
  601. chia/rpc/rpc_client.py +144 -0
  602. chia/rpc/rpc_server.py +447 -0
  603. chia/rpc/timelord_rpc_api.py +27 -0
  604. chia/rpc/util.py +293 -0
  605. chia/rpc/wallet_request_types.py +688 -0
  606. chia/rpc/wallet_rpc_api.py +4779 -0
  607. chia/rpc/wallet_rpc_client.py +1844 -0
  608. chia/seeder/__init__.py +0 -0
  609. chia/seeder/crawl_store.py +427 -0
  610. chia/seeder/crawler.py +423 -0
  611. chia/seeder/crawler_api.py +129 -0
  612. chia/seeder/dns_server.py +544 -0
  613. chia/seeder/peer_record.py +146 -0
  614. chia/seeder/start_crawler.py +88 -0
  615. chia/server/__init__.py +0 -0
  616. chia/server/address_manager.py +658 -0
  617. chia/server/address_manager_store.py +237 -0
  618. chia/server/api_protocol.py +11 -0
  619. chia/server/capabilities.py +24 -0
  620. chia/server/chia_policy.py +345 -0
  621. chia/server/introducer_peers.py +76 -0
  622. chia/server/node_discovery.py +718 -0
  623. chia/server/outbound_message.py +33 -0
  624. chia/server/rate_limit_numbers.py +204 -0
  625. chia/server/rate_limits.py +113 -0
  626. chia/server/server.py +720 -0
  627. chia/server/signal_handlers.py +117 -0
  628. chia/server/ssl_context.py +32 -0
  629. chia/server/start_data_layer.py +137 -0
  630. chia/server/start_farmer.py +86 -0
  631. chia/server/start_full_node.py +106 -0
  632. chia/server/start_harvester.py +80 -0
  633. chia/server/start_introducer.py +69 -0
  634. chia/server/start_service.py +328 -0
  635. chia/server/start_timelord.py +82 -0
  636. chia/server/start_wallet.py +109 -0
  637. chia/server/upnp.py +117 -0
  638. chia/server/ws_connection.py +752 -0
  639. chia/simulator/__init__.py +0 -0
  640. chia/simulator/block_tools.py +2053 -0
  641. chia/simulator/full_node_simulator.py +802 -0
  642. chia/simulator/keyring.py +128 -0
  643. chia/simulator/setup_services.py +505 -0
  644. chia/simulator/simulator_constants.py +13 -0
  645. chia/simulator/simulator_full_node_rpc_api.py +101 -0
  646. chia/simulator/simulator_full_node_rpc_client.py +62 -0
  647. chia/simulator/simulator_protocol.py +29 -0
  648. chia/simulator/simulator_test_tools.py +163 -0
  649. chia/simulator/socket.py +27 -0
  650. chia/simulator/ssl_certs.py +114 -0
  651. chia/simulator/ssl_certs_1.py +699 -0
  652. chia/simulator/ssl_certs_10.py +699 -0
  653. chia/simulator/ssl_certs_2.py +699 -0
  654. chia/simulator/ssl_certs_3.py +699 -0
  655. chia/simulator/ssl_certs_4.py +699 -0
  656. chia/simulator/ssl_certs_5.py +699 -0
  657. chia/simulator/ssl_certs_6.py +699 -0
  658. chia/simulator/ssl_certs_7.py +699 -0
  659. chia/simulator/ssl_certs_8.py +699 -0
  660. chia/simulator/ssl_certs_9.py +699 -0
  661. chia/simulator/start_simulator.py +135 -0
  662. chia/simulator/wallet_tools.py +245 -0
  663. chia/ssl/__init__.py +0 -0
  664. chia/ssl/chia_ca.crt +19 -0
  665. chia/ssl/chia_ca.key +28 -0
  666. chia/ssl/create_ssl.py +249 -0
  667. chia/ssl/dst_root_ca.pem +20 -0
  668. chia/timelord/__init__.py +0 -0
  669. chia/timelord/iters_from_block.py +50 -0
  670. chia/timelord/timelord.py +1202 -0
  671. chia/timelord/timelord_api.py +132 -0
  672. chia/timelord/timelord_launcher.py +188 -0
  673. chia/timelord/timelord_state.py +244 -0
  674. chia/timelord/types.py +22 -0
  675. chia/types/__init__.py +0 -0
  676. chia/types/aliases.py +35 -0
  677. chia/types/block_protocol.py +20 -0
  678. chia/types/blockchain_format/__init__.py +0 -0
  679. chia/types/blockchain_format/classgroup.py +5 -0
  680. chia/types/blockchain_format/coin.py +28 -0
  681. chia/types/blockchain_format/foliage.py +8 -0
  682. chia/types/blockchain_format/pool_target.py +5 -0
  683. chia/types/blockchain_format/program.py +270 -0
  684. chia/types/blockchain_format/proof_of_space.py +135 -0
  685. chia/types/blockchain_format/reward_chain_block.py +6 -0
  686. chia/types/blockchain_format/serialized_program.py +5 -0
  687. chia/types/blockchain_format/sized_bytes.py +11 -0
  688. chia/types/blockchain_format/slots.py +9 -0
  689. chia/types/blockchain_format/sub_epoch_summary.py +5 -0
  690. chia/types/blockchain_format/tree_hash.py +72 -0
  691. chia/types/blockchain_format/vdf.py +86 -0
  692. chia/types/clvm_cost.py +13 -0
  693. chia/types/coin_record.py +43 -0
  694. chia/types/coin_spend.py +115 -0
  695. chia/types/condition_opcodes.py +73 -0
  696. chia/types/condition_with_args.py +17 -0
  697. chia/types/eligible_coin_spends.py +364 -0
  698. chia/types/end_of_slot_bundle.py +5 -0
  699. chia/types/fee_rate.py +38 -0
  700. chia/types/full_block.py +5 -0
  701. chia/types/generator_types.py +14 -0
  702. chia/types/header_block.py +5 -0
  703. chia/types/internal_mempool_item.py +19 -0
  704. chia/types/mempool_inclusion_status.py +9 -0
  705. chia/types/mempool_item.py +85 -0
  706. chia/types/mempool_submission_status.py +30 -0
  707. chia/types/mojos.py +7 -0
  708. chia/types/peer_info.py +64 -0
  709. chia/types/signing_mode.py +29 -0
  710. chia/types/spend_bundle.py +31 -0
  711. chia/types/spend_bundle_conditions.py +7 -0
  712. chia/types/transaction_queue_entry.py +55 -0
  713. chia/types/unfinished_block.py +5 -0
  714. chia/types/unfinished_header_block.py +37 -0
  715. chia/types/weight_proof.py +50 -0
  716. chia/util/__init__.py +0 -0
  717. chia/util/action_scope.py +168 -0
  718. chia/util/api_decorators.py +89 -0
  719. chia/util/async_pool.py +224 -0
  720. chia/util/augmented_chain.py +130 -0
  721. chia/util/batches.py +39 -0
  722. chia/util/bech32m.py +123 -0
  723. chia/util/beta_metrics.py +118 -0
  724. chia/util/block_cache.py +56 -0
  725. chia/util/byte_types.py +10 -0
  726. chia/util/check_fork_next_block.py +32 -0
  727. chia/util/chia_logging.py +124 -0
  728. chia/util/chia_version.py +33 -0
  729. chia/util/collection.py +17 -0
  730. chia/util/condition_tools.py +201 -0
  731. chia/util/config.py +366 -0
  732. chia/util/cpu.py +20 -0
  733. chia/util/db_synchronous.py +21 -0
  734. chia/util/db_version.py +30 -0
  735. chia/util/db_wrapper.py +427 -0
  736. chia/util/default_root.py +10 -0
  737. chia/util/dump_keyring.py +93 -0
  738. chia/util/english.txt +2048 -0
  739. chia/util/errors.py +351 -0
  740. chia/util/file_keyring.py +480 -0
  741. chia/util/files.py +95 -0
  742. chia/util/full_block_utils.py +321 -0
  743. chia/util/generator_tools.py +62 -0
  744. chia/util/hash.py +29 -0
  745. chia/util/initial-config.yaml +675 -0
  746. chia/util/inline_executor.py +24 -0
  747. chia/util/ints.py +19 -0
  748. chia/util/json_util.py +41 -0
  749. chia/util/keychain.py +673 -0
  750. chia/util/keyring_wrapper.py +266 -0
  751. chia/util/limited_semaphore.py +39 -0
  752. chia/util/lock.py +47 -0
  753. chia/util/log_exceptions.py +29 -0
  754. chia/util/logging.py +34 -0
  755. chia/util/lru_cache.py +29 -0
  756. chia/util/math.py +20 -0
  757. chia/util/network.py +240 -0
  758. chia/util/paginator.py +46 -0
  759. chia/util/path.py +29 -0
  760. chia/util/permissions.py +19 -0
  761. chia/util/pprint.py +40 -0
  762. chia/util/prev_transaction_block.py +23 -0
  763. chia/util/priority_mutex.py +92 -0
  764. chia/util/profiler.py +194 -0
  765. chia/util/recursive_replace.py +22 -0
  766. chia/util/safe_cancel_task.py +14 -0
  767. chia/util/service_groups.py +47 -0
  768. chia/util/setproctitle.py +20 -0
  769. chia/util/significant_bits.py +30 -0
  770. chia/util/ssl_check.py +213 -0
  771. chia/util/streamable.py +654 -0
  772. chia/util/task_timing.py +378 -0
  773. chia/util/timing.py +64 -0
  774. chia/util/vdf_prover.py +31 -0
  775. chia/util/ws_message.py +66 -0
  776. chia/wallet/__init__.py +0 -0
  777. chia/wallet/cat_wallet/__init__.py +0 -0
  778. chia/wallet/cat_wallet/cat_constants.py +75 -0
  779. chia/wallet/cat_wallet/cat_info.py +47 -0
  780. chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
  781. chia/wallet/cat_wallet/cat_utils.py +163 -0
  782. chia/wallet/cat_wallet/cat_wallet.py +869 -0
  783. chia/wallet/cat_wallet/dao_cat_info.py +28 -0
  784. chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
  785. chia/wallet/cat_wallet/lineage_store.py +74 -0
  786. chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
  787. chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
  788. chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
  789. chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
  790. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
  791. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
  792. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
  793. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
  794. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
  795. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
  796. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
  797. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
  798. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
  799. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
  800. chia/wallet/coin_selection.py +188 -0
  801. chia/wallet/conditions.py +1326 -0
  802. chia/wallet/dao_wallet/__init__.py +0 -0
  803. chia/wallet/dao_wallet/dao_info.py +61 -0
  804. chia/wallet/dao_wallet/dao_utils.py +810 -0
  805. chia/wallet/dao_wallet/dao_wallet.py +2121 -0
  806. chia/wallet/db_wallet/__init__.py +0 -0
  807. chia/wallet/db_wallet/db_wallet_puzzles.py +107 -0
  808. chia/wallet/derivation_record.py +30 -0
  809. chia/wallet/derive_keys.py +146 -0
  810. chia/wallet/did_wallet/__init__.py +0 -0
  811. chia/wallet/did_wallet/did_info.py +39 -0
  812. chia/wallet/did_wallet/did_wallet.py +1485 -0
  813. chia/wallet/did_wallet/did_wallet_puzzles.py +220 -0
  814. chia/wallet/did_wallet/puzzles/__init__.py +0 -0
  815. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
  816. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
  817. chia/wallet/driver_protocol.py +26 -0
  818. chia/wallet/key_val_store.py +55 -0
  819. chia/wallet/lineage_proof.py +58 -0
  820. chia/wallet/nft_wallet/__init__.py +0 -0
  821. chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
  822. chia/wallet/nft_wallet/nft_info.py +120 -0
  823. chia/wallet/nft_wallet/nft_puzzles.py +305 -0
  824. chia/wallet/nft_wallet/nft_wallet.py +1686 -0
  825. chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
  826. chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
  827. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
  828. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
  829. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
  830. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
  831. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
  832. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
  833. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
  834. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
  835. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
  836. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
  837. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
  838. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
  839. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
  840. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
  841. chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
  842. chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
  843. chia/wallet/nft_wallet/uncurry_nft.py +217 -0
  844. chia/wallet/notification_manager.py +117 -0
  845. chia/wallet/notification_store.py +178 -0
  846. chia/wallet/outer_puzzles.py +84 -0
  847. chia/wallet/payment.py +34 -0
  848. chia/wallet/puzzle_drivers.py +118 -0
  849. chia/wallet/puzzles/__init__.py +0 -0
  850. chia/wallet/puzzles/augmented_condition.clsp +13 -0
  851. chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
  852. chia/wallet/puzzles/clawback/__init__.py +0 -0
  853. chia/wallet/puzzles/clawback/drivers.py +188 -0
  854. chia/wallet/puzzles/clawback/metadata.py +38 -0
  855. chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
  856. chia/wallet/puzzles/condition_codes.clib +77 -0
  857. chia/wallet/puzzles/curry-and-treehash.clib +102 -0
  858. chia/wallet/puzzles/curry.clib +135 -0
  859. chia/wallet/puzzles/curry_by_index.clib +16 -0
  860. chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
  861. chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
  862. chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
  863. chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
  864. chia/wallet/puzzles/dao_finished_state.clsp +35 -0
  865. chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
  866. chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
  867. chia/wallet/puzzles/dao_lockup.clsp +288 -0
  868. chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
  869. chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
  870. chia/wallet/puzzles/dao_proposal.clsp +377 -0
  871. chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
  872. chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
  873. chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
  874. chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
  875. chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
  876. chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
  877. chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
  878. chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
  879. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
  880. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
  881. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
  882. chia/wallet/puzzles/dao_treasury.clsp +115 -0
  883. chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
  884. chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
  885. chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
  886. chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
  887. chia/wallet/puzzles/json.clib +25 -0
  888. chia/wallet/puzzles/load_clvm.py +162 -0
  889. chia/wallet/puzzles/merkle_utils.clib +18 -0
  890. chia/wallet/puzzles/notification.clsp +7 -0
  891. chia/wallet/puzzles/notification.clsp.hex +1 -0
  892. chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
  893. chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
  894. chia/wallet/puzzles/p2_conditions.clsp +3 -0
  895. chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
  896. chia/wallet/puzzles/p2_conditions.py +27 -0
  897. chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
  898. chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
  899. chia/wallet/puzzles/p2_delegated_conditions.py +22 -0
  900. chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
  901. chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
  902. chia/wallet/puzzles/p2_delegated_puzzle.py +35 -0
  903. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
  904. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
  905. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +161 -0
  906. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
  907. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
  908. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +22 -0
  909. chia/wallet/puzzles/p2_parent.clsp +19 -0
  910. chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
  911. chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
  912. chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
  913. chia/wallet/puzzles/p2_puzzle_hash.py +28 -0
  914. chia/wallet/puzzles/p2_singleton.clsp +30 -0
  915. chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
  916. chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
  917. chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
  918. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
  919. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
  920. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
  921. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
  922. chia/wallet/puzzles/puzzle_utils.py +34 -0
  923. chia/wallet/puzzles/settlement_payments.clsp +49 -0
  924. chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
  925. chia/wallet/puzzles/sha256tree.clib +11 -0
  926. chia/wallet/puzzles/singleton_launcher.clsp +16 -0
  927. chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
  928. chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
  929. chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
  930. chia/wallet/puzzles/singleton_top_layer.py +295 -0
  931. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
  932. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
  933. chia/wallet/puzzles/singleton_top_layer_v1_1.py +344 -0
  934. chia/wallet/puzzles/singleton_truths.clib +21 -0
  935. chia/wallet/puzzles/tails.py +344 -0
  936. chia/wallet/puzzles/utility_macros.clib +48 -0
  937. chia/wallet/signer_protocol.py +126 -0
  938. chia/wallet/singleton.py +106 -0
  939. chia/wallet/singleton_record.py +30 -0
  940. chia/wallet/trade_manager.py +1088 -0
  941. chia/wallet/trade_record.py +67 -0
  942. chia/wallet/trading/__init__.py +0 -0
  943. chia/wallet/trading/offer.py +703 -0
  944. chia/wallet/trading/trade_status.py +13 -0
  945. chia/wallet/trading/trade_store.py +526 -0
  946. chia/wallet/transaction_record.py +143 -0
  947. chia/wallet/transaction_sorting.py +14 -0
  948. chia/wallet/uncurried_puzzle.py +17 -0
  949. chia/wallet/util/__init__.py +0 -0
  950. chia/wallet/util/address_type.py +55 -0
  951. chia/wallet/util/blind_signer_tl.py +168 -0
  952. chia/wallet/util/clvm_streamable.py +203 -0
  953. chia/wallet/util/compute_hints.py +66 -0
  954. chia/wallet/util/compute_memos.py +45 -0
  955. chia/wallet/util/curry_and_treehash.py +90 -0
  956. chia/wallet/util/debug_spend_bundle.py +234 -0
  957. chia/wallet/util/merkle_tree.py +100 -0
  958. chia/wallet/util/merkle_utils.py +102 -0
  959. chia/wallet/util/new_peak_queue.py +82 -0
  960. chia/wallet/util/notifications.py +12 -0
  961. chia/wallet/util/peer_request_cache.py +174 -0
  962. chia/wallet/util/puzzle_compression.py +96 -0
  963. chia/wallet/util/puzzle_decorator.py +100 -0
  964. chia/wallet/util/puzzle_decorator_type.py +7 -0
  965. chia/wallet/util/query_filter.py +60 -0
  966. chia/wallet/util/transaction_type.py +23 -0
  967. chia/wallet/util/tx_config.py +158 -0
  968. chia/wallet/util/wallet_sync_utils.py +348 -0
  969. chia/wallet/util/wallet_types.py +65 -0
  970. chia/wallet/vc_wallet/__init__.py +0 -0
  971. chia/wallet/vc_wallet/cr_cat_drivers.py +663 -0
  972. chia/wallet/vc_wallet/cr_cat_wallet.py +875 -0
  973. chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
  974. chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
  975. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
  976. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
  977. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
  978. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
  979. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
  980. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
  981. chia/wallet/vc_wallet/vc_drivers.py +838 -0
  982. chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
  983. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
  984. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
  985. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
  986. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
  987. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
  988. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
  989. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
  990. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
  991. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
  992. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
  993. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
  994. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
  995. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
  996. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
  997. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
  998. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
  999. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
  1000. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
  1001. chia/wallet/vc_wallet/vc_store.py +263 -0
  1002. chia/wallet/vc_wallet/vc_wallet.py +638 -0
  1003. chia/wallet/wallet.py +698 -0
  1004. chia/wallet/wallet_action_scope.py +95 -0
  1005. chia/wallet/wallet_blockchain.py +244 -0
  1006. chia/wallet/wallet_coin_record.py +72 -0
  1007. chia/wallet/wallet_coin_store.py +351 -0
  1008. chia/wallet/wallet_info.py +36 -0
  1009. chia/wallet/wallet_interested_store.py +188 -0
  1010. chia/wallet/wallet_nft_store.py +279 -0
  1011. chia/wallet/wallet_node.py +1769 -0
  1012. chia/wallet/wallet_node_api.py +201 -0
  1013. chia/wallet/wallet_pool_store.py +120 -0
  1014. chia/wallet/wallet_protocol.py +90 -0
  1015. chia/wallet/wallet_puzzle_store.py +365 -0
  1016. chia/wallet/wallet_retry_store.py +70 -0
  1017. chia/wallet/wallet_singleton_store.py +258 -0
  1018. chia/wallet/wallet_spend_bundle.py +41 -0
  1019. chia/wallet/wallet_state_manager.py +2820 -0
  1020. chia/wallet/wallet_transaction_store.py +470 -0
  1021. chia/wallet/wallet_user_store.py +110 -0
  1022. chia/wallet/wallet_weight_proof_handler.py +126 -0
  1023. chia_blockchain-2.4.4.dist-info/LICENSE +201 -0
  1024. chia_blockchain-2.4.4.dist-info/METADATA +161 -0
  1025. chia_blockchain-2.4.4.dist-info/RECORD +1028 -0
  1026. chia_blockchain-2.4.4.dist-info/WHEEL +4 -0
  1027. chia_blockchain-2.4.4.dist-info/entry_points.txt +17 -0
  1028. mozilla-ca/cacert.pem +3666 -0
chia/daemon/server.py ADDED
@@ -0,0 +1,1616 @@
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 time
14
+ import traceback
15
+ import uuid
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, AsyncIterator, Dict, List, Optional, Set, TextIO, Tuple
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.service_groups import validate_service
48
+ from chia.util.setproctitle import setproctitle
49
+ from chia.util.ws_message import WsRpcMessage, create_payload, format_response
50
+ from chia.wallet.derive_keys import (
51
+ master_pk_to_wallet_pk_unhardened,
52
+ master_sk_to_farmer_sk,
53
+ master_sk_to_pool_sk,
54
+ master_sk_to_wallet_sk,
55
+ )
56
+
57
+ io_pool_exc = ThreadPoolExecutor()
58
+
59
+ try:
60
+ from aiohttp import WSMsgType, web
61
+ from aiohttp.web_ws import WebSocketResponse
62
+ except ModuleNotFoundError:
63
+ print("Error: Make sure to run . ./activate from the project folder before starting Chia.")
64
+ sys.exit()
65
+
66
+
67
+ log = logging.getLogger(__name__)
68
+
69
+ service_plotter = "chia_plotter"
70
+
71
+
72
+ class PlotState(str, Enum):
73
+ SUBMITTED = "SUBMITTED"
74
+ RUNNING = "RUNNING"
75
+ REMOVING = "REMOVING"
76
+ FINISHED = "FINISHED"
77
+
78
+
79
+ class PlotEvent(str, Enum):
80
+ LOG_CHANGED = "log_changed"
81
+ STATE_CHANGED = "state_changed"
82
+
83
+
84
+ # determine if application is a script file or frozen exe
85
+ if getattr(sys, "frozen", False):
86
+ name_map = {
87
+ "chia": "chia",
88
+ "chia_data_layer": "start_data_layer",
89
+ "chia_data_layer_http": "start_data_layer_http",
90
+ "chia_wallet": "start_wallet",
91
+ "chia_full_node": "start_full_node",
92
+ "chia_harvester": "start_harvester",
93
+ "chia_farmer": "start_farmer",
94
+ "chia_introducer": "start_introducer",
95
+ "chia_timelord": "start_timelord",
96
+ "chia_timelord_launcher": "timelord_launcher",
97
+ "chia_full_node_simulator": "start_simulator",
98
+ "chia_seeder": "start_seeder",
99
+ "chia_crawler": "start_crawler",
100
+ }
101
+
102
+ def executable_for_service(service_name: str) -> str:
103
+ application_path = os.path.dirname(sys.executable)
104
+ if sys.platform == "win32" or sys.platform == "cygwin":
105
+ executable = name_map[service_name]
106
+ path = f"{application_path}/{executable}.exe"
107
+ return path
108
+ else:
109
+ path = f"{application_path}/{name_map[service_name]}"
110
+ return path
111
+
112
+ else:
113
+ application_path = os.path.dirname(__file__)
114
+
115
+ def executable_for_service(service_name: str) -> str:
116
+ cmd_to_exec = shutil.which(service_name)
117
+ return cmd_to_exec if cmd_to_exec is not None else service_name
118
+
119
+
120
+ async def ping() -> Dict[str, Any]:
121
+ response = {"success": True, "value": "pong"}
122
+ return response
123
+
124
+
125
+ class Command(Protocol):
126
+ async def __call__(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]: ...
127
+
128
+
129
+ def _get_keys_by_fingerprints(fingerprints: Optional[List[uint32]]) -> Tuple[List[KeyData], Set[uint32]]:
130
+ all_keys = Keychain().get_keys(include_secrets=True)
131
+ missing_fingerprints = set()
132
+
133
+ # if fingerprints is None, we want all keys, otherwise we want the keys that match the fingerprints
134
+ if fingerprints is None:
135
+ keys = all_keys
136
+ else:
137
+ if not isinstance(fingerprints, list):
138
+ raise ValueError("fingerprints must be a list of integer")
139
+ keys_by_fingerprint = {key.fingerprint: key for key in all_keys}
140
+ keys = []
141
+ for fingerprint in fingerprints:
142
+ f = uint32(fingerprint)
143
+ if f not in keys_by_fingerprint:
144
+ missing_fingerprints.add(f)
145
+ else:
146
+ keys.append(keys_by_fingerprint[f])
147
+ return keys, missing_fingerprints
148
+
149
+
150
+ @dataclasses.dataclass(frozen=True)
151
+ class StatusMessage:
152
+ service: str
153
+ command: str
154
+ destination: str
155
+ origin: str
156
+ data: Dict[str, Any]
157
+
158
+ def create_payload(self) -> str:
159
+ return create_payload(command=self.command, data=self.data, origin=self.origin, destination=self.destination)
160
+
161
+
162
+ class WebSocketServer:
163
+ def __init__(
164
+ self,
165
+ root_path: Path,
166
+ ca_crt_path: Path,
167
+ ca_key_path: Path,
168
+ crt_path: Path,
169
+ key_path: Path,
170
+ run_check_keys_on_unlock: bool = False,
171
+ ):
172
+ self.root_path = root_path
173
+ self.log = log
174
+ self.services: Dict[str, List[subprocess.Popen]] = dict()
175
+ self.plots_queue: List[Dict] = []
176
+ self.connections: Dict[str, Set[WebSocketResponse]] = dict() # service name : {WebSocketResponse}
177
+ self.ping_job: Optional[asyncio.Task] = None
178
+ self.net_config = load_config(root_path, "config.yaml")
179
+ self.self_hostname = self.net_config["self_hostname"]
180
+ self.daemon_port = self.net_config["daemon_port"]
181
+ self.daemon_max_message_size = self.net_config.get("daemon_max_message_size", 50 * 1000 * 1000)
182
+ self.heartbeat = self.net_config.get("daemon_heartbeat", 300)
183
+ self.webserver: Optional[WebServer] = None
184
+ self.ssl_context = ssl_context_for_server(ca_crt_path, ca_key_path, crt_path, key_path, log=self.log)
185
+ self.keychain_server = KeychainServer()
186
+ self.run_check_keys_on_unlock = run_check_keys_on_unlock
187
+ self.shutdown_event = asyncio.Event()
188
+ self.state_changed_msg_queue: asyncio.Queue[StatusMessage] = asyncio.Queue()
189
+ self.state_changed_task: Optional[asyncio.Task] = None
190
+
191
+ @asynccontextmanager
192
+ async def run(self) -> AsyncIterator[None]:
193
+ self.log.info(f"Starting Daemon Server ({self.self_hostname}:{self.daemon_port})")
194
+
195
+ # Note: the minimum_version has been already set to TLSv1_2
196
+ # in ssl_context_for_server()
197
+ # Daemon is internal connections, so override to TLSv1_3 only unless specified in the config
198
+ if ssl.HAS_TLSv1_3 and not self.net_config.get("daemon_allow_tls_1_2", False):
199
+ try:
200
+ self.ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
201
+ except ValueError:
202
+ # in case the attempt above confused the config, set it again (likely not needed but doesn't hurt)
203
+ self.ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
204
+
205
+ if self.ssl_context.minimum_version is not ssl.TLSVersion.TLSv1_3:
206
+ self.log.warning(
207
+ (
208
+ "Deprecation Warning: Your version of SSL (%s) does not support TLS1.3. "
209
+ "A future version of Chia will require TLS1.3."
210
+ ),
211
+ ssl.OPENSSL_VERSION,
212
+ )
213
+
214
+ self.state_changed_task = asyncio.create_task(self._process_state_changed_queue())
215
+ self.webserver = await WebServer.create(
216
+ hostname=self.self_hostname,
217
+ port=self.daemon_port,
218
+ keepalive_timeout=300,
219
+ shutdown_timeout=3,
220
+ routes=[web.get("/", self.incoming_connection)],
221
+ ssl_context=self.ssl_context,
222
+ logger=self.log,
223
+ )
224
+ try:
225
+ yield
226
+ finally:
227
+ if not self.shutdown_event.is_set():
228
+ await self.stop()
229
+ await self.exit()
230
+
231
+ async def setup_process_global_state(self, signal_handlers: SignalHandlers) -> None:
232
+ signal_handlers.setup_async_signal_handler(handler=self._accept_signal)
233
+
234
+ async def _accept_signal(
235
+ self,
236
+ signal_: signal.Signals,
237
+ stack_frame: Optional[FrameType],
238
+ loop: asyncio.AbstractEventLoop,
239
+ ) -> None:
240
+ self.log.info("Received signal %s (%s), shutting down.", signal_.name, signal_.value)
241
+ await self.stop()
242
+
243
+ def cancel_task_safe(self, task: Optional[asyncio.Task]):
244
+ if task is not None:
245
+ try:
246
+ task.cancel()
247
+ except Exception as e:
248
+ self.log.error(f"Error while canceling task.{e} {task}")
249
+
250
+ async def stop_command(self, websocket: WebSocketResponse, request: Dict[str, Any] = {}) -> Dict[str, Any]:
251
+ return await self.stop()
252
+
253
+ async def stop(self) -> Dict[str, Any]:
254
+ self.cancel_task_safe(self.ping_job)
255
+ self.cancel_task_safe(self.state_changed_task)
256
+ service_names = list(self.services.keys())
257
+ stop_service_jobs = [
258
+ asyncio.create_task(kill_service(self.root_path, self.services, s_n)) for s_n in service_names
259
+ ]
260
+ if stop_service_jobs:
261
+ await asyncio.wait(stop_service_jobs)
262
+ self.services.clear()
263
+ self.shutdown_event.set()
264
+ log.info(f"Daemon Server stopping, Services stopped: {service_names}")
265
+ return {"success": True, "services_stopped": service_names}
266
+
267
+ async def incoming_connection(self, request: web.Request) -> web.StreamResponse:
268
+ ws: WebSocketResponse = web.WebSocketResponse(
269
+ max_msg_size=self.daemon_max_message_size, heartbeat=self.heartbeat
270
+ )
271
+ await ws.prepare(request)
272
+
273
+ while True:
274
+ msg = await ws.receive()
275
+ self.log.debug("Received message: %s", msg)
276
+ decoded: WsRpcMessage = {
277
+ "command": "",
278
+ "ack": False,
279
+ "data": {},
280
+ "request_id": "",
281
+ "destination": "",
282
+ "origin": "",
283
+ }
284
+ if msg.type == WSMsgType.TEXT:
285
+ try:
286
+ decoded = json.loads(msg.data)
287
+ if "data" not in decoded:
288
+ decoded["data"] = {}
289
+
290
+ maybe_response = await self.handle_message(ws, decoded)
291
+ if maybe_response is None:
292
+ continue
293
+
294
+ response, connections = maybe_response
295
+
296
+ except Exception as e:
297
+ tb = traceback.format_exc()
298
+ self.log.error(f"Error while handling message: {tb}")
299
+ error = {"success": False, "error": f"{e}"}
300
+ response = format_response(decoded, error)
301
+ connections = {ws} # send error back to the sender
302
+
303
+ await self.send_all_responses(connections, response)
304
+ else:
305
+ service_names = self.remove_connection(ws)
306
+
307
+ if len(service_names) == 0:
308
+ service_names = ["Unknown"]
309
+
310
+ closing_message = f"Closing websocket with {service_names}."
311
+
312
+ level = logging.INFO
313
+ if msg.type == WSMsgType.CLOSED:
314
+ message = f"Connection closed. {closing_message}"
315
+ elif msg.type == WSMsgType.CLOSING:
316
+ message = f"Connection closing. {closing_message}"
317
+ elif msg.type == WSMsgType.CLOSE:
318
+ message = f"Connection close requested. {closing_message}"
319
+ elif msg.type == WSMsgType.ERROR:
320
+ level = logging.ERROR
321
+ message = f"Websocket exception. {closing_message} {ws.exception()}"
322
+ else:
323
+ level = logging.ERROR
324
+ message = f"Unexpected message type. {closing_message} {msg.type}"
325
+
326
+ self.log.log(level=level, msg=message)
327
+
328
+ await ws.close()
329
+ break
330
+
331
+ return ws
332
+
333
+ async def send_all_responses(self, connections: Set[WebSocketResponse], response: str) -> None:
334
+ for connection in connections.copy():
335
+ try:
336
+ await connection.send_str(response)
337
+ except Exception as e:
338
+ service_names = self.remove_connection(connection)
339
+ if len(service_names) == 0:
340
+ service_names = ["Unknown"]
341
+
342
+ if isinstance(e, ConnectionResetError):
343
+ self.log.info(f"Peer disconnected. Closing websocket with {service_names}")
344
+ else:
345
+ tb = traceback.format_exc()
346
+ self.log.error(f"Unexpected exception trying to send to {service_names} (websocket: {e} {tb})")
347
+ self.log.info(f"Closing websocket with {service_names}")
348
+
349
+ await connection.close()
350
+
351
+ def remove_connection(self, websocket: WebSocketResponse) -> List[str]:
352
+ """Returns a list of service names from which the connection was removed"""
353
+ service_names = []
354
+ for service_name, connections in self.connections.items():
355
+ try:
356
+ connections.remove(websocket)
357
+ except KeyError:
358
+ continue
359
+ service_names.append(service_name)
360
+ return service_names
361
+
362
+ async def ping_task(self) -> None:
363
+ with log_exceptions(
364
+ log=self.log,
365
+ consume=True,
366
+ message="Ping task received Cancel",
367
+ level=logging.DEBUG,
368
+ show_traceback=False,
369
+ exceptions_to_process=asyncio.CancelledError,
370
+ ):
371
+ while True:
372
+ with log_exceptions(
373
+ log=self.log,
374
+ consume=True,
375
+ message="Unexpected exception, continuing:",
376
+ ):
377
+ await asyncio.sleep(30)
378
+
379
+ for service_name, connections in self.connections.items():
380
+ if service_name == service_plotter:
381
+ continue
382
+
383
+ for connection in connections.copy():
384
+ self.log.debug(f"About to ping: {service_name}")
385
+ try:
386
+ with log_exceptions(
387
+ log=self.log,
388
+ message=f"Ping error to {service_name}, closing connection.",
389
+ level=logging.WARNING,
390
+ show_traceback=False,
391
+ ):
392
+ await connection.ping()
393
+ except: # noqa E722
394
+ self.remove_connection(connection)
395
+ await connection.close()
396
+
397
+ async def handle_message(
398
+ self, websocket: WebSocketResponse, message: WsRpcMessage
399
+ ) -> Optional[Tuple[str, Set[WebSocketResponse]]]:
400
+ """
401
+ This function gets called when new message is received via websocket.
402
+ """
403
+
404
+ command = message["command"]
405
+ destination = message["destination"]
406
+ if destination != "daemon":
407
+ if destination in self.connections:
408
+ sockets = self.connections[destination]
409
+ return dict_to_json_str(message), sockets
410
+
411
+ return None
412
+
413
+ data = message["data"]
414
+ commands_with_data = [
415
+ "start_service",
416
+ "start_plotting",
417
+ "stop_plotting",
418
+ "stop_service",
419
+ "is_running",
420
+ "register_service",
421
+ ]
422
+ if len(data) == 0 and command in commands_with_data:
423
+ response = {"success": False, "error": f'{command} requires "data"'}
424
+ # Keychain commands should be handled by KeychainServer
425
+ elif command in keychain_commands:
426
+ response = await self.keychain_server.handle_command(command, data)
427
+ elif command == "ping":
428
+ response = await ping()
429
+ else:
430
+ command_mapping = self.get_command_mapping()
431
+ if command in command_mapping:
432
+ response = await command_mapping[command](websocket=websocket, request=data)
433
+ else:
434
+ self.log.error(f"UK>> {message}")
435
+ response = {"success": False, "error": f"unknown_command {command}"}
436
+
437
+ full_response = format_response(message, response)
438
+ return full_response, {websocket}
439
+
440
+ def get_command_mapping(self) -> Dict[str, Command]:
441
+ """
442
+ Returns a mapping of commands to their respective function calls.
443
+ """
444
+ return {
445
+ "start_service": self.start_service,
446
+ "start_plotting": self.start_plotting,
447
+ "stop_plotting": self.stop_plotting,
448
+ "stop_service": self.stop_service,
449
+ "is_running": self.is_running_command,
450
+ "running_services": self.running_services_command,
451
+ "is_keyring_locked": self.is_keyring_locked,
452
+ "keyring_status": self.keyring_status_command,
453
+ "unlock_keyring": self.unlock_keyring,
454
+ "validate_keyring_passphrase": self.validate_keyring_passphrase,
455
+ "set_keyring_passphrase": self.set_keyring_passphrase,
456
+ "remove_keyring_passphrase": self.remove_keyring_passphrase,
457
+ "exit": self.stop_command,
458
+ "register_service": self.register_service,
459
+ "get_status": self.get_status,
460
+ "get_version": self.get_version,
461
+ "get_plotters": self.get_plotters,
462
+ "get_routes": self.get_routes,
463
+ "get_wallet_addresses": self.get_wallet_addresses,
464
+ "get_keys_for_plotting": self.get_keys_for_plotting,
465
+ "get_network_info": self.get_network_info,
466
+ }
467
+
468
+ async def get_network_info(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
469
+ network_name = self.net_config["selected_network"]
470
+ address_prefix = self.net_config["network_overrides"]["config"][network_name]["address_prefix"]
471
+ genesis_challenge = self.net_config["network_overrides"]["constants"][network_name]["GENESIS_CHALLENGE"]
472
+ response: Dict[str, Any] = {
473
+ "success": True,
474
+ "network_name": network_name,
475
+ "network_prefix": address_prefix,
476
+ "genesis_challenge": genesis_challenge,
477
+ }
478
+ return response
479
+
480
+ async def is_keyring_locked(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
481
+ locked: bool = Keychain.is_keyring_locked()
482
+ response: Dict[str, Any] = {"success": True, "is_keyring_locked": locked}
483
+ return response
484
+
485
+ async def keyring_status_command(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
486
+ return await self.keyring_status()
487
+
488
+ async def keyring_status(self) -> Dict[str, Any]:
489
+ can_save_passphrase: bool = supports_os_passphrase_storage()
490
+ user_passphrase_is_set: bool = Keychain.has_master_passphrase() and not using_default_passphrase()
491
+ locked: bool = Keychain.is_keyring_locked()
492
+ can_set_passphrase_hint: bool = True
493
+ passphrase_hint: str = Keychain.get_master_passphrase_hint() or ""
494
+ requirements: Dict[str, Any] = passphrase_requirements()
495
+ response: Dict[str, Any] = {
496
+ "success": True,
497
+ "is_keyring_locked": locked,
498
+ "can_save_passphrase": can_save_passphrase,
499
+ "user_passphrase_is_set": user_passphrase_is_set,
500
+ "can_set_passphrase_hint": can_set_passphrase_hint,
501
+ "passphrase_hint": passphrase_hint,
502
+ "passphrase_requirements": requirements,
503
+ }
504
+ # Help diagnose GUI launch issues
505
+ self.log.debug(f"Keyring status: {response}")
506
+ return response
507
+
508
+ async def unlock_keyring(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
509
+ success: bool = False
510
+ error: Optional[str] = None
511
+ key: Optional[str] = request.get("key", None)
512
+ if type(key) is not str:
513
+ return {"success": False, "error": "missing key"}
514
+
515
+ try:
516
+ if Keychain.master_passphrase_is_valid(key, force_reload=True):
517
+ Keychain.set_cached_master_passphrase(key)
518
+ success = True
519
+ # Inform the GUI of keyring status changes
520
+ self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
521
+ else:
522
+ error = "bad passphrase"
523
+ except Exception as e:
524
+ tb = traceback.format_exc()
525
+ self.log.error(f"Keyring passphrase validation failed: {e} {tb}")
526
+ error = "validation exception"
527
+
528
+ if success and self.run_check_keys_on_unlock:
529
+ try:
530
+ self.log.info("Running check_keys now that the keyring is unlocked")
531
+ check_keys(self.root_path)
532
+ self.run_check_keys_on_unlock = False
533
+ except Exception as e:
534
+ tb = traceback.format_exc()
535
+ self.log.error(f"check_keys failed after unlocking keyring: {e} {tb}")
536
+
537
+ response: Dict[str, Any] = {"success": success, "error": error}
538
+ return response
539
+
540
+ async def validate_keyring_passphrase(
541
+ self,
542
+ websocket: WebSocketResponse,
543
+ request: Dict[str, Any],
544
+ ) -> Dict[str, Any]:
545
+ success: bool = False
546
+ error: Optional[str] = None
547
+ key: Optional[str] = request.get("key", None)
548
+ if type(key) is not str:
549
+ return {"success": False, "error": "missing key"}
550
+
551
+ try:
552
+ success = Keychain.master_passphrase_is_valid(key, force_reload=True)
553
+ except Exception as e:
554
+ tb = traceback.format_exc()
555
+ self.log.error(f"Keyring passphrase validation failed: {e} {tb}")
556
+ error = "validation exception"
557
+
558
+ response: Dict[str, Any] = {"success": success, "error": error}
559
+ return response
560
+
561
+ async def set_keyring_passphrase(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
562
+ success: bool = False
563
+ error: Optional[str] = None
564
+ current_passphrase: Optional[str] = None
565
+ new_passphrase: Optional[str] = None
566
+ passphrase_hint: Optional[str] = request.get("passphrase_hint", None)
567
+ save_passphrase: bool = request.get("save_passphrase", False)
568
+
569
+ if using_default_passphrase():
570
+ current_passphrase = default_passphrase()
571
+
572
+ if Keychain.has_master_passphrase() and not current_passphrase:
573
+ current_passphrase = request.get("current_passphrase", None)
574
+ if type(current_passphrase) is not str:
575
+ return {"success": False, "error": "missing current_passphrase"}
576
+
577
+ new_passphrase = request.get("new_passphrase", None)
578
+ if type(new_passphrase) is not str:
579
+ return {"success": False, "error": "missing new_passphrase"}
580
+
581
+ if not Keychain.passphrase_meets_requirements(new_passphrase):
582
+ return {"success": False, "error": "passphrase doesn't satisfy requirements"}
583
+
584
+ try:
585
+ assert new_passphrase is not None # mypy, I love you
586
+ Keychain.set_master_passphrase(
587
+ current_passphrase,
588
+ new_passphrase,
589
+ passphrase_hint=passphrase_hint,
590
+ save_passphrase=save_passphrase,
591
+ )
592
+ except KeychainCurrentPassphraseIsInvalid:
593
+ error = "current passphrase is invalid"
594
+ except Exception as e:
595
+ tb = traceback.format_exc()
596
+ self.log.error(f"Failed to set keyring passphrase: {e} {tb}")
597
+ else:
598
+ success = True
599
+ # Inform the GUI of keyring status changes
600
+ self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
601
+
602
+ response: Dict[str, Any] = {"success": success, "error": error}
603
+ return response
604
+
605
+ async def remove_keyring_passphrase(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
606
+ success: bool = False
607
+ error: Optional[str] = None
608
+ current_passphrase: Optional[str] = None
609
+
610
+ if not Keychain.has_master_passphrase():
611
+ return {"success": False, "error": "passphrase not set"}
612
+
613
+ current_passphrase = request.get("current_passphrase", None)
614
+ if type(current_passphrase) is not str:
615
+ return {"success": False, "error": "missing current_passphrase"}
616
+
617
+ try:
618
+ Keychain.remove_master_passphrase(current_passphrase)
619
+ except KeychainCurrentPassphraseIsInvalid:
620
+ error = "current passphrase is invalid"
621
+ except Exception as e:
622
+ tb = traceback.format_exc()
623
+ self.log.error(f"Failed to remove keyring passphrase: {e} {tb}")
624
+ else:
625
+ success = True
626
+ # Inform the GUI of keyring status changes
627
+ self.keyring_status_changed(await self.keyring_status(), "wallet_ui")
628
+
629
+ response: Dict[str, Any] = {"success": success, "error": error}
630
+ return response
631
+
632
+ async def get_status(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
633
+ response = {"success": True, "genesis_initialized": True}
634
+ return response
635
+
636
+ async def get_version(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
637
+ response = {"success": True, "version": __version__}
638
+ return response
639
+
640
+ async def get_plotters(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
641
+ plotters: Dict[str, Any] = get_available_plotters(self.root_path)
642
+ response: Dict[str, Any] = {"success": True, "plotters": plotters}
643
+ return response
644
+
645
+ async def get_routes(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
646
+ routes = list(self.get_command_mapping().keys())
647
+ response: Dict[str, Any] = {"success": True, "routes": routes}
648
+ return response
649
+
650
+ async def get_wallet_addresses(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
651
+ fingerprints = request.get("fingerprints", None)
652
+ keys, missing_fingerprints = _get_keys_by_fingerprints(fingerprints)
653
+ if len(missing_fingerprints) > 0:
654
+ return {"success": False, "error": f"key(s) not found for fingerprint(s) {missing_fingerprints}"}
655
+
656
+ index = request.get("index", 0)
657
+ count = request.get("count", 1)
658
+ non_observer_derivation = request.get("non_observer_derivation", False)
659
+
660
+ selected = self.net_config["selected_network"]
661
+ prefix = self.net_config["network_overrides"]["config"][selected]["address_prefix"]
662
+
663
+ wallet_addresses_by_fingerprint = {}
664
+ for key in keys:
665
+ address_entries = []
666
+
667
+ # we require access to the private key to generate wallet addresses for non observer
668
+ if key.secrets is None and non_observer_derivation:
669
+ return {"success": False, "error": f"missing private key for key with fingerprint {key.fingerprint}"}
670
+
671
+ for i in range(index, index + count):
672
+ if non_observer_derivation:
673
+ sk = master_sk_to_wallet_sk(key.private_key, uint32(i))
674
+ pk = sk.get_g1()
675
+ else:
676
+ pk = master_pk_to_wallet_pk_unhardened(key.public_key, uint32(i))
677
+ wallet_address = encode_puzzle_hash(create_puzzlehash_for_pk(pk), prefix)
678
+ if non_observer_derivation:
679
+ hd_path = f"m/12381n/8444n/2n/{i}n"
680
+ else:
681
+ hd_path = f"m/12381/8444/2/{i}"
682
+
683
+ address_entries.append({"address": wallet_address, "hd_path": hd_path})
684
+
685
+ wallet_addresses_by_fingerprint[key.fingerprint] = address_entries
686
+
687
+ response: Dict[str, Any] = {"success": True, "wallet_addresses": wallet_addresses_by_fingerprint}
688
+ return response
689
+
690
+ async def get_keys_for_plotting(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
691
+ fingerprints = request.get("fingerprints", None)
692
+ keys, missing_fingerprints = _get_keys_by_fingerprints(fingerprints)
693
+ if len(missing_fingerprints) > 0:
694
+ return {"success": False, "error": f"key(s) not found for fingerprint(s) {missing_fingerprints}"}
695
+
696
+ keys_for_plot: Dict[uint32, Any] = {}
697
+ for key in keys:
698
+ if key.secrets is None:
699
+ continue
700
+ sk = key.private_key
701
+ farmer_public_key: G1Element = master_sk_to_farmer_sk(sk).get_g1()
702
+ pool_public_key: G1Element = master_sk_to_pool_sk(sk).get_g1()
703
+ keys_for_plot[key.fingerprint] = {
704
+ "farmer_public_key": bytes(farmer_public_key).hex(),
705
+ "pool_public_key": bytes(pool_public_key).hex(),
706
+ }
707
+ response: Dict[str, Any] = {
708
+ "success": True,
709
+ "keys": keys_for_plot,
710
+ }
711
+ return response
712
+
713
+ def plot_queue_to_payload(self, plot_queue_item, send_full_log: bool) -> Dict[str, Any]:
714
+ error = plot_queue_item.get("error")
715
+ has_error = error is not None
716
+
717
+ item = {
718
+ "id": plot_queue_item["id"],
719
+ "queue": plot_queue_item["queue"],
720
+ "size": plot_queue_item["size"],
721
+ "parallel": plot_queue_item["parallel"],
722
+ "delay": plot_queue_item["delay"],
723
+ "state": plot_queue_item["state"],
724
+ "error": str(error) if has_error else None,
725
+ "deleted": plot_queue_item["deleted"],
726
+ "log_new": plot_queue_item.get("log_new"),
727
+ }
728
+
729
+ if send_full_log:
730
+ item["log"] = plot_queue_item.get("log")
731
+ return item
732
+
733
+ def prepare_plot_state_message(self, state: PlotEvent, id):
734
+ message = {
735
+ "state": state,
736
+ "queue": self.extract_plot_queue(id),
737
+ }
738
+ return message
739
+
740
+ def extract_plot_queue(self, id=None) -> List[Dict]:
741
+ send_full_log = id is None
742
+ data = []
743
+ for item in self.plots_queue:
744
+ if id is None or item["id"] == id:
745
+ data.append(self.plot_queue_to_payload(item, send_full_log))
746
+ return data
747
+
748
+ async def _process_state_changed_queue(self) -> None:
749
+ with log_exceptions(
750
+ log=self.log,
751
+ consume=True,
752
+ message="State changed task received Cancel",
753
+ level=logging.DEBUG,
754
+ show_traceback=False,
755
+ exceptions_to_process=asyncio.CancelledError,
756
+ ):
757
+ while True:
758
+ with log_exceptions(
759
+ log=self.log,
760
+ consume=True,
761
+ message="Unexpected exception, continuing:",
762
+ ):
763
+ message = await self.state_changed_msg_queue.get()
764
+ await self._state_changed(message)
765
+
766
+ async def _state_changed(self, message: StatusMessage) -> None:
767
+ """If id is None, send the whole state queue"""
768
+ if message.service not in self.connections:
769
+ return None
770
+
771
+ websockets = self.connections[message.service]
772
+ for websocket in websockets.copy():
773
+ try:
774
+ await websocket.send_str(message.create_payload())
775
+ except Exception as e:
776
+ tb = traceback.format_exc()
777
+ self.log.error(f"Unexpected exception trying to send to websocket: {e} {tb}")
778
+ websockets.remove(websocket)
779
+ await websocket.close()
780
+
781
+ def state_changed(self, service: str, message: Dict[str, Any]) -> None:
782
+ self.state_changed_msg_queue.put_nowait(
783
+ StatusMessage(
784
+ service=service, command="state_changed", destination="wallet_ui", origin=service, data=message
785
+ )
786
+ )
787
+
788
+ def keyring_status_changed(self, keyring_status: Dict[str, Any], destination: str) -> None:
789
+ self.state_changed_msg_queue.put_nowait(
790
+ StatusMessage(
791
+ service="wallet_ui",
792
+ command="keyring_status_changed",
793
+ destination=destination,
794
+ origin="daemon",
795
+ data=keyring_status,
796
+ )
797
+ )
798
+
799
+ async def _watch_file_changes(self, config, fp: TextIO, loop: asyncio.AbstractEventLoop):
800
+ id: str = config["id"]
801
+ plotter: str = config["plotter"]
802
+ final_words: List[str] = []
803
+
804
+ if plotter == "chiapos":
805
+ final_words = ["Renamed final file"]
806
+ elif plotter == "bladebit":
807
+ if "cudaplot" in config["command_args"]:
808
+ final_words = ["Completed writing plot"]
809
+ else:
810
+ final_words = ["Finished plotting in"]
811
+ elif plotter == "madmax":
812
+ temp_dir = config["temp_dir"]
813
+ final_dir = config["final_dir"]
814
+ if temp_dir == final_dir:
815
+ final_words = ["Total plot creation time was"]
816
+ else:
817
+ # "Renamed final plot" if moving to a final dir on the same volume
818
+ # "Copy to <path> finished, took..." if copying to another volume
819
+ final_words = ["Renamed final plot", "finished, took"]
820
+
821
+ while True:
822
+ new_data = await loop.run_in_executor(io_pool_exc, fp.readline)
823
+
824
+ if config["state"] is not PlotState.RUNNING:
825
+ return None
826
+
827
+ if new_data not in (None, ""):
828
+ config["log"] = new_data if config["log"] is None else config["log"] + new_data
829
+ config["log_new"] = new_data
830
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.LOG_CHANGED, id))
831
+
832
+ if new_data:
833
+ for word in final_words:
834
+ if word in new_data:
835
+ return None
836
+ else:
837
+ time.sleep(0.5)
838
+
839
+ async def _track_plotting_progress(self, config, loop: asyncio.AbstractEventLoop):
840
+ file_path = config["out_file"]
841
+ with open(file_path) as fp:
842
+ await self._watch_file_changes(config, fp, loop)
843
+
844
+ def _common_plotting_command_args(self, request: Any, ignoreCount: bool) -> List[str]:
845
+ n = 1 if ignoreCount else request["n"] # Plot count
846
+ d = request["d"] # Final directory
847
+ r = request["r"] # Threads
848
+ f = request.get("f") # Farmer pubkey
849
+ p = request.get("p") # Pool pubkey
850
+ c = request.get("c") # Pool contract address
851
+
852
+ command_args: List[str] = ["-n", str(n), "-d", d, "-r", str(r)]
853
+
854
+ if f is not None:
855
+ command_args.append("-f")
856
+ command_args.append(str(f))
857
+ if p is not None:
858
+ command_args.append("-p")
859
+ command_args.append(str(p))
860
+ if c is not None:
861
+ command_args.append("-c")
862
+ command_args.append(str(c))
863
+
864
+ return command_args
865
+
866
+ def _chiapos_plotting_command_args(self, request: Any, ignoreCount: bool) -> List[str]:
867
+ k = request["k"] # Plot size
868
+ t = request["t"] # Temp directory
869
+ t2 = request.get("t2") # Temp2 directory
870
+ b = request["b"] # Buffer size
871
+ u = request["u"] # Buckets
872
+ a = request.get("a") # Fingerprint
873
+ e = request["e"] # Disable bitfield
874
+ x = request["x"] # Exclude final directory
875
+ override_k = request["overrideK"] # Force plot sizes < k32
876
+
877
+ command_args: List[str] = ["-k", str(k), "-t", t, "-b", str(b), "-u", str(u)]
878
+
879
+ if t2 is not None:
880
+ command_args.append("-2")
881
+ command_args.append(str(t2))
882
+ if a is not None:
883
+ command_args.append("-a")
884
+ command_args.append(str(a))
885
+ if e is True:
886
+ command_args.append("-e")
887
+ if x is True:
888
+ command_args.append("-x")
889
+ if override_k is True:
890
+ command_args.append("--override-k")
891
+
892
+ return command_args
893
+
894
+ def _bladebit_plotting_command_args(self, request: Any, ignoreCount: bool) -> List[str]:
895
+ plot_type = request["plot_type"]
896
+ if plot_type not in ["ramplot", "diskplot", "cudaplot"]:
897
+ raise ValueError(f"Unknown plot_type: {plot_type}")
898
+
899
+ command_args: List[str] = []
900
+
901
+ # Common options among diskplot, ramplot, cudaplot
902
+ w = request.get("w", False) # Warm start
903
+ m = request.get("m", False) # Disable NUMA
904
+ no_cpu_affinity = request.get("no_cpu_affinity", False)
905
+ compress = request.get("compress", None) # Compression level
906
+
907
+ if w is True:
908
+ command_args.append("--warmstart")
909
+ if m is True:
910
+ command_args.append("--nonuma")
911
+ if no_cpu_affinity is True:
912
+ command_args.append("--no-cpu-affinity")
913
+ if compress is not None and str(compress).isdigit():
914
+ command_args.append("--compress")
915
+ command_args.append(str(compress))
916
+
917
+ # ramplot don't accept any more options
918
+ if plot_type == "ramplot":
919
+ return command_args
920
+
921
+ # Options only applicable for cudaplot
922
+ if plot_type == "cudaplot":
923
+ device_index = request.get("device", None)
924
+ t1 = request.get("t", None) # Temp directory
925
+ t2 = request.get("t2", None) # Temp2 directory
926
+ disk_128 = request.get("disk_128", False)
927
+ disk_16 = request.get("disk_16", False)
928
+
929
+ if device_index is not None and str(device_index).isdigit():
930
+ command_args.append("--device")
931
+ command_args.append(str(device_index))
932
+ if t1 is not None:
933
+ command_args.append("-t")
934
+ command_args.append(t1)
935
+ if t2 is not None:
936
+ command_args.append("-2")
937
+ command_args.append(t2)
938
+ if disk_128:
939
+ command_args.append("--disk-128")
940
+ if disk_16:
941
+ command_args.append("--disk-16")
942
+ return command_args
943
+
944
+ # if plot_type == "diskplot"
945
+ # memo = request["memo"]
946
+ t1 = request["t"] # Temp directory
947
+ t2 = request.get("t2") # Temp2 directory
948
+ u = request.get("u") # Buckets
949
+ cache = request.get("cache")
950
+ f1_threads = request.get("f1_threads")
951
+ fp_threads = request.get("fp_threads")
952
+ c_threads = request.get("c_threads")
953
+ p2_threads = request.get("p2_threads")
954
+ p3_threads = request.get("p3_threads")
955
+ alternate = request.get("alternate", False)
956
+ no_t1_direct = request.get("no_t1_direct", False)
957
+ no_t2_direct = request.get("no_t2_direct", False)
958
+
959
+ command_args.append("-t")
960
+ command_args.append(t1)
961
+ if t2 is not None:
962
+ command_args.append("-2")
963
+ command_args.append(t2)
964
+ if u is not None:
965
+ command_args.append("-u")
966
+ command_args.append(str(u))
967
+ if cache is not None:
968
+ command_args.append("--cache")
969
+ command_args.append(str(cache))
970
+ if f1_threads is not None:
971
+ command_args.append("--f1-threads")
972
+ command_args.append(str(f1_threads))
973
+ if fp_threads is not None:
974
+ command_args.append("--fp-threads")
975
+ command_args.append(str(fp_threads))
976
+ if c_threads is not None:
977
+ command_args.append("--c-threads")
978
+ command_args.append(str(c_threads))
979
+ if p2_threads is not None:
980
+ command_args.append("--p2-threads")
981
+ command_args.append(str(p2_threads))
982
+ if p3_threads is not None:
983
+ command_args.append("--p3-threads")
984
+ command_args.append(str(p3_threads))
985
+ if alternate is not None:
986
+ command_args.append("--alternate")
987
+ if no_t1_direct is not None:
988
+ command_args.append("--no-t1-direct")
989
+ if no_t2_direct is not None:
990
+ command_args.append("--no-t2-direct")
991
+
992
+ return command_args
993
+
994
+ def _madmax_plotting_command_args(self, request: Any, ignoreCount: bool, index: int) -> List[str]:
995
+ k = request["k"] # Plot size
996
+ t = request["t"] # Temp directory
997
+ t2 = request["t2"] # Temp2 directory
998
+ u = request["u"] # Buckets
999
+ v = request["v"] # Buckets for phase 3 & 4
1000
+ K = request.get("K", 1) # Thread multiplier for phase 2
1001
+ G = request.get("G", False) # Alternate tmpdir/tmp2dir
1002
+
1003
+ command_args: List[str] = []
1004
+ command_args.append(f"-k{k}")
1005
+ command_args.append(f"-u{u}")
1006
+ command_args.append(f"-v{v}")
1007
+ command_args.append(f"-K{K}")
1008
+
1009
+ # Handle madmax's tmptoggle option ourselves when managing GUI plotting
1010
+ if G is True and t != t2 and index % 2:
1011
+ # Swap tmp and tmp2
1012
+ command_args.append(f"-t{t2}")
1013
+ command_args.append(f"-2{t}")
1014
+ else:
1015
+ command_args.append(f"-t{t}")
1016
+ command_args.append(f"-2{t2}")
1017
+
1018
+ return command_args
1019
+
1020
+ def _build_plotting_command_args(self, request: Any, ignoreCount: bool, index: int) -> List[str]:
1021
+ plotter: str = request.get("plotter", "chiapos")
1022
+ command_args: List[str] = ["chia", "plotters", plotter]
1023
+
1024
+ if plotter == "bladebit":
1025
+ # plotter command must be either
1026
+ # 'chia plotters bladebit ramplot' or 'chia plotters bladebit diskplot'
1027
+ plot_type = request["plot_type"]
1028
+ assert plot_type == "diskplot" or plot_type == "ramplot" or plot_type == "cudaplot"
1029
+ command_args.append(plot_type)
1030
+
1031
+ command_args.extend(self._common_plotting_command_args(request, ignoreCount))
1032
+
1033
+ if plotter == "chiapos":
1034
+ command_args.extend(self._chiapos_plotting_command_args(request, ignoreCount))
1035
+ elif plotter == "madmax":
1036
+ command_args.extend(self._madmax_plotting_command_args(request, ignoreCount, index))
1037
+ elif plotter == "bladebit":
1038
+ command_args.extend(self._bladebit_plotting_command_args(request, ignoreCount))
1039
+
1040
+ return command_args
1041
+
1042
+ def _is_serial_plotting_running(self, queue: str = "default") -> bool:
1043
+ response = False
1044
+ for item in self.plots_queue:
1045
+ if item["queue"] == queue and item["parallel"] is False and item["state"] is PlotState.RUNNING:
1046
+ response = True
1047
+ return response
1048
+
1049
+ def _get_plots_queue_item(self, id: str):
1050
+ config = next(item for item in self.plots_queue if item["id"] == id)
1051
+ return config
1052
+
1053
+ def _run_next_serial_plotting(self, loop: asyncio.AbstractEventLoop, queue: str = "default"):
1054
+ next_plot_id = None
1055
+
1056
+ if self._is_serial_plotting_running(queue) is True:
1057
+ return None
1058
+
1059
+ for item in self.plots_queue:
1060
+ if item["queue"] == queue and item["state"] is PlotState.SUBMITTED and item["parallel"] is False:
1061
+ next_plot_id = item["id"]
1062
+ break
1063
+
1064
+ if next_plot_id is not None:
1065
+ loop.create_task(self._start_plotting(next_plot_id, loop, queue))
1066
+
1067
+ def _post_process_plotting_job(self, job: Dict[str, Any]):
1068
+ id: str = job["id"]
1069
+ final_dir: str = job["final_dir"]
1070
+ exclude_final_dir: bool = job["exclude_final_dir"]
1071
+ log.info(f"Post-processing plotter job with ID {id}") # lgtm [py/clear-text-logging-sensitive-data]
1072
+ if not exclude_final_dir:
1073
+ try:
1074
+ add_plot_directory(self.root_path, final_dir)
1075
+ except ValueError as e:
1076
+ log.warning(f"_post_process_plotting_job: {e}")
1077
+
1078
+ async def _start_plotting(self, id: str, loop: asyncio.AbstractEventLoop, queue: str = "default"):
1079
+ current_process = None
1080
+ try:
1081
+ log.info(f"Starting plotting with ID {id}") # lgtm [py/clear-text-logging-sensitive-data]
1082
+ config = self._get_plots_queue_item(id)
1083
+
1084
+ if config is None:
1085
+ raise Exception(f"Plot queue config with ID {id} does not exist")
1086
+
1087
+ state = config["state"]
1088
+ if state is not PlotState.SUBMITTED:
1089
+ raise Exception(f"Plot with ID {id} has no state submitted")
1090
+
1091
+ assert id == config["id"]
1092
+ delay = config["delay"]
1093
+ await asyncio.sleep(delay)
1094
+
1095
+ if config["state"] is not PlotState.SUBMITTED:
1096
+ return None
1097
+
1098
+ service_name = config["service_name"]
1099
+ command_args = config["command_args"]
1100
+
1101
+ # Set the -D/--connect_to_daemon flag to signify that the child should connect
1102
+ # to the daemon to access the keychain
1103
+ command_args.append("-D")
1104
+
1105
+ self.log.debug(f"command_args before launch_plotter are {command_args}")
1106
+ self.log.debug(f"self.root_path before launch_plotter is {self.root_path}")
1107
+ process, pid_path = launch_plotter(self.root_path, service_name, command_args, id)
1108
+
1109
+ current_process = process
1110
+
1111
+ config["state"] = PlotState.RUNNING
1112
+ config["out_file"] = plotter_log_path(self.root_path, id).absolute()
1113
+ config["process"] = process
1114
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1115
+
1116
+ if service_name not in self.services:
1117
+ self.services[service_name] = []
1118
+
1119
+ self.services[service_name].append(process)
1120
+
1121
+ await self._track_plotting_progress(config, loop)
1122
+
1123
+ self.log.debug("finished tracking plotting progress. setting state to FINISHED")
1124
+
1125
+ config["state"] = PlotState.FINISHED
1126
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1127
+
1128
+ self._post_process_plotting_job(config)
1129
+
1130
+ except (subprocess.SubprocessError, OSError):
1131
+ log.exception(f"problem starting {service_name}") # lgtm [py/clear-text-logging-sensitive-data]
1132
+ error = Exception("Start plotting failed")
1133
+ config["state"] = PlotState.FINISHED
1134
+ config["error"] = error
1135
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1136
+ raise error
1137
+
1138
+ finally:
1139
+ if current_process is not None:
1140
+ self.services[service_name].remove(current_process)
1141
+ current_process.wait() # prevent zombies
1142
+ self._run_next_serial_plotting(loop, queue)
1143
+
1144
+ async def start_plotting(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1145
+ service_name = request["service"]
1146
+
1147
+ plotter = request.get("plotter", "chiapos")
1148
+ delay = int(request.get("delay", 0))
1149
+ parallel = request.get("parallel", False)
1150
+ size = request.get("k")
1151
+ temp_dir = request.get("t")
1152
+ final_dir = request.get("d")
1153
+ exclude_final_dir = request.get("x", False)
1154
+ count = int(request.get("n", 1))
1155
+ queue = request.get("queue", "default")
1156
+
1157
+ if ("p" in request) and ("c" in request):
1158
+ response = {
1159
+ "success": False,
1160
+ "service_name": service_name,
1161
+ "error": "Choose one of pool_contract_address and pool_public_key",
1162
+ }
1163
+ return response
1164
+
1165
+ ids: List[str] = []
1166
+ for k in range(count):
1167
+ id = str(uuid.uuid4())
1168
+ ids.append(id)
1169
+ config = {
1170
+ "id": id, # lgtm [py/clear-text-logging-sensitive-data]
1171
+ "size": size,
1172
+ "queue": queue,
1173
+ "plotter": plotter,
1174
+ "service_name": service_name,
1175
+ "command_args": self._build_plotting_command_args(request, True, k),
1176
+ "parallel": parallel,
1177
+ "delay": delay * k if parallel is True else delay,
1178
+ "state": PlotState.SUBMITTED,
1179
+ "deleted": False,
1180
+ "error": None,
1181
+ "log": None,
1182
+ "process": None,
1183
+ "temp_dir": temp_dir,
1184
+ "final_dir": final_dir,
1185
+ "exclude_final_dir": exclude_final_dir,
1186
+ }
1187
+
1188
+ self.plots_queue.append(config)
1189
+
1190
+ # notify GUI about new plot queue item
1191
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1192
+
1193
+ # only the first item can start when user selected serial plotting
1194
+ can_start_serial_plotting = k == 0 and self._is_serial_plotting_running(queue) is False
1195
+
1196
+ if parallel is True or can_start_serial_plotting:
1197
+ log.info(f"Plotting will start in {config['delay']} seconds")
1198
+ # TODO: loop gets passed down a lot, review for potential removal
1199
+ loop = asyncio.get_running_loop()
1200
+ loop.create_task(self._start_plotting(id, loop, queue))
1201
+ else:
1202
+ log.info("Plotting will start automatically when previous plotting finish")
1203
+
1204
+ response = {
1205
+ "success": True,
1206
+ "ids": ids,
1207
+ "service_name": service_name,
1208
+ }
1209
+
1210
+ return response
1211
+
1212
+ async def stop_plotting(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1213
+ id = request["id"]
1214
+ config = self._get_plots_queue_item(id)
1215
+ if config is None:
1216
+ return {"success": False}
1217
+
1218
+ id = config["id"]
1219
+ state = config["state"]
1220
+ process = config["process"]
1221
+ queue = config["queue"]
1222
+
1223
+ if config["state"] is PlotState.REMOVING:
1224
+ return {"success": False}
1225
+
1226
+ try:
1227
+ run_next = False
1228
+ if process is not None and state == PlotState.RUNNING:
1229
+ run_next = True
1230
+ config["state"] = PlotState.REMOVING
1231
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1232
+ await kill_processes([process], self.root_path, service_plotter, id)
1233
+
1234
+ config["state"] = PlotState.FINISHED
1235
+ config["deleted"] = True
1236
+
1237
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1238
+
1239
+ self.plots_queue.remove(config)
1240
+
1241
+ if run_next:
1242
+ # TODO: review to see if we can remove this
1243
+ loop = asyncio.get_running_loop()
1244
+ self._run_next_serial_plotting(loop, queue)
1245
+
1246
+ return {"success": True}
1247
+ except Exception as e:
1248
+ log.error(f"Error during killing the plot process: {e}")
1249
+ config["state"] = PlotState.FINISHED
1250
+ config["error"] = str(e)
1251
+ self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
1252
+ return {"success": False}
1253
+
1254
+ async def start_service(self, websocket: WebSocketResponse, request: Dict[str, Any]):
1255
+ service_command = request["service"]
1256
+
1257
+ error = None
1258
+ success = False
1259
+ testing = False
1260
+ already_running = False
1261
+ if "testing" in request:
1262
+ testing = request["testing"]
1263
+
1264
+ if not validate_service(service_command):
1265
+ error = "unknown service"
1266
+
1267
+ if service_command in self.services:
1268
+ processes = self.services[service_command]
1269
+ if all(process.poll() is not None for process in processes):
1270
+ self.services.pop(service_command)
1271
+ error = None
1272
+ else:
1273
+ self.log.info(f"Service {service_command} already running")
1274
+ already_running = True
1275
+ elif len(self.connections.get(service_command, [])) > 0:
1276
+ # If the service was started manually (not launched by the daemon), we should
1277
+ # have a connection to it.
1278
+ self.log.info(f"Service {service_command} already registered")
1279
+ already_running = True
1280
+
1281
+ if already_running:
1282
+ success = True
1283
+ elif error is None:
1284
+ try:
1285
+ exe_command = service_command
1286
+ if testing is True:
1287
+ exe_command = f"{service_command} --testing=true"
1288
+ process, pid_path = launch_service(self.root_path, exe_command)
1289
+ self.services[service_command] = [process]
1290
+ success = True
1291
+ except (subprocess.SubprocessError, OSError):
1292
+ log.exception(f"problem starting {service_command}")
1293
+ error = "start failed"
1294
+
1295
+ response = {"success": success, "service": service_command, "error": error}
1296
+ return response
1297
+
1298
+ async def stop_service(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1299
+ service_name = request["service"]
1300
+ result = await kill_service(self.root_path, self.services, service_name)
1301
+ response = {"success": result, "service_name": service_name}
1302
+ return response
1303
+
1304
+ def is_service_running(self, service_name: str) -> bool:
1305
+ processes: List[subprocess.Popen]
1306
+ if service_name == service_plotter:
1307
+ processes = self.services.get(service_name, [])
1308
+ is_running = len(processes) > 0
1309
+ else:
1310
+ processes = self.services.get(service_name, [])
1311
+ is_running = any(process.poll() is None for process in processes)
1312
+ if not is_running:
1313
+ # Check if we have a connection to the requested service. This might be the
1314
+ # case if the service was started manually (i.e. not started by the daemon).
1315
+ service_connections = self.connections.get(service_name)
1316
+ if service_connections is not None:
1317
+ is_running = len(service_connections) > 0
1318
+ return is_running
1319
+
1320
+ async def running_services_command(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1321
+ return await self.running_services()
1322
+
1323
+ async def running_services(self) -> Dict[str, Any]:
1324
+ services = list({*self.services.keys(), *self.connections.keys()})
1325
+ running_services = [service_name for service_name in services if self.is_service_running(service_name)]
1326
+
1327
+ return {"success": True, "running_services": running_services}
1328
+
1329
+ async def is_running_command(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1330
+ return await self.is_running(request=request)
1331
+
1332
+ async def is_running(self, request: Dict[str, Any]) -> Dict[str, Any]:
1333
+ service_name = request["service"]
1334
+ is_running = self.is_service_running(service_name)
1335
+ return {"success": True, "service_name": service_name, "is_running": is_running}
1336
+
1337
+ async def exit(self) -> None:
1338
+ if self.webserver is not None:
1339
+ self.webserver.close()
1340
+ await self.webserver.await_closed()
1341
+ log.info("chia daemon exiting")
1342
+
1343
+ async def register_service(self, websocket: WebSocketResponse, request: Dict[str, Any]) -> Dict[str, Any]:
1344
+ self.log.info(f"Register service {request}")
1345
+ service = request.get("service")
1346
+ if service is None:
1347
+ self.log.error("Service Name missing from request to 'register_service'")
1348
+ return {"success": False}
1349
+ if service not in self.connections:
1350
+ self.connections[service] = set()
1351
+ self.connections[service].add(websocket)
1352
+
1353
+ response: Dict[str, Any] = {"success": True}
1354
+ if service == service_plotter:
1355
+ response = {
1356
+ "success": True,
1357
+ "service": service,
1358
+ "queue": self.extract_plot_queue(),
1359
+ }
1360
+ else:
1361
+ if self.ping_job is None:
1362
+ self.ping_job = asyncio.create_task(self.ping_task())
1363
+ self.log.info(f"registered for service {service}")
1364
+ log.info(f"{response}")
1365
+ return response
1366
+
1367
+
1368
+ def daemon_launch_lock_path(root_path: Path) -> Path:
1369
+ """
1370
+ A path to a file that is lock when a daemon is launching but not yet started.
1371
+ This prevents multiple instances from launching.
1372
+ """
1373
+ return service_launch_lock_path(root_path, "daemon")
1374
+
1375
+
1376
+ def service_launch_lock_path(root_path: Path, service: str) -> Path:
1377
+ """
1378
+ A path that is locked when a service is running.
1379
+ """
1380
+ service_name = service.replace(" ", "-").replace("/", "-")
1381
+ return root_path / "run" / service_name
1382
+
1383
+
1384
+ def pid_path_for_service(root_path: Path, service: str, id: str = "") -> Path:
1385
+ """
1386
+ Generate a path for a PID file for the given service name.
1387
+ """
1388
+ pid_name = service.replace(" ", "-").replace("/", "-")
1389
+ return root_path / "run" / f"{pid_name}{id}.pid"
1390
+
1391
+
1392
+ def plotter_log_path(root_path: Path, id: str):
1393
+ return root_path / "plotter" / f"plotter_log_{id}.txt"
1394
+
1395
+
1396
+ def launch_plotter(
1397
+ root_path: Path, service_name: str, service_array: List[str], id: str
1398
+ ) -> Tuple[subprocess.Popen, Path]:
1399
+ # we need to pass on the possibly altered CHIA_ROOT
1400
+ os.environ["CHIA_ROOT"] = str(root_path)
1401
+ service_executable = executable_for_service(service_array[0])
1402
+
1403
+ # Swap service name with name of executable
1404
+ service_array[0] = service_executable
1405
+ startupinfo = None
1406
+ creationflags = 0
1407
+ if sys.platform == "win32" or sys.platform == "cygwin":
1408
+ startupinfo = subprocess.STARTUPINFO()
1409
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
1410
+ # If the current process group is used, CTRL_C_EVENT will kill the parent and everyone in the group!
1411
+ creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
1412
+
1413
+ plotter_path = plotter_log_path(root_path, id)
1414
+
1415
+ if plotter_path.parent.exists():
1416
+ if plotter_path.exists():
1417
+ plotter_path.unlink()
1418
+ else:
1419
+ plotter_path.parent.mkdir(parents=True, exist_ok=True)
1420
+ outfile = open(plotter_path.resolve(), "w")
1421
+ log.info(f"Service array: {service_array}") # lgtm [py/clear-text-logging-sensitive-data]
1422
+ process = subprocess.Popen(
1423
+ service_array,
1424
+ shell=False,
1425
+ stderr=outfile,
1426
+ stdout=outfile,
1427
+ startupinfo=startupinfo,
1428
+ creationflags=creationflags,
1429
+ )
1430
+
1431
+ pid_path = pid_path_for_service(root_path, service_name, id)
1432
+ try:
1433
+ pid_path.parent.mkdir(parents=True, exist_ok=True)
1434
+ with open(pid_path, "w") as f:
1435
+ f.write(f"{process.pid}\n")
1436
+ except Exception:
1437
+ pass
1438
+ return process, pid_path
1439
+
1440
+
1441
+ def launch_service(root_path: Path, service_command) -> Tuple[subprocess.Popen, Path]:
1442
+ """
1443
+ Launch a child process.
1444
+ """
1445
+ # set up CHIA_ROOT
1446
+ # invoke correct script
1447
+ # save away PID
1448
+
1449
+ # we need to pass on the possibly altered CHIA_ROOT
1450
+ os.environ["CHIA_ROOT"] = str(root_path)
1451
+
1452
+ # Insert proper e
1453
+ service_array = service_command.split()
1454
+ service_executable = executable_for_service(service_array[0])
1455
+ service_array[0] = service_executable
1456
+
1457
+ startupinfo = None
1458
+ if sys.platform == "win32" or sys.platform == "cygwin":
1459
+ startupinfo = subprocess.STARTUPINFO()
1460
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
1461
+
1462
+ log.debug(f"Launching service {service_array} with CHIA_ROOT: {os.environ['CHIA_ROOT']}")
1463
+
1464
+ # CREATE_NEW_PROCESS_GROUP allows graceful shutdown on windows, by CTRL_BREAK_EVENT signal
1465
+ if sys.platform == "win32" or sys.platform == "cygwin":
1466
+ creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
1467
+ else:
1468
+ creationflags = 0
1469
+ environ_copy = os.environ.copy()
1470
+ process = subprocess.Popen(
1471
+ service_array, shell=False, startupinfo=startupinfo, creationflags=creationflags, env=environ_copy
1472
+ )
1473
+
1474
+ pid_path = pid_path_for_service(root_path, service_command)
1475
+ try:
1476
+ pid_path.parent.mkdir(parents=True, exist_ok=True)
1477
+ with open(pid_path, "w") as f:
1478
+ f.write(f"{process.pid}\n")
1479
+ except Exception:
1480
+ pass
1481
+ return process, pid_path
1482
+
1483
+
1484
+ async def kill_processes(
1485
+ processes: List[subprocess.Popen],
1486
+ root_path: Path,
1487
+ service_name: str,
1488
+ id: str,
1489
+ delay_before_kill: int = 15,
1490
+ ) -> bool:
1491
+ pid_path = pid_path_for_service(root_path, service_name, id)
1492
+
1493
+ if sys.platform == "win32" or sys.platform == "cygwin":
1494
+ log.info("sending CTRL_BREAK_EVENT signal to %s", service_name)
1495
+
1496
+ for process in processes:
1497
+ kill(process.pid, signal.SIGBREAK)
1498
+ else:
1499
+ log.info("sending term signal to %s", service_name)
1500
+ for process in processes:
1501
+ process.terminate()
1502
+
1503
+ count: float = 0
1504
+ while count < delay_before_kill:
1505
+ if all(process.poll() is not None for process in processes):
1506
+ break
1507
+ await asyncio.sleep(0.5)
1508
+ count += 0.5
1509
+ else:
1510
+ for process in processes:
1511
+ process.kill()
1512
+ log.info("sending kill signal to %s", service_name)
1513
+ for process in processes:
1514
+ r = process.wait()
1515
+ log.info("process %s returned %d", service_name, r)
1516
+
1517
+ try:
1518
+ pid_path_killed = pid_path.with_suffix(".pid-killed")
1519
+ if pid_path_killed.exists():
1520
+ pid_path_killed.unlink()
1521
+ os.rename(pid_path, pid_path_killed)
1522
+ except Exception:
1523
+ pass
1524
+
1525
+ return True
1526
+
1527
+
1528
+ async def kill_service(
1529
+ root_path: Path, services: Dict[str, List[subprocess.Popen]], service_name: str, delay_before_kill: int = 15
1530
+ ) -> bool:
1531
+ processes = services.get(service_name)
1532
+ if processes is None:
1533
+ return False
1534
+ del services[service_name]
1535
+ result = await kill_processes(processes, root_path, service_name, "", delay_before_kill)
1536
+ return result
1537
+
1538
+
1539
+ def is_running(services: Dict[str, subprocess.Popen], service_name: str) -> bool:
1540
+ process = services.get(service_name)
1541
+ return process is not None and process.poll() is None
1542
+
1543
+
1544
+ async def async_run_daemon(root_path: Path, wait_for_unlock: bool = False) -> int:
1545
+ # When wait_for_unlock is true, we want to skip the check_keys() call in chia_init
1546
+ # since it might be necessary to wait for the GUI to unlock the keyring first.
1547
+ chia_init(root_path, should_check_keys=(not wait_for_unlock))
1548
+ config = load_config(root_path, "config.yaml")
1549
+ setproctitle("chia_daemon")
1550
+ initialize_service_logging("daemon", config)
1551
+ crt_path = root_path / config["daemon_ssl"]["private_crt"]
1552
+ key_path = root_path / config["daemon_ssl"]["private_key"]
1553
+ ca_crt_path = root_path / config["private_ssl_ca"]["crt"]
1554
+ ca_key_path = root_path / config["private_ssl_ca"]["key"]
1555
+ sys.stdout.flush()
1556
+ json_msg = dict_to_json_str(
1557
+ {
1558
+ "message": "cert_path",
1559
+ "success": True,
1560
+ "cert": f"{crt_path}",
1561
+ "key": f"{key_path}",
1562
+ "ca_crt": f"{ca_crt_path}",
1563
+ }
1564
+ )
1565
+ sys.stdout.write("\n" + json_msg + "\n")
1566
+ sys.stdout.flush()
1567
+ try:
1568
+ with Lockfile.create(daemon_launch_lock_path(root_path), timeout=1):
1569
+ log.info(f"chia-blockchain version: {chia_short_version()}")
1570
+
1571
+ beta_metrics = None
1572
+ if config.get("beta", {}).get("enabled", False):
1573
+ from chia.util.beta_metrics import BetaMetricsLogger
1574
+
1575
+ beta_metrics = BetaMetricsLogger(root_path)
1576
+ beta_metrics.start_logging()
1577
+
1578
+ ws_server = WebSocketServer(
1579
+ root_path,
1580
+ ca_crt_path,
1581
+ ca_key_path,
1582
+ crt_path,
1583
+ key_path,
1584
+ run_check_keys_on_unlock=wait_for_unlock,
1585
+ )
1586
+ async with SignalHandlers.manage() as signal_handlers:
1587
+ await ws_server.setup_process_global_state(signal_handlers=signal_handlers)
1588
+ async with ws_server.run():
1589
+ await ws_server.shutdown_event.wait()
1590
+
1591
+ if beta_metrics is not None:
1592
+ await beta_metrics.stop_logging()
1593
+
1594
+ log.info("Daemon WebSocketServer closed")
1595
+ sys.stdout.close()
1596
+ return 0
1597
+ except LockfileError:
1598
+ print("daemon: already launching")
1599
+ return 2
1600
+
1601
+
1602
+ def run_daemon(root_path: Path, wait_for_unlock: bool = False) -> int:
1603
+ result = asyncio.run(async_run_daemon(root_path, wait_for_unlock))
1604
+ return result
1605
+
1606
+
1607
+ def main() -> int:
1608
+ from chia.util.default_root import DEFAULT_ROOT_PATH
1609
+ from chia.util.keychain import Keychain
1610
+
1611
+ wait_for_unlock = "--wait-for-unlock" in sys.argv[1:] and Keychain.is_keyring_locked()
1612
+ return run_daemon(DEFAULT_ROOT_PATH, wait_for_unlock)
1613
+
1614
+
1615
+ if __name__ == "__main__":
1616
+ main()