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
@@ -0,0 +1,1974 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import functools
5
+ import logging
6
+ import time
7
+ import traceback
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from datetime import datetime, timezone
10
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
11
+
12
+ import anyio
13
+ from chia_rs import AugSchemeMPL, G1Element, G2Element, MerkleSet
14
+ from chiabip158 import PyBIP158
15
+
16
+ from chia.consensus.block_creation import create_unfinished_block
17
+ from chia.consensus.block_record import BlockRecord
18
+ from chia.consensus.blockchain import BlockchainMutexPriority
19
+ from chia.consensus.get_block_generator import get_block_generator
20
+ from chia.consensus.pot_iterations import calculate_ip_iters, calculate_iterations_quality, calculate_sp_iters
21
+ from chia.full_node.bundle_tools import simple_solution_generator, simple_solution_generator_backrefs
22
+ from chia.full_node.coin_store import CoinStore
23
+ from chia.full_node.fee_estimate import FeeEstimate, FeeEstimateGroup, fee_rate_v2_to_v1
24
+ from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
25
+ from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions, get_puzzle_and_solution_for_coin
26
+ from chia.full_node.signage_point import SignagePoint
27
+ from chia.full_node.tx_processing_queue import TransactionQueueFull
28
+ from chia.protocols import farmer_protocol, full_node_protocol, introducer_protocol, timelord_protocol, wallet_protocol
29
+ from chia.protocols.full_node_protocol import RejectBlock, RejectBlocks
30
+ from chia.protocols.protocol_message_types import ProtocolMessageTypes
31
+ from chia.protocols.shared_protocol import Capability
32
+ from chia.protocols.wallet_protocol import (
33
+ CoinState,
34
+ PuzzleSolutionResponse,
35
+ RejectBlockHeaders,
36
+ RejectHeaderBlocks,
37
+ RejectHeaderRequest,
38
+ RespondFeeEstimates,
39
+ RespondSESInfo,
40
+ )
41
+ from chia.server.outbound_message import Message, make_msg
42
+ from chia.server.server import ChiaServer
43
+ from chia.server.ws_connection import WSChiaConnection
44
+ from chia.types.block_protocol import BlockInfo
45
+ from chia.types.blockchain_format.coin import Coin, hash_coin_ids
46
+ from chia.types.blockchain_format.foliage import FoliageBlockData, FoliageTransactionBlock
47
+ from chia.types.blockchain_format.pool_target import PoolTarget
48
+ from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
49
+ from chia.types.blockchain_format.reward_chain_block import RewardChainBlockUnfinished
50
+ from chia.types.blockchain_format.sized_bytes import bytes32
51
+ from chia.types.blockchain_format.sub_epoch_summary import SubEpochSummary
52
+ from chia.types.coin_record import CoinRecord
53
+ from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
54
+ from chia.types.full_block import FullBlock
55
+ from chia.types.generator_types import BlockGenerator
56
+ from chia.types.mempool_inclusion_status import MempoolInclusionStatus
57
+ from chia.types.peer_info import PeerInfo
58
+ from chia.types.spend_bundle import SpendBundle
59
+ from chia.types.transaction_queue_entry import TransactionQueueEntry
60
+ from chia.types.unfinished_block import UnfinishedBlock
61
+ from chia.util.api_decorators import api_request
62
+ from chia.util.batches import to_batches
63
+ from chia.util.db_wrapper import SQLITE_MAX_VARIABLE_NUMBER
64
+ from chia.util.full_block_utils import header_block_from_block
65
+ from chia.util.generator_tools import get_block_header, tx_removals_and_additions
66
+ from chia.util.hash import std_hash
67
+ from chia.util.ints import uint8, uint32, uint64, uint128
68
+ from chia.util.limited_semaphore import LimitedSemaphoreFullError
69
+
70
+ if TYPE_CHECKING:
71
+ from chia.full_node.full_node import FullNode
72
+ else:
73
+ FullNode = object
74
+
75
+
76
+ class FullNodeAPI:
77
+ log: logging.Logger
78
+ full_node: FullNode
79
+ executor: ThreadPoolExecutor
80
+
81
+ def __init__(self, full_node: FullNode) -> None:
82
+ self.log = logging.getLogger(__name__)
83
+ self.full_node = full_node
84
+ self.executor = ThreadPoolExecutor(max_workers=1)
85
+
86
+ @property
87
+ def server(self) -> ChiaServer:
88
+ assert self.full_node.server is not None
89
+ return self.full_node.server
90
+
91
+ def ready(self) -> bool:
92
+ return self.full_node.initialized
93
+
94
+ @api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_peers])
95
+ async def request_peers(
96
+ self, _request: full_node_protocol.RequestPeers, peer: WSChiaConnection
97
+ ) -> Optional[Message]:
98
+ if peer.peer_server_port is None:
99
+ return None
100
+ peer_info = PeerInfo(peer.peer_info.host, peer.peer_server_port)
101
+ if self.full_node.full_node_peers is not None:
102
+ msg = await self.full_node.full_node_peers.request_peers(peer_info)
103
+ return msg
104
+ return None
105
+
106
+ @api_request(peer_required=True)
107
+ async def respond_peers(
108
+ self, request: full_node_protocol.RespondPeers, peer: WSChiaConnection
109
+ ) -> Optional[Message]:
110
+ self.log.debug(f"Received {len(request.peer_list)} peers")
111
+ if self.full_node.full_node_peers is not None:
112
+ await self.full_node.full_node_peers.add_peers(request.peer_list, peer.get_peer_info(), True)
113
+ return None
114
+
115
+ @api_request(peer_required=True)
116
+ async def respond_peers_introducer(
117
+ self, request: introducer_protocol.RespondPeersIntroducer, peer: WSChiaConnection
118
+ ) -> Optional[Message]:
119
+ self.log.debug(f"Received {len(request.peer_list)} peers from introducer")
120
+ if self.full_node.full_node_peers is not None:
121
+ await self.full_node.full_node_peers.add_peers(request.peer_list, peer.get_peer_info(), False)
122
+
123
+ await peer.close()
124
+ return None
125
+
126
+ @api_request(peer_required=True, execute_task=True)
127
+ async def new_peak(self, request: full_node_protocol.NewPeak, peer: WSChiaConnection) -> None:
128
+ """
129
+ A peer notifies us that they have added a new peak to their blockchain. If we don't have it,
130
+ we can ask for it.
131
+ """
132
+ # this semaphore limits the number of tasks that can call new_peak() at
133
+ # the same time, since it can be expensive
134
+ try:
135
+ async with self.full_node.new_peak_sem.acquire():
136
+ await self.full_node.new_peak(request, peer)
137
+ except LimitedSemaphoreFullError:
138
+ self.log.debug("Ignoring NewPeak, limited semaphore full: %s %s", peer.get_peer_logging(), request)
139
+ return None
140
+
141
+ return None
142
+
143
+ @api_request(peer_required=True)
144
+ async def new_transaction(
145
+ self, transaction: full_node_protocol.NewTransaction, peer: WSChiaConnection
146
+ ) -> Optional[Message]:
147
+ """
148
+ A peer notifies us of a new transaction.
149
+ Requests a full transaction if we haven't seen it previously, and if the fees are enough.
150
+ """
151
+ # Ignore if syncing
152
+ if self.full_node.sync_store.get_sync_mode():
153
+ return None
154
+ if not (await self.full_node.synced()):
155
+ return None
156
+
157
+ # Ignore if already seen
158
+ if self.full_node.mempool_manager.seen(transaction.transaction_id):
159
+ return None
160
+
161
+ if self.full_node.mempool_manager.is_fee_enough(transaction.fees, transaction.cost):
162
+ # If there's current pending request just add this peer to the set of peers that have this tx
163
+ if transaction.transaction_id in self.full_node.full_node_store.pending_tx_request:
164
+ if transaction.transaction_id in self.full_node.full_node_store.peers_with_tx:
165
+ current_set = self.full_node.full_node_store.peers_with_tx[transaction.transaction_id]
166
+ if peer.peer_node_id in current_set:
167
+ return None
168
+ current_set.add(peer.peer_node_id)
169
+ return None
170
+ else:
171
+ new_set = set()
172
+ new_set.add(peer.peer_node_id)
173
+ self.full_node.full_node_store.peers_with_tx[transaction.transaction_id] = new_set
174
+ return None
175
+
176
+ self.full_node.full_node_store.pending_tx_request[transaction.transaction_id] = peer.peer_node_id
177
+ new_set = set()
178
+ new_set.add(peer.peer_node_id)
179
+ self.full_node.full_node_store.peers_with_tx[transaction.transaction_id] = new_set
180
+
181
+ async def tx_request_and_timeout(full_node: FullNode, transaction_id: bytes32, task_id: bytes32) -> None:
182
+ counter = 0
183
+ try:
184
+ while True:
185
+ # Limit to asking a few peers, it's possible that this tx got included on chain already
186
+ # Highly unlikely that the peers that advertised a tx don't respond to a request. Also, if we
187
+ # drop some transactions, we don't want to re-fetch too many times
188
+ if counter == 5:
189
+ break
190
+ if transaction_id not in full_node.full_node_store.peers_with_tx:
191
+ break
192
+ peers_with_tx: Set[bytes32] = full_node.full_node_store.peers_with_tx[transaction_id]
193
+ if len(peers_with_tx) == 0:
194
+ break
195
+ peer_id = peers_with_tx.pop()
196
+ assert full_node.server is not None
197
+ if peer_id not in full_node.server.all_connections:
198
+ continue
199
+ random_peer = full_node.server.all_connections[peer_id]
200
+ request_tx = full_node_protocol.RequestTransaction(transaction.transaction_id)
201
+ msg = make_msg(ProtocolMessageTypes.request_transaction, request_tx)
202
+ await random_peer.send_message(msg)
203
+ await asyncio.sleep(5)
204
+ counter += 1
205
+ if full_node.mempool_manager.seen(transaction_id):
206
+ break
207
+ except asyncio.CancelledError:
208
+ pass
209
+ finally:
210
+ # Always Cleanup
211
+ if transaction_id in full_node.full_node_store.peers_with_tx:
212
+ full_node.full_node_store.peers_with_tx.pop(transaction_id)
213
+ if transaction_id in full_node.full_node_store.pending_tx_request:
214
+ full_node.full_node_store.pending_tx_request.pop(transaction_id)
215
+ if task_id in full_node.full_node_store.tx_fetch_tasks:
216
+ full_node.full_node_store.tx_fetch_tasks.pop(task_id)
217
+
218
+ task_id: bytes32 = bytes32.secret()
219
+ fetch_task = asyncio.create_task(
220
+ tx_request_and_timeout(self.full_node, transaction.transaction_id, task_id)
221
+ )
222
+ self.full_node.full_node_store.tx_fetch_tasks[task_id] = fetch_task
223
+ return None
224
+ return None
225
+
226
+ @api_request(reply_types=[ProtocolMessageTypes.respond_transaction])
227
+ async def request_transaction(self, request: full_node_protocol.RequestTransaction) -> Optional[Message]:
228
+ """Peer has requested a full transaction from us."""
229
+ # Ignore if syncing
230
+ if self.full_node.sync_store.get_sync_mode():
231
+ return None
232
+ spend_bundle = self.full_node.mempool_manager.get_spendbundle(request.transaction_id)
233
+ if spend_bundle is None:
234
+ return None
235
+
236
+ transaction = full_node_protocol.RespondTransaction(spend_bundle)
237
+
238
+ msg = make_msg(ProtocolMessageTypes.respond_transaction, transaction)
239
+ return msg
240
+
241
+ @api_request(peer_required=True, bytes_required=True)
242
+ async def respond_transaction(
243
+ self,
244
+ tx: full_node_protocol.RespondTransaction,
245
+ peer: WSChiaConnection,
246
+ tx_bytes: bytes = b"",
247
+ test: bool = False,
248
+ ) -> Optional[Message]:
249
+ """
250
+ Receives a full transaction from peer.
251
+ If tx is added to mempool, send tx_id to others. (new_transaction)
252
+ """
253
+ assert tx_bytes != b""
254
+ spend_name = std_hash(tx_bytes)
255
+ if spend_name in self.full_node.full_node_store.pending_tx_request:
256
+ self.full_node.full_node_store.pending_tx_request.pop(spend_name)
257
+ if spend_name in self.full_node.full_node_store.peers_with_tx:
258
+ self.full_node.full_node_store.peers_with_tx.pop(spend_name)
259
+
260
+ # TODO: Use fee in priority calculation, to prioritize high fee TXs
261
+ try:
262
+ await self.full_node.transaction_queue.put(
263
+ TransactionQueueEntry(tx.transaction, tx_bytes, spend_name, peer, test), peer.peer_node_id
264
+ )
265
+ except TransactionQueueFull:
266
+ pass # we can't do anything here, the tx will be dropped. We might do something in the future.
267
+ return None
268
+
269
+ @api_request(reply_types=[ProtocolMessageTypes.respond_proof_of_weight])
270
+ async def request_proof_of_weight(self, request: full_node_protocol.RequestProofOfWeight) -> Optional[Message]:
271
+ if self.full_node.weight_proof_handler is None:
272
+ return None
273
+ if not self.full_node.blockchain.contains_block(request.tip):
274
+ self.log.error(f"got weight proof request for unknown peak {request.tip}")
275
+ return None
276
+ if request.tip in self.full_node.pow_creation:
277
+ event = self.full_node.pow_creation[request.tip]
278
+ await event.wait()
279
+ wp = await self.full_node.weight_proof_handler.get_proof_of_weight(request.tip)
280
+ else:
281
+ event = asyncio.Event()
282
+ self.full_node.pow_creation[request.tip] = event
283
+ wp = await self.full_node.weight_proof_handler.get_proof_of_weight(request.tip)
284
+ event.set()
285
+ tips = list(self.full_node.pow_creation.keys())
286
+
287
+ if len(tips) > 4:
288
+ # Remove old from cache
289
+ for i in range(0, 4):
290
+ self.full_node.pow_creation.pop(tips[i])
291
+
292
+ if wp is None:
293
+ self.log.error(f"failed creating weight proof for peak {request.tip}")
294
+ return None
295
+
296
+ # Serialization of wp is slow
297
+ if (
298
+ self.full_node.full_node_store.serialized_wp_message_tip is not None
299
+ and self.full_node.full_node_store.serialized_wp_message_tip == request.tip
300
+ ):
301
+ return self.full_node.full_node_store.serialized_wp_message
302
+ message = make_msg(
303
+ ProtocolMessageTypes.respond_proof_of_weight, full_node_protocol.RespondProofOfWeight(wp, request.tip)
304
+ )
305
+ self.full_node.full_node_store.serialized_wp_message_tip = request.tip
306
+ self.full_node.full_node_store.serialized_wp_message = message
307
+ return message
308
+
309
+ @api_request()
310
+ async def respond_proof_of_weight(self, request: full_node_protocol.RespondProofOfWeight) -> Optional[Message]:
311
+ self.log.warning("Received proof of weight too late.")
312
+ return None
313
+
314
+ @api_request(reply_types=[ProtocolMessageTypes.respond_block, ProtocolMessageTypes.reject_block])
315
+ async def request_block(self, request: full_node_protocol.RequestBlock) -> Optional[Message]:
316
+ if not self.full_node.blockchain.contains_height(request.height):
317
+ reject = RejectBlock(request.height)
318
+ msg = make_msg(ProtocolMessageTypes.reject_block, reject)
319
+ return msg
320
+ header_hash: Optional[bytes32] = self.full_node.blockchain.height_to_hash(request.height)
321
+ if header_hash is None:
322
+ return make_msg(ProtocolMessageTypes.reject_block, RejectBlock(request.height))
323
+
324
+ block: Optional[FullBlock] = await self.full_node.block_store.get_full_block(header_hash)
325
+ if block is not None:
326
+ if not request.include_transaction_block and block.transactions_generator is not None:
327
+ block = block.replace(transactions_generator=None)
328
+ return make_msg(ProtocolMessageTypes.respond_block, full_node_protocol.RespondBlock(block))
329
+ return make_msg(ProtocolMessageTypes.reject_block, RejectBlock(request.height))
330
+
331
+ @api_request(reply_types=[ProtocolMessageTypes.respond_blocks, ProtocolMessageTypes.reject_blocks])
332
+ async def request_blocks(self, request: full_node_protocol.RequestBlocks) -> Optional[Message]:
333
+ # note that we treat the request range as *inclusive*, but we check the
334
+ # size before we bump end_height. So MAX_BLOCK_COUNT_PER_REQUESTS is off
335
+ # by one
336
+ if (
337
+ request.end_height < request.start_height
338
+ or request.end_height - request.start_height > self.full_node.constants.MAX_BLOCK_COUNT_PER_REQUESTS
339
+ ):
340
+ reject = RejectBlocks(request.start_height, request.end_height)
341
+ msg: Message = make_msg(ProtocolMessageTypes.reject_blocks, reject)
342
+ return msg
343
+ for i in range(request.start_height, request.end_height + 1):
344
+ if not self.full_node.blockchain.contains_height(uint32(i)):
345
+ reject = RejectBlocks(request.start_height, request.end_height)
346
+ msg = make_msg(ProtocolMessageTypes.reject_blocks, reject)
347
+ return msg
348
+
349
+ if not request.include_transaction_block:
350
+ blocks: List[FullBlock] = []
351
+ for i in range(request.start_height, request.end_height + 1):
352
+ header_hash_i: Optional[bytes32] = self.full_node.blockchain.height_to_hash(uint32(i))
353
+ if header_hash_i is None:
354
+ reject = RejectBlocks(request.start_height, request.end_height)
355
+ return make_msg(ProtocolMessageTypes.reject_blocks, reject)
356
+
357
+ block: Optional[FullBlock] = await self.full_node.block_store.get_full_block(header_hash_i)
358
+ if block is None:
359
+ reject = RejectBlocks(request.start_height, request.end_height)
360
+ return make_msg(ProtocolMessageTypes.reject_blocks, reject)
361
+ block = block.replace(transactions_generator=None)
362
+ blocks.append(block)
363
+ msg = make_msg(
364
+ ProtocolMessageTypes.respond_blocks,
365
+ full_node_protocol.RespondBlocks(request.start_height, request.end_height, blocks),
366
+ )
367
+ else:
368
+ blocks_bytes: List[bytes] = []
369
+ for i in range(request.start_height, request.end_height + 1):
370
+ header_hash_i = self.full_node.blockchain.height_to_hash(uint32(i))
371
+ if header_hash_i is None:
372
+ reject = RejectBlocks(request.start_height, request.end_height)
373
+ return make_msg(ProtocolMessageTypes.reject_blocks, reject)
374
+ block_bytes: Optional[bytes] = await self.full_node.block_store.get_full_block_bytes(header_hash_i)
375
+ if block_bytes is None:
376
+ reject = RejectBlocks(request.start_height, request.end_height)
377
+ msg = make_msg(ProtocolMessageTypes.reject_blocks, reject)
378
+ return msg
379
+
380
+ blocks_bytes.append(block_bytes)
381
+
382
+ respond_blocks_manually_streamed: bytes = (
383
+ uint32(request.start_height).stream_to_bytes()
384
+ + uint32(request.end_height).stream_to_bytes()
385
+ + uint32(len(blocks_bytes)).stream_to_bytes()
386
+ )
387
+ for block_bytes in blocks_bytes:
388
+ respond_blocks_manually_streamed += block_bytes
389
+ msg = make_msg(ProtocolMessageTypes.respond_blocks, respond_blocks_manually_streamed)
390
+
391
+ return msg
392
+
393
+ @api_request()
394
+ async def reject_block(self, request: full_node_protocol.RejectBlock) -> None:
395
+ self.log.debug(f"reject_block {request.height}")
396
+
397
+ @api_request()
398
+ async def reject_blocks(self, request: full_node_protocol.RejectBlocks) -> None:
399
+ self.log.debug(f"reject_blocks {request.start_height} {request.end_height}")
400
+
401
+ @api_request()
402
+ async def respond_blocks(self, request: full_node_protocol.RespondBlocks) -> None:
403
+ self.log.warning("Received unsolicited/late blocks")
404
+ return None
405
+
406
+ @api_request(peer_required=True)
407
+ async def respond_block(
408
+ self,
409
+ respond_block: full_node_protocol.RespondBlock,
410
+ peer: WSChiaConnection,
411
+ ) -> Optional[Message]:
412
+ """
413
+ Receive a full block from a peer full node (or ourselves).
414
+ """
415
+
416
+ self.log.warning(f"Received unsolicited/late block from peer {peer.get_peer_logging()}")
417
+ return None
418
+
419
+ @api_request()
420
+ async def new_unfinished_block(
421
+ self, new_unfinished_block: full_node_protocol.NewUnfinishedBlock
422
+ ) -> Optional[Message]:
423
+ # Ignore if syncing
424
+ if self.full_node.sync_store.get_sync_mode():
425
+ return None
426
+ block_hash = new_unfinished_block.unfinished_reward_hash
427
+ if self.full_node.full_node_store.get_unfinished_block(block_hash) is not None:
428
+ return None
429
+
430
+ # This prevents us from downloading the same block from many peers
431
+ requesting, count = self.full_node.full_node_store.is_requesting_unfinished_block(block_hash, None)
432
+ if requesting:
433
+ self.log.debug(
434
+ f"Already have or requesting {count} Unfinished Blocks with partial "
435
+ f"hash {block_hash}. Ignoring this one"
436
+ )
437
+ return None
438
+
439
+ msg = make_msg(
440
+ ProtocolMessageTypes.request_unfinished_block,
441
+ full_node_protocol.RequestUnfinishedBlock(block_hash),
442
+ )
443
+ self.full_node.full_node_store.mark_requesting_unfinished_block(block_hash, None)
444
+
445
+ # However, we want to eventually download from other peers, if this peer does not respond
446
+ # Todo: keep track of who it was
447
+ async def eventually_clear() -> None:
448
+ await asyncio.sleep(5)
449
+ self.full_node.full_node_store.remove_requesting_unfinished_block(block_hash, None)
450
+
451
+ asyncio.create_task(eventually_clear())
452
+
453
+ return msg
454
+
455
+ @api_request(reply_types=[ProtocolMessageTypes.respond_unfinished_block])
456
+ async def request_unfinished_block(
457
+ self, request_unfinished_block: full_node_protocol.RequestUnfinishedBlock
458
+ ) -> Optional[Message]:
459
+ unfinished_block: Optional[UnfinishedBlock] = self.full_node.full_node_store.get_unfinished_block(
460
+ request_unfinished_block.unfinished_reward_hash
461
+ )
462
+ if unfinished_block is not None:
463
+ msg = make_msg(
464
+ ProtocolMessageTypes.respond_unfinished_block,
465
+ full_node_protocol.RespondUnfinishedBlock(unfinished_block),
466
+ )
467
+ return msg
468
+ return None
469
+
470
+ @api_request()
471
+ async def new_unfinished_block2(
472
+ self, new_unfinished_block: full_node_protocol.NewUnfinishedBlock2
473
+ ) -> Optional[Message]:
474
+ # Ignore if syncing
475
+ if self.full_node.sync_store.get_sync_mode():
476
+ return None
477
+ block_hash = new_unfinished_block.unfinished_reward_hash
478
+ foliage_hash = new_unfinished_block.foliage_hash
479
+ entry, count, have_better = self.full_node.full_node_store.get_unfinished_block2(block_hash, foliage_hash)
480
+
481
+ if entry is not None:
482
+ return None
483
+
484
+ if have_better:
485
+ self.log.info(
486
+ f"Already have a better Unfinished Block with partial hash {block_hash.hex()} ignoring this one"
487
+ )
488
+ return None
489
+
490
+ max_duplicate_unfinished_blocks = self.full_node.config.get("max_duplicate_unfinished_blocks", 3)
491
+ if count > max_duplicate_unfinished_blocks:
492
+ self.log.info(
493
+ f"Already have {count} Unfinished Blocks with partial hash {block_hash.hex()} ignoring another one"
494
+ )
495
+ return None
496
+
497
+ # This prevents us from downloading the same block from many peers
498
+ requesting, count = self.full_node.full_node_store.is_requesting_unfinished_block(block_hash, foliage_hash)
499
+ if requesting:
500
+ return None
501
+ if count >= max_duplicate_unfinished_blocks:
502
+ self.log.info(
503
+ f"Already requesting {count} Unfinished Blocks with partial hash {block_hash} ignoring another one"
504
+ )
505
+ return None
506
+
507
+ msg = make_msg(
508
+ ProtocolMessageTypes.request_unfinished_block2,
509
+ full_node_protocol.RequestUnfinishedBlock2(block_hash, foliage_hash),
510
+ )
511
+ self.full_node.full_node_store.mark_requesting_unfinished_block(block_hash, foliage_hash)
512
+
513
+ # However, we want to eventually download from other peers, if this peer does not respond
514
+ # Todo: keep track of who it was
515
+ async def eventually_clear() -> None:
516
+ await asyncio.sleep(5)
517
+ self.full_node.full_node_store.remove_requesting_unfinished_block(block_hash, foliage_hash)
518
+
519
+ asyncio.create_task(eventually_clear())
520
+
521
+ return msg
522
+
523
+ @api_request(reply_types=[ProtocolMessageTypes.respond_unfinished_block])
524
+ async def request_unfinished_block2(
525
+ self, request_unfinished_block: full_node_protocol.RequestUnfinishedBlock2
526
+ ) -> Optional[Message]:
527
+ unfinished_block: Optional[UnfinishedBlock]
528
+ unfinished_block, _, _ = self.full_node.full_node_store.get_unfinished_block2(
529
+ request_unfinished_block.unfinished_reward_hash,
530
+ request_unfinished_block.foliage_hash,
531
+ )
532
+ if unfinished_block is not None:
533
+ msg = make_msg(
534
+ ProtocolMessageTypes.respond_unfinished_block,
535
+ full_node_protocol.RespondUnfinishedBlock(unfinished_block),
536
+ )
537
+ return msg
538
+ return None
539
+
540
+ @api_request(peer_required=True, bytes_required=True)
541
+ async def respond_unfinished_block(
542
+ self,
543
+ respond_unfinished_block: full_node_protocol.RespondUnfinishedBlock,
544
+ peer: WSChiaConnection,
545
+ respond_unfinished_block_bytes: bytes = b"",
546
+ ) -> Optional[Message]:
547
+ if self.full_node.sync_store.get_sync_mode():
548
+ return None
549
+ await self.full_node.add_unfinished_block(
550
+ respond_unfinished_block.unfinished_block, peer, block_bytes=respond_unfinished_block_bytes
551
+ )
552
+ return None
553
+
554
+ @api_request(peer_required=True)
555
+ async def new_signage_point_or_end_of_sub_slot(
556
+ self, new_sp: full_node_protocol.NewSignagePointOrEndOfSubSlot, peer: WSChiaConnection
557
+ ) -> Optional[Message]:
558
+ # Ignore if syncing
559
+ if self.full_node.sync_store.get_sync_mode():
560
+ return None
561
+ if (
562
+ self.full_node.full_node_store.get_signage_point_by_index(
563
+ new_sp.challenge_hash,
564
+ new_sp.index_from_challenge,
565
+ new_sp.last_rc_infusion,
566
+ )
567
+ is not None
568
+ ):
569
+ return None
570
+ if self.full_node.full_node_store.have_newer_signage_point(
571
+ new_sp.challenge_hash, new_sp.index_from_challenge, new_sp.last_rc_infusion
572
+ ):
573
+ return None
574
+
575
+ if new_sp.index_from_challenge == 0 and new_sp.prev_challenge_hash is not None:
576
+ if self.full_node.full_node_store.get_sub_slot(new_sp.prev_challenge_hash) is None:
577
+ collected_eos = []
578
+ challenge_hash_to_request = new_sp.challenge_hash
579
+ last_rc = new_sp.last_rc_infusion
580
+ num_non_empty_sub_slots_seen = 0
581
+ for _ in range(30):
582
+ if num_non_empty_sub_slots_seen >= 3:
583
+ self.log.debug("Diverged from peer. Don't have the same blocks")
584
+ return None
585
+ # If this is an end of sub slot, and we don't have the prev, request the prev instead
586
+ # We want to catch up to the latest slot so we can receive signage points
587
+ full_node_request = full_node_protocol.RequestSignagePointOrEndOfSubSlot(
588
+ challenge_hash_to_request, uint8(0), last_rc
589
+ )
590
+ response = await peer.call_api(
591
+ FullNodeAPI.request_signage_point_or_end_of_sub_slot, full_node_request, timeout=10
592
+ )
593
+ if not isinstance(response, full_node_protocol.RespondEndOfSubSlot):
594
+ self.full_node.log.debug(f"Invalid response for slot {response}")
595
+ return None
596
+ collected_eos.append(response)
597
+ if (
598
+ self.full_node.full_node_store.get_sub_slot(
599
+ response.end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge
600
+ )
601
+ is not None
602
+ or response.end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge
603
+ == self.full_node.constants.GENESIS_CHALLENGE
604
+ ):
605
+ for eos in reversed(collected_eos):
606
+ await self.respond_end_of_sub_slot(eos, peer)
607
+ return None
608
+ if (
609
+ response.end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.number_of_iterations
610
+ != response.end_of_slot_bundle.reward_chain.end_of_slot_vdf.number_of_iterations
611
+ ):
612
+ num_non_empty_sub_slots_seen += 1
613
+ challenge_hash_to_request = (
614
+ response.end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge
615
+ )
616
+ last_rc = response.end_of_slot_bundle.reward_chain.end_of_slot_vdf.challenge
617
+ self.full_node.log.warning("Failed to catch up in sub-slots")
618
+ return None
619
+
620
+ if new_sp.index_from_challenge > 0:
621
+ if (
622
+ new_sp.challenge_hash != self.full_node.constants.GENESIS_CHALLENGE
623
+ and self.full_node.full_node_store.get_sub_slot(new_sp.challenge_hash) is None
624
+ ):
625
+ # If this is a normal signage point,, and we don't have the end of sub slot, request the end of sub slot
626
+ full_node_request = full_node_protocol.RequestSignagePointOrEndOfSubSlot(
627
+ new_sp.challenge_hash, uint8(0), new_sp.last_rc_infusion
628
+ )
629
+ return make_msg(ProtocolMessageTypes.request_signage_point_or_end_of_sub_slot, full_node_request)
630
+
631
+ # Otherwise (we have the prev or the end of sub slot), request it normally
632
+ full_node_request = full_node_protocol.RequestSignagePointOrEndOfSubSlot(
633
+ new_sp.challenge_hash, new_sp.index_from_challenge, new_sp.last_rc_infusion
634
+ )
635
+
636
+ return make_msg(ProtocolMessageTypes.request_signage_point_or_end_of_sub_slot, full_node_request)
637
+
638
+ @api_request(reply_types=[ProtocolMessageTypes.respond_signage_point, ProtocolMessageTypes.respond_end_of_sub_slot])
639
+ async def request_signage_point_or_end_of_sub_slot(
640
+ self, request: full_node_protocol.RequestSignagePointOrEndOfSubSlot
641
+ ) -> Optional[Message]:
642
+ if request.index_from_challenge == 0:
643
+ sub_slot: Optional[Tuple[EndOfSubSlotBundle, int, uint128]] = self.full_node.full_node_store.get_sub_slot(
644
+ request.challenge_hash
645
+ )
646
+ if sub_slot is not None:
647
+ return make_msg(
648
+ ProtocolMessageTypes.respond_end_of_sub_slot,
649
+ full_node_protocol.RespondEndOfSubSlot(sub_slot[0]),
650
+ )
651
+ else:
652
+ if self.full_node.full_node_store.get_sub_slot(request.challenge_hash) is None:
653
+ if request.challenge_hash != self.full_node.constants.GENESIS_CHALLENGE:
654
+ self.log.info(f"Don't have challenge hash {request.challenge_hash.hex()}")
655
+
656
+ sp: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point_by_index(
657
+ request.challenge_hash,
658
+ request.index_from_challenge,
659
+ request.last_rc_infusion,
660
+ )
661
+ if sp is not None:
662
+ assert (
663
+ sp.cc_vdf is not None
664
+ and sp.cc_proof is not None
665
+ and sp.rc_vdf is not None
666
+ and sp.rc_proof is not None
667
+ )
668
+ full_node_response = full_node_protocol.RespondSignagePoint(
669
+ request.index_from_challenge,
670
+ sp.cc_vdf,
671
+ sp.cc_proof,
672
+ sp.rc_vdf,
673
+ sp.rc_proof,
674
+ )
675
+ return make_msg(ProtocolMessageTypes.respond_signage_point, full_node_response)
676
+ else:
677
+ self.log.info(f"Don't have signage point {request}")
678
+ return None
679
+
680
+ @api_request(peer_required=True)
681
+ async def respond_signage_point(
682
+ self, request: full_node_protocol.RespondSignagePoint, peer: WSChiaConnection
683
+ ) -> Optional[Message]:
684
+ if self.full_node.sync_store.get_sync_mode():
685
+ return None
686
+ async with self.full_node.timelord_lock:
687
+ # Already have signage point
688
+
689
+ if self.full_node.full_node_store.have_newer_signage_point(
690
+ request.challenge_chain_vdf.challenge,
691
+ request.index_from_challenge,
692
+ request.reward_chain_vdf.challenge,
693
+ ):
694
+ return None
695
+ existing_sp = self.full_node.full_node_store.get_signage_point(
696
+ request.challenge_chain_vdf.output.get_hash()
697
+ )
698
+ if existing_sp is not None and existing_sp.rc_vdf == request.reward_chain_vdf:
699
+ return None
700
+ peak = self.full_node.blockchain.get_peak()
701
+ if peak is not None and peak.height > self.full_node.constants.MAX_SUB_SLOT_BLOCKS:
702
+ next_sub_slot_iters = self.full_node.blockchain.get_next_slot_iters(peak.header_hash, True)
703
+ sub_slots_for_peak = await self.full_node.blockchain.get_sp_and_ip_sub_slots(peak.header_hash)
704
+ assert sub_slots_for_peak is not None
705
+ ip_sub_slot: Optional[EndOfSubSlotBundle] = sub_slots_for_peak[1]
706
+ else:
707
+ sub_slot_iters = self.full_node.constants.SUB_SLOT_ITERS_STARTING
708
+ next_sub_slot_iters = sub_slot_iters
709
+ ip_sub_slot = None
710
+
711
+ added = self.full_node.full_node_store.new_signage_point(
712
+ request.index_from_challenge,
713
+ self.full_node.blockchain,
714
+ self.full_node.blockchain.get_peak(),
715
+ next_sub_slot_iters,
716
+ SignagePoint(
717
+ request.challenge_chain_vdf,
718
+ request.challenge_chain_proof,
719
+ request.reward_chain_vdf,
720
+ request.reward_chain_proof,
721
+ ),
722
+ )
723
+
724
+ if added:
725
+ await self.full_node.signage_point_post_processing(request, peer, ip_sub_slot)
726
+ else:
727
+ self.log.debug(
728
+ f"Signage point {request.index_from_challenge} not added, CC challenge: "
729
+ f"{request.challenge_chain_vdf.challenge.hex()}, "
730
+ f"RC challenge: {request.reward_chain_vdf.challenge.hex()}"
731
+ )
732
+
733
+ return None
734
+
735
+ @api_request(peer_required=True)
736
+ async def respond_end_of_sub_slot(
737
+ self, request: full_node_protocol.RespondEndOfSubSlot, peer: WSChiaConnection
738
+ ) -> Optional[Message]:
739
+ if self.full_node.sync_store.get_sync_mode():
740
+ return None
741
+ msg, _ = await self.full_node.add_end_of_sub_slot(request.end_of_slot_bundle, peer)
742
+ return msg
743
+
744
+ @api_request(peer_required=True)
745
+ async def request_mempool_transactions(
746
+ self,
747
+ request: full_node_protocol.RequestMempoolTransactions,
748
+ peer: WSChiaConnection,
749
+ ) -> Optional[Message]:
750
+ received_filter = PyBIP158(bytearray(request.filter))
751
+
752
+ items: List[SpendBundle] = self.full_node.mempool_manager.get_items_not_in_filter(received_filter)
753
+
754
+ for item in items:
755
+ transaction = full_node_protocol.RespondTransaction(item)
756
+ msg = make_msg(ProtocolMessageTypes.respond_transaction, transaction)
757
+ await peer.send_message(msg)
758
+ return None
759
+
760
+ # FARMER PROTOCOL
761
+ @api_request(peer_required=True)
762
+ async def declare_proof_of_space(
763
+ self, request: farmer_protocol.DeclareProofOfSpace, peer: WSChiaConnection
764
+ ) -> Optional[Message]:
765
+ """
766
+ Creates a block body and header, with the proof of space, coinbase, and fee targets provided
767
+ by the farmer, and sends the hash of the header data back to the farmer.
768
+ """
769
+ if self.full_node.sync_store.get_sync_mode():
770
+ return None
771
+
772
+ async with self.full_node.timelord_lock:
773
+ sp_vdfs: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point(
774
+ request.challenge_chain_sp
775
+ )
776
+
777
+ if sp_vdfs is None:
778
+ self.log.warning(f"Received proof of space for an unknown signage point {request.challenge_chain_sp}")
779
+ return None
780
+ if request.signage_point_index > 0:
781
+ assert sp_vdfs.rc_vdf is not None
782
+ if sp_vdfs.rc_vdf.output.get_hash() != request.reward_chain_sp:
783
+ self.log.debug(
784
+ f"Received proof of space for a potentially old signage point {request.challenge_chain_sp}. "
785
+ f"Current sp: {sp_vdfs.rc_vdf.output.get_hash().hex()}"
786
+ )
787
+ return None
788
+
789
+ if request.signage_point_index == 0:
790
+ cc_challenge_hash: bytes32 = request.challenge_chain_sp
791
+ else:
792
+ assert sp_vdfs.cc_vdf is not None
793
+ cc_challenge_hash = sp_vdfs.cc_vdf.challenge
794
+
795
+ pos_sub_slot: Optional[Tuple[EndOfSubSlotBundle, int, uint128]] = None
796
+ if request.challenge_hash != self.full_node.constants.GENESIS_CHALLENGE:
797
+ # Checks that the proof of space is a response to a recent challenge and valid SP
798
+ pos_sub_slot = self.full_node.full_node_store.get_sub_slot(cc_challenge_hash)
799
+ if pos_sub_slot is None:
800
+ self.log.warning(f"Received proof of space for an unknown sub slot: {request}")
801
+ return None
802
+ total_iters_pos_slot: uint128 = pos_sub_slot[2]
803
+ else:
804
+ total_iters_pos_slot = uint128(0)
805
+ assert cc_challenge_hash == request.challenge_hash
806
+
807
+ # Now we know that the proof of space has a signage point either:
808
+ # 1. In the previous sub-slot of the peak (overflow)
809
+ # 2. In the same sub-slot as the peak
810
+ # 3. In a future sub-slot that we already know of
811
+
812
+ # Grab best transactions from Mempool for given tip target
813
+ aggregate_signature: G2Element = G2Element()
814
+ block_generator: Optional[BlockGenerator] = None
815
+ additions: Optional[List[Coin]] = []
816
+ removals: Optional[List[Coin]] = []
817
+ async with self.full_node.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high):
818
+ peak: Optional[BlockRecord] = self.full_node.blockchain.get_peak()
819
+
820
+ # Checks that the proof of space is valid
821
+ height: uint32
822
+ if peak is None:
823
+ height = uint32(0)
824
+ else:
825
+ height = peak.height
826
+ quality_string: Optional[bytes32] = verify_and_get_quality_string(
827
+ request.proof_of_space,
828
+ self.full_node.constants,
829
+ cc_challenge_hash,
830
+ request.challenge_chain_sp,
831
+ height=height,
832
+ )
833
+ assert quality_string is not None and len(quality_string) == 32
834
+
835
+ if peak is not None:
836
+ # Finds the last transaction block before this one
837
+ curr_l_tb: BlockRecord = peak
838
+ while not curr_l_tb.is_transaction_block:
839
+ curr_l_tb = self.full_node.blockchain.block_record(curr_l_tb.prev_hash)
840
+ try:
841
+ mempool_bundle = await self.full_node.mempool_manager.create_bundle_from_mempool(
842
+ curr_l_tb.header_hash, self.full_node.coin_store.get_unspent_lineage_info_for_puzzle_hash
843
+ )
844
+ except Exception as e:
845
+ self.log.error(f"Traceback: {traceback.format_exc()}")
846
+ self.full_node.log.error(f"Error making spend bundle {e} peak: {peak}")
847
+ mempool_bundle = None
848
+ if mempool_bundle is not None:
849
+ spend_bundle, additions = mempool_bundle
850
+ removals = spend_bundle.removals()
851
+ self.full_node.log.info(f"Add rem: {len(additions)} {len(removals)}")
852
+ aggregate_signature = spend_bundle.aggregated_signature
853
+ # when the hard fork activates, block generators are
854
+ # allowed to be serialized with the improved CLVM
855
+ # serialization format, supporting back-references
856
+ if peak.height >= self.full_node.constants.HARD_FORK_HEIGHT:
857
+ block_generator = simple_solution_generator_backrefs(spend_bundle)
858
+ else:
859
+ block_generator = simple_solution_generator(spend_bundle)
860
+
861
+ def get_plot_sig(to_sign: bytes32, _extra: G1Element) -> G2Element:
862
+ if to_sign == request.challenge_chain_sp:
863
+ return request.challenge_chain_sp_signature
864
+ elif to_sign == request.reward_chain_sp:
865
+ return request.reward_chain_sp_signature
866
+ return G2Element()
867
+
868
+ def get_pool_sig(_1: PoolTarget, _2: Optional[G1Element]) -> Optional[G2Element]:
869
+ return request.pool_signature
870
+
871
+ prev_b: Optional[BlockRecord] = peak
872
+
873
+ # Finds the previous block from the signage point, ensuring that the reward chain VDF is correct
874
+ if prev_b is not None:
875
+ if request.signage_point_index == 0:
876
+ if pos_sub_slot is None:
877
+ self.log.warning("Pos sub slot is None")
878
+ return None
879
+ rc_challenge = pos_sub_slot[0].reward_chain.end_of_slot_vdf.challenge
880
+ else:
881
+ assert sp_vdfs.rc_vdf is not None
882
+ rc_challenge = sp_vdfs.rc_vdf.challenge
883
+
884
+ # Backtrack through empty sub-slots
885
+ for eos, _, _ in reversed(self.full_node.full_node_store.finished_sub_slots):
886
+ if eos is not None and eos.reward_chain.get_hash() == rc_challenge:
887
+ rc_challenge = eos.reward_chain.end_of_slot_vdf.challenge
888
+
889
+ found = False
890
+ attempts = 0
891
+ while prev_b is not None and attempts < 10:
892
+ if prev_b.reward_infusion_new_challenge == rc_challenge:
893
+ found = True
894
+ break
895
+ if prev_b.finished_reward_slot_hashes is not None and len(prev_b.finished_reward_slot_hashes) > 0:
896
+ if prev_b.finished_reward_slot_hashes[-1] == rc_challenge:
897
+ # This block includes a sub-slot which is where our SP vdf starts. Go back one more
898
+ # to find the prev block
899
+ prev_b = self.full_node.blockchain.try_block_record(prev_b.prev_hash)
900
+ found = True
901
+ break
902
+ prev_b = self.full_node.blockchain.try_block_record(prev_b.prev_hash)
903
+ attempts += 1
904
+ if not found:
905
+ self.log.warning("Did not find a previous block with the correct reward chain hash")
906
+ return None
907
+
908
+ try:
909
+ finished_sub_slots: Optional[List[EndOfSubSlotBundle]] = (
910
+ self.full_node.full_node_store.get_finished_sub_slots(
911
+ self.full_node.blockchain, prev_b, cc_challenge_hash
912
+ )
913
+ )
914
+ if finished_sub_slots is None:
915
+ return None
916
+
917
+ if (
918
+ len(finished_sub_slots) > 0
919
+ and pos_sub_slot is not None
920
+ and finished_sub_slots[-1] != pos_sub_slot[0]
921
+ ):
922
+ self.log.error("Have different sub-slots than is required to farm this block")
923
+ return None
924
+ except ValueError as e:
925
+ self.log.warning(f"Value Error: {e}")
926
+ return None
927
+ if prev_b is None:
928
+ pool_target = PoolTarget(
929
+ self.full_node.constants.GENESIS_PRE_FARM_POOL_PUZZLE_HASH,
930
+ uint32(0),
931
+ )
932
+ farmer_ph = self.full_node.constants.GENESIS_PRE_FARM_FARMER_PUZZLE_HASH
933
+ else:
934
+ farmer_ph = request.farmer_puzzle_hash
935
+ if request.proof_of_space.pool_contract_puzzle_hash is not None:
936
+ pool_target = PoolTarget(request.proof_of_space.pool_contract_puzzle_hash, uint32(0))
937
+ else:
938
+ assert request.pool_target is not None
939
+ pool_target = request.pool_target
940
+
941
+ if peak is None or peak.height <= self.full_node.constants.MAX_SUB_SLOT_BLOCKS:
942
+ difficulty = self.full_node.constants.DIFFICULTY_STARTING
943
+ sub_slot_iters = self.full_node.constants.SUB_SLOT_ITERS_STARTING
944
+ else:
945
+ difficulty = uint64(peak.weight - self.full_node.blockchain.block_record(peak.prev_hash).weight)
946
+ sub_slot_iters = peak.sub_slot_iters
947
+ for sub_slot in finished_sub_slots:
948
+ if sub_slot.challenge_chain.new_difficulty is not None:
949
+ difficulty = sub_slot.challenge_chain.new_difficulty
950
+ if sub_slot.challenge_chain.new_sub_slot_iters is not None:
951
+ sub_slot_iters = sub_slot.challenge_chain.new_sub_slot_iters
952
+
953
+ required_iters: uint64 = calculate_iterations_quality(
954
+ self.full_node.constants.DIFFICULTY_CONSTANT_FACTOR,
955
+ quality_string,
956
+ request.proof_of_space.size,
957
+ difficulty,
958
+ request.challenge_chain_sp,
959
+ )
960
+ sp_iters: uint64 = calculate_sp_iters(self.full_node.constants, sub_slot_iters, request.signage_point_index)
961
+ ip_iters: uint64 = calculate_ip_iters(
962
+ self.full_node.constants,
963
+ sub_slot_iters,
964
+ request.signage_point_index,
965
+ required_iters,
966
+ )
967
+
968
+ # The block's timestamp must be greater than the previous transaction block's timestamp
969
+ timestamp = uint64(int(time.time()))
970
+ curr: Optional[BlockRecord] = prev_b
971
+ while curr is not None and not curr.is_transaction_block and curr.height != 0:
972
+ curr = self.full_node.blockchain.try_block_record(curr.prev_hash)
973
+ if curr is not None:
974
+ assert curr.timestamp is not None
975
+ if timestamp <= curr.timestamp:
976
+ timestamp = uint64(int(curr.timestamp + 1))
977
+
978
+ self.log.info("Starting to make the unfinished block")
979
+ unfinished_block: UnfinishedBlock = create_unfinished_block(
980
+ self.full_node.constants,
981
+ total_iters_pos_slot,
982
+ sub_slot_iters,
983
+ request.signage_point_index,
984
+ sp_iters,
985
+ ip_iters,
986
+ request.proof_of_space,
987
+ cc_challenge_hash,
988
+ farmer_ph,
989
+ pool_target,
990
+ get_plot_sig,
991
+ get_pool_sig,
992
+ sp_vdfs,
993
+ timestamp,
994
+ self.full_node.blockchain,
995
+ b"",
996
+ block_generator,
997
+ aggregate_signature,
998
+ additions,
999
+ removals,
1000
+ prev_b,
1001
+ finished_sub_slots,
1002
+ )
1003
+ self.log.info("Made the unfinished block")
1004
+ if prev_b is not None:
1005
+ height = uint32(prev_b.height + 1)
1006
+ else:
1007
+ height = uint32(0)
1008
+ self.full_node.full_node_store.add_candidate_block(quality_string, height, unfinished_block)
1009
+
1010
+ foliage_sb_data_hash = unfinished_block.foliage.foliage_block_data.get_hash()
1011
+ if unfinished_block.is_transaction_block():
1012
+ foliage_transaction_block_hash = unfinished_block.foliage.foliage_transaction_block_hash
1013
+ else:
1014
+ foliage_transaction_block_hash = bytes32([0] * 32)
1015
+ assert foliage_transaction_block_hash is not None
1016
+
1017
+ foliage_block_data: Optional[FoliageBlockData] = None
1018
+ foliage_transaction_block_data: Optional[FoliageTransactionBlock] = None
1019
+ rc_block_unfinished: Optional[RewardChainBlockUnfinished] = None
1020
+ if request.include_signature_source_data:
1021
+ foliage_block_data = unfinished_block.foliage.foliage_block_data
1022
+ rc_block_unfinished = unfinished_block.reward_chain_block
1023
+ if unfinished_block.is_transaction_block():
1024
+ foliage_transaction_block_data = unfinished_block.foliage_transaction_block
1025
+
1026
+ message = farmer_protocol.RequestSignedValues(
1027
+ quality_string,
1028
+ foliage_sb_data_hash,
1029
+ foliage_transaction_block_hash,
1030
+ foliage_block_data=foliage_block_data,
1031
+ foliage_transaction_block_data=foliage_transaction_block_data,
1032
+ rc_block_unfinished=rc_block_unfinished,
1033
+ )
1034
+ await peer.send_message(make_msg(ProtocolMessageTypes.request_signed_values, message))
1035
+
1036
+ # Adds backup in case the first one fails
1037
+ if unfinished_block.is_transaction_block() and unfinished_block.transactions_generator is not None:
1038
+ unfinished_block_backup = create_unfinished_block(
1039
+ self.full_node.constants,
1040
+ total_iters_pos_slot,
1041
+ sub_slot_iters,
1042
+ request.signage_point_index,
1043
+ sp_iters,
1044
+ ip_iters,
1045
+ request.proof_of_space,
1046
+ cc_challenge_hash,
1047
+ farmer_ph,
1048
+ pool_target,
1049
+ get_plot_sig,
1050
+ get_pool_sig,
1051
+ sp_vdfs,
1052
+ timestamp,
1053
+ self.full_node.blockchain,
1054
+ b"",
1055
+ None,
1056
+ G2Element(),
1057
+ None,
1058
+ None,
1059
+ prev_b,
1060
+ finished_sub_slots,
1061
+ )
1062
+
1063
+ self.full_node.full_node_store.add_candidate_block(
1064
+ quality_string, height, unfinished_block_backup, backup=True
1065
+ )
1066
+ return None
1067
+
1068
+ @api_request(peer_required=True)
1069
+ async def signed_values(
1070
+ self, farmer_request: farmer_protocol.SignedValues, peer: WSChiaConnection
1071
+ ) -> Optional[Message]:
1072
+ """
1073
+ Signature of header hash, by the harvester. This is enough to create an unfinished
1074
+ block, which only needs a Proof of Time to be finished. If the signature is valid,
1075
+ we call the unfinished_block routine.
1076
+ """
1077
+ candidate_tuple: Optional[Tuple[uint32, UnfinishedBlock]] = self.full_node.full_node_store.get_candidate_block(
1078
+ farmer_request.quality_string
1079
+ )
1080
+
1081
+ if candidate_tuple is None:
1082
+ self.log.warning(f"Quality string {farmer_request.quality_string} not found in database")
1083
+ return None
1084
+ height, candidate = candidate_tuple
1085
+
1086
+ if not AugSchemeMPL.verify(
1087
+ candidate.reward_chain_block.proof_of_space.plot_public_key,
1088
+ candidate.foliage.foliage_block_data.get_hash(),
1089
+ farmer_request.foliage_block_data_signature,
1090
+ ):
1091
+ self.log.warning("Signature not valid. There might be a collision in plots. Ignore this during tests.")
1092
+ return None
1093
+
1094
+ fsb2 = candidate.foliage.replace(foliage_block_data_signature=farmer_request.foliage_block_data_signature)
1095
+ if candidate.is_transaction_block():
1096
+ fsb2 = fsb2.replace(foliage_transaction_block_signature=farmer_request.foliage_transaction_block_signature)
1097
+
1098
+ new_candidate = candidate.replace(foliage=fsb2)
1099
+ if not self.full_node.has_valid_pool_sig(new_candidate):
1100
+ self.log.warning("Trying to make a pre-farm block but height is not 0")
1101
+ return None
1102
+
1103
+ # Propagate to ourselves (which validates and does further propagations)
1104
+ try:
1105
+ await self.full_node.add_unfinished_block(new_candidate, None, True)
1106
+ except Exception as e:
1107
+ # If we have an error with this block, try making an empty block
1108
+ self.full_node.log.error(f"Error farming block {e} {new_candidate}")
1109
+ candidate_tuple = self.full_node.full_node_store.get_candidate_block(
1110
+ farmer_request.quality_string, backup=True
1111
+ )
1112
+ if candidate_tuple is not None:
1113
+ height, unfinished_block = candidate_tuple
1114
+ self.full_node.full_node_store.add_candidate_block(
1115
+ farmer_request.quality_string, height, unfinished_block, False
1116
+ )
1117
+ # All unfinished blocks that we create will have the foliage transaction block and hash
1118
+ assert unfinished_block.foliage.foliage_transaction_block_hash is not None
1119
+ message = farmer_protocol.RequestSignedValues(
1120
+ farmer_request.quality_string,
1121
+ unfinished_block.foliage.foliage_block_data.get_hash(),
1122
+ unfinished_block.foliage.foliage_transaction_block_hash,
1123
+ )
1124
+ await peer.send_message(make_msg(ProtocolMessageTypes.request_signed_values, message))
1125
+ return None
1126
+
1127
+ # TIMELORD PROTOCOL
1128
+ @api_request(peer_required=True)
1129
+ async def new_infusion_point_vdf(
1130
+ self, request: timelord_protocol.NewInfusionPointVDF, peer: WSChiaConnection
1131
+ ) -> Optional[Message]:
1132
+ if self.full_node.sync_store.get_sync_mode():
1133
+ return None
1134
+ # Lookup unfinished blocks
1135
+ async with self.full_node.timelord_lock:
1136
+ return await self.full_node.new_infusion_point_vdf(request, peer)
1137
+
1138
+ @api_request(peer_required=True)
1139
+ async def new_signage_point_vdf(
1140
+ self, request: timelord_protocol.NewSignagePointVDF, peer: WSChiaConnection
1141
+ ) -> None:
1142
+ if self.full_node.sync_store.get_sync_mode():
1143
+ return None
1144
+
1145
+ full_node_message = full_node_protocol.RespondSignagePoint(
1146
+ request.index_from_challenge,
1147
+ request.challenge_chain_sp_vdf,
1148
+ request.challenge_chain_sp_proof,
1149
+ request.reward_chain_sp_vdf,
1150
+ request.reward_chain_sp_proof,
1151
+ )
1152
+ await self.respond_signage_point(full_node_message, peer)
1153
+
1154
+ @api_request(peer_required=True)
1155
+ async def new_end_of_sub_slot_vdf(
1156
+ self, request: timelord_protocol.NewEndOfSubSlotVDF, peer: WSChiaConnection
1157
+ ) -> Optional[Message]:
1158
+ if self.full_node.sync_store.get_sync_mode():
1159
+ return None
1160
+ if (
1161
+ self.full_node.full_node_store.get_sub_slot(request.end_of_sub_slot_bundle.challenge_chain.get_hash())
1162
+ is not None
1163
+ ):
1164
+ return None
1165
+ # Calls our own internal message to handle the end of sub slot, and potentially broadcasts to other peers.
1166
+ msg, added = await self.full_node.add_end_of_sub_slot(request.end_of_sub_slot_bundle, peer)
1167
+ if not added:
1168
+ self.log.error(
1169
+ f"Was not able to add end of sub-slot: "
1170
+ f"{request.end_of_sub_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}. "
1171
+ f"Re-sending new-peak to timelord"
1172
+ )
1173
+ await self.full_node.send_peak_to_timelords(peer=peer)
1174
+ return None
1175
+ else:
1176
+ return msg
1177
+
1178
+ @api_request()
1179
+ async def request_block_header(self, request: wallet_protocol.RequestBlockHeader) -> Optional[Message]:
1180
+ header_hash = self.full_node.blockchain.height_to_hash(request.height)
1181
+ if header_hash is None:
1182
+ msg = make_msg(ProtocolMessageTypes.reject_header_request, RejectHeaderRequest(request.height))
1183
+ return msg
1184
+ block: Optional[FullBlock] = await self.full_node.block_store.get_full_block(header_hash)
1185
+ if block is None:
1186
+ return None
1187
+
1188
+ tx_removals: List[bytes32] = []
1189
+ tx_additions: List[Coin] = []
1190
+
1191
+ if block.transactions_generator is not None:
1192
+ block_generator: Optional[BlockGenerator] = await get_block_generator(
1193
+ self.full_node.blockchain.lookup_block_generators, block
1194
+ )
1195
+ # get_block_generator() returns None in case the block we specify
1196
+ # does not have a generator (i.e. is not a transaction block).
1197
+ # in this case we've already made sure `block` does have a
1198
+ # transactions_generator, so the block_generator should always be set
1199
+ assert block_generator is not None, "failed to get block_generator for tx-block"
1200
+
1201
+ npc_result = await asyncio.get_running_loop().run_in_executor(
1202
+ self.executor,
1203
+ functools.partial(
1204
+ get_name_puzzle_conditions,
1205
+ block_generator,
1206
+ self.full_node.constants.MAX_BLOCK_COST_CLVM,
1207
+ mempool_mode=False,
1208
+ height=request.height,
1209
+ constants=self.full_node.constants,
1210
+ ),
1211
+ )
1212
+
1213
+ tx_removals, tx_additions = tx_removals_and_additions(npc_result.conds)
1214
+ header_block = get_block_header(block, tx_additions, tx_removals)
1215
+ msg = make_msg(
1216
+ ProtocolMessageTypes.respond_block_header,
1217
+ wallet_protocol.RespondBlockHeader(header_block),
1218
+ )
1219
+ return msg
1220
+
1221
+ @api_request()
1222
+ async def request_additions(self, request: wallet_protocol.RequestAdditions) -> Optional[Message]:
1223
+ if request.header_hash is None:
1224
+ header_hash: Optional[bytes32] = self.full_node.blockchain.height_to_hash(request.height)
1225
+ else:
1226
+ header_hash = request.header_hash
1227
+ if header_hash is None:
1228
+ raise ValueError(f"Block at height {request.height} not found")
1229
+
1230
+ # Note: this might return bad data if there is a reorg in this time
1231
+ additions = await self.full_node.coin_store.get_coins_added_at_height(request.height)
1232
+
1233
+ if self.full_node.blockchain.height_to_hash(request.height) != header_hash:
1234
+ raise ValueError(f"Block {header_hash} no longer in chain, or invalid header_hash")
1235
+
1236
+ puzzlehash_coins_map: Dict[bytes32, List[Coin]] = {}
1237
+ for coin_record in additions:
1238
+ if coin_record.coin.puzzle_hash in puzzlehash_coins_map:
1239
+ puzzlehash_coins_map[coin_record.coin.puzzle_hash].append(coin_record.coin)
1240
+ else:
1241
+ puzzlehash_coins_map[coin_record.coin.puzzle_hash] = [coin_record.coin]
1242
+
1243
+ coins_map: List[Tuple[bytes32, List[Coin]]] = []
1244
+ proofs_map: List[Tuple[bytes32, bytes, Optional[bytes]]] = []
1245
+
1246
+ if request.puzzle_hashes is None:
1247
+ for puzzle_hash, coins in puzzlehash_coins_map.items():
1248
+ coins_map.append((puzzle_hash, coins))
1249
+ response = wallet_protocol.RespondAdditions(request.height, header_hash, coins_map, None)
1250
+ else:
1251
+ # Create addition Merkle set
1252
+ # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
1253
+ leafs: List[bytes32] = []
1254
+ for puzzle, coins in puzzlehash_coins_map.items():
1255
+ leafs.append(puzzle)
1256
+ leafs.append(hash_coin_ids([c.name() for c in coins]))
1257
+
1258
+ addition_merkle_set = MerkleSet(leafs)
1259
+
1260
+ for puzzle_hash in request.puzzle_hashes:
1261
+ # This is a proof of inclusion if it's in (result==True), or exclusion of it's not in
1262
+ result, proof = addition_merkle_set.is_included_already_hashed(puzzle_hash)
1263
+ if puzzle_hash in puzzlehash_coins_map:
1264
+ coins_map.append((puzzle_hash, puzzlehash_coins_map[puzzle_hash]))
1265
+ hash_coin_str = hash_coin_ids([c.name() for c in puzzlehash_coins_map[puzzle_hash]])
1266
+ # This is a proof of inclusion of all coin ids that have this ph
1267
+ result_2, proof_2 = addition_merkle_set.is_included_already_hashed(hash_coin_str)
1268
+ assert result
1269
+ assert result_2
1270
+ proofs_map.append((puzzle_hash, proof, proof_2))
1271
+ else:
1272
+ coins_map.append((puzzle_hash, []))
1273
+ assert not result
1274
+ proofs_map.append((puzzle_hash, proof, None))
1275
+ response = wallet_protocol.RespondAdditions(request.height, header_hash, coins_map, proofs_map)
1276
+ return make_msg(ProtocolMessageTypes.respond_additions, response)
1277
+
1278
+ @api_request()
1279
+ async def request_removals(self, request: wallet_protocol.RequestRemovals) -> Optional[Message]:
1280
+ block: Optional[FullBlock] = await self.full_node.block_store.get_full_block(request.header_hash)
1281
+
1282
+ # We lock so that the coin store does not get modified
1283
+ peak_height = self.full_node.blockchain.get_peak_height()
1284
+ if (
1285
+ block is None
1286
+ or block.is_transaction_block() is False
1287
+ or block.height != request.height
1288
+ or (peak_height is not None and block.height > peak_height)
1289
+ or self.full_node.blockchain.height_to_hash(block.height) != request.header_hash
1290
+ ):
1291
+ reject = wallet_protocol.RejectRemovalsRequest(request.height, request.header_hash)
1292
+ msg = make_msg(ProtocolMessageTypes.reject_removals_request, reject)
1293
+ return msg
1294
+
1295
+ assert block is not None and block.foliage_transaction_block is not None
1296
+
1297
+ # Note: this might return bad data if there is a reorg in this time
1298
+ all_removals: List[CoinRecord] = await self.full_node.coin_store.get_coins_removed_at_height(block.height)
1299
+
1300
+ if self.full_node.blockchain.height_to_hash(block.height) != request.header_hash:
1301
+ raise ValueError(f"Block {block.header_hash} no longer in chain")
1302
+
1303
+ all_removals_dict: Dict[bytes32, Coin] = {}
1304
+ for coin_record in all_removals:
1305
+ all_removals_dict[coin_record.coin.name()] = coin_record.coin
1306
+
1307
+ coins_map: List[Tuple[bytes32, Optional[Coin]]] = []
1308
+ proofs_map: List[Tuple[bytes32, bytes]] = []
1309
+
1310
+ # If there are no transactions, respond with empty lists
1311
+ if block.transactions_generator is None:
1312
+ proofs: Optional[List[Tuple[bytes32, bytes]]]
1313
+ if request.coin_names is None:
1314
+ proofs = None
1315
+ else:
1316
+ proofs = []
1317
+ response = wallet_protocol.RespondRemovals(block.height, block.header_hash, [], proofs)
1318
+ elif request.coin_names is None or len(request.coin_names) == 0:
1319
+ for removed_name, removed_coin in all_removals_dict.items():
1320
+ coins_map.append((removed_name, removed_coin))
1321
+ response = wallet_protocol.RespondRemovals(block.height, block.header_hash, coins_map, None)
1322
+ else:
1323
+ assert block.transactions_generator
1324
+ leafs: List[bytes32] = []
1325
+ for removed_name, removed_coin in all_removals_dict.items():
1326
+ leafs.append(removed_name)
1327
+ removal_merkle_set = MerkleSet(leafs)
1328
+ assert removal_merkle_set.get_root() == block.foliage_transaction_block.removals_root
1329
+ for coin_name in request.coin_names:
1330
+ result, proof = removal_merkle_set.is_included_already_hashed(coin_name)
1331
+ proofs_map.append((coin_name, proof))
1332
+ if coin_name in all_removals_dict:
1333
+ removed_coin = all_removals_dict[coin_name]
1334
+ coins_map.append((coin_name, removed_coin))
1335
+ assert result
1336
+ else:
1337
+ coins_map.append((coin_name, None))
1338
+ assert not result
1339
+ response = wallet_protocol.RespondRemovals(block.height, block.header_hash, coins_map, proofs_map)
1340
+
1341
+ msg = make_msg(ProtocolMessageTypes.respond_removals, response)
1342
+ return msg
1343
+
1344
+ @api_request()
1345
+ async def send_transaction(
1346
+ self, request: wallet_protocol.SendTransaction, *, test: bool = False
1347
+ ) -> Optional[Message]:
1348
+ spend_name = request.transaction.name()
1349
+ if self.full_node.mempool_manager.get_spendbundle(spend_name) is not None:
1350
+ self.full_node.mempool_manager.remove_seen(spend_name)
1351
+ response = wallet_protocol.TransactionAck(spend_name, uint8(MempoolInclusionStatus.SUCCESS), None)
1352
+ return make_msg(ProtocolMessageTypes.transaction_ack, response)
1353
+
1354
+ queue_entry = TransactionQueueEntry(request.transaction, None, spend_name, None, test)
1355
+ await self.full_node.transaction_queue.put(queue_entry, peer_id=None, high_priority=True)
1356
+ try:
1357
+ with anyio.fail_after(delay=45):
1358
+ status, error = await queue_entry.done.wait()
1359
+ except TimeoutError:
1360
+ response = wallet_protocol.TransactionAck(spend_name, uint8(MempoolInclusionStatus.PENDING), None)
1361
+ else:
1362
+ error_name = error.name if error is not None else None
1363
+ if status == MempoolInclusionStatus.SUCCESS:
1364
+ response = wallet_protocol.TransactionAck(spend_name, uint8(status.value), error_name)
1365
+ else:
1366
+ # If if failed/pending, but it previously succeeded (in mempool), this is idempotence, return SUCCESS
1367
+ if self.full_node.mempool_manager.get_spendbundle(spend_name) is not None:
1368
+ response = wallet_protocol.TransactionAck(
1369
+ spend_name, uint8(MempoolInclusionStatus.SUCCESS.value), None
1370
+ )
1371
+ else:
1372
+ response = wallet_protocol.TransactionAck(spend_name, uint8(status.value), error_name)
1373
+ return make_msg(ProtocolMessageTypes.transaction_ack, response)
1374
+
1375
+ @api_request()
1376
+ async def request_puzzle_solution(self, request: wallet_protocol.RequestPuzzleSolution) -> Optional[Message]:
1377
+ coin_name = request.coin_name
1378
+ height = request.height
1379
+ coin_record = await self.full_node.coin_store.get_coin_record(coin_name)
1380
+ reject = wallet_protocol.RejectPuzzleSolution(coin_name, height)
1381
+ reject_msg = make_msg(ProtocolMessageTypes.reject_puzzle_solution, reject)
1382
+ if coin_record is None or coin_record.spent_block_index != height:
1383
+ return reject_msg
1384
+
1385
+ header_hash: Optional[bytes32] = self.full_node.blockchain.height_to_hash(height)
1386
+ if header_hash is None:
1387
+ return reject_msg
1388
+
1389
+ block: Optional[BlockInfo] = await self.full_node.block_store.get_block_info(header_hash)
1390
+
1391
+ if block is None or block.transactions_generator is None:
1392
+ return reject_msg
1393
+
1394
+ block_generator: Optional[BlockGenerator] = await get_block_generator(
1395
+ self.full_node.blockchain.lookup_block_generators, block
1396
+ )
1397
+ assert block_generator is not None
1398
+ try:
1399
+ spend_info = await asyncio.get_running_loop().run_in_executor(
1400
+ self.executor,
1401
+ get_puzzle_and_solution_for_coin,
1402
+ block_generator,
1403
+ coin_record.coin,
1404
+ height,
1405
+ self.full_node.constants,
1406
+ )
1407
+ except ValueError:
1408
+ return reject_msg
1409
+ wrapper = PuzzleSolutionResponse(coin_name, height, spend_info.puzzle, spend_info.solution)
1410
+ response = wallet_protocol.RespondPuzzleSolution(wrapper)
1411
+ response_msg = make_msg(ProtocolMessageTypes.respond_puzzle_solution, response)
1412
+ return response_msg
1413
+
1414
+ @api_request()
1415
+ async def request_block_headers(self, request: wallet_protocol.RequestBlockHeaders) -> Optional[Message]:
1416
+ """Returns header blocks by directly streaming bytes into Message
1417
+
1418
+ This method should be used instead of RequestHeaderBlocks
1419
+ """
1420
+ reject = RejectBlockHeaders(request.start_height, request.end_height)
1421
+
1422
+ if request.end_height < request.start_height or request.end_height - request.start_height > 128:
1423
+ return make_msg(ProtocolMessageTypes.reject_block_headers, reject)
1424
+ if self.full_node.block_store.db_wrapper.db_version == 2:
1425
+ try:
1426
+ blocks_bytes = await self.full_node.block_store.get_block_bytes_in_range(
1427
+ request.start_height, request.end_height
1428
+ )
1429
+ except ValueError:
1430
+ return make_msg(ProtocolMessageTypes.reject_block_headers, reject)
1431
+
1432
+ else:
1433
+ height_to_hash = self.full_node.blockchain.height_to_hash
1434
+ header_hashes: List[bytes32] = []
1435
+ for i in range(request.start_height, request.end_height + 1):
1436
+ header_hash: Optional[bytes32] = height_to_hash(uint32(i))
1437
+ if header_hash is None:
1438
+ return make_msg(ProtocolMessageTypes.reject_header_blocks, reject)
1439
+ header_hashes.append(header_hash)
1440
+
1441
+ blocks_bytes = await self.full_node.block_store.get_block_bytes_by_hash(header_hashes)
1442
+ if len(blocks_bytes) != (request.end_height - request.start_height + 1): # +1 because interval is inclusive
1443
+ return make_msg(ProtocolMessageTypes.reject_block_headers, reject)
1444
+ return_filter = request.return_filter
1445
+ header_blocks_bytes: List[bytes] = [header_block_from_block(memoryview(b), return_filter) for b in blocks_bytes]
1446
+
1447
+ # we're building the RespondHeaderBlocks manually to avoid cost of
1448
+ # dynamic serialization
1449
+ # ---
1450
+ # we start building RespondBlockHeaders response (start_height, end_height)
1451
+ # and then need to define size of list object
1452
+ respond_header_blocks_manually_streamed: bytes = (
1453
+ uint32(request.start_height).stream_to_bytes()
1454
+ + uint32(request.end_height).stream_to_bytes()
1455
+ + uint32(len(header_blocks_bytes)).stream_to_bytes()
1456
+ )
1457
+ # and now stream the whole list in bytes
1458
+ respond_header_blocks_manually_streamed += b"".join(header_blocks_bytes)
1459
+ return make_msg(ProtocolMessageTypes.respond_block_headers, respond_header_blocks_manually_streamed)
1460
+
1461
+ @api_request()
1462
+ async def request_header_blocks(self, request: wallet_protocol.RequestHeaderBlocks) -> Optional[Message]:
1463
+ """DEPRECATED: please use RequestBlockHeaders"""
1464
+ if (
1465
+ request.end_height < request.start_height
1466
+ or request.end_height - request.start_height > self.full_node.constants.MAX_BLOCK_COUNT_PER_REQUESTS
1467
+ ):
1468
+ return None
1469
+ height_to_hash = self.full_node.blockchain.height_to_hash
1470
+ header_hashes: List[bytes32] = []
1471
+ for i in range(request.start_height, request.end_height + 1):
1472
+ header_hash: Optional[bytes32] = height_to_hash(uint32(i))
1473
+ if header_hash is None:
1474
+ reject = RejectHeaderBlocks(request.start_height, request.end_height)
1475
+ msg = make_msg(ProtocolMessageTypes.reject_header_blocks, reject)
1476
+ return msg
1477
+ header_hashes.append(header_hash)
1478
+
1479
+ blocks: List[FullBlock] = await self.full_node.block_store.get_blocks_by_hash(header_hashes)
1480
+ header_blocks = []
1481
+ for block in blocks:
1482
+ added_coins_records_coroutine = self.full_node.coin_store.get_coins_added_at_height(block.height)
1483
+ removed_coins_records_coroutine = self.full_node.coin_store.get_coins_removed_at_height(block.height)
1484
+ added_coins_records, removed_coins_records = await asyncio.gather(
1485
+ added_coins_records_coroutine, removed_coins_records_coroutine
1486
+ )
1487
+ added_coins = [record.coin for record in added_coins_records if not record.coinbase]
1488
+ removal_names = [record.coin.name() for record in removed_coins_records]
1489
+ header_block = get_block_header(block, added_coins, removal_names)
1490
+ header_blocks.append(header_block)
1491
+
1492
+ msg = make_msg(
1493
+ ProtocolMessageTypes.respond_header_blocks,
1494
+ wallet_protocol.RespondHeaderBlocks(request.start_height, request.end_height, header_blocks),
1495
+ )
1496
+ return msg
1497
+
1498
+ @api_request(bytes_required=True, execute_task=True)
1499
+ async def respond_compact_proof_of_time(
1500
+ self, request: timelord_protocol.RespondCompactProofOfTime, request_bytes: bytes = b""
1501
+ ) -> None:
1502
+ if self.full_node.sync_store.get_sync_mode():
1503
+ return None
1504
+ name = std_hash(request_bytes)
1505
+ if name in self.full_node.compact_vdf_requests:
1506
+ self.log.debug(f"Ignoring CompactProofOfTime: {request}, already requested")
1507
+ return None
1508
+
1509
+ self.full_node.compact_vdf_requests.add(name)
1510
+
1511
+ # this semaphore will only allow a limited number of tasks call
1512
+ # new_compact_vdf() at a time, since it can be expensive
1513
+ try:
1514
+ async with self.full_node.compact_vdf_sem.acquire():
1515
+ try:
1516
+ await self.full_node.add_compact_proof_of_time(request)
1517
+ finally:
1518
+ self.full_node.compact_vdf_requests.remove(name)
1519
+ except LimitedSemaphoreFullError:
1520
+ self.log.debug(f"Ignoring CompactProofOfTime: {request}, _waiters")
1521
+
1522
+ return None
1523
+
1524
+ @api_request(peer_required=True, bytes_required=True, execute_task=True)
1525
+ async def new_compact_vdf(
1526
+ self, request: full_node_protocol.NewCompactVDF, peer: WSChiaConnection, request_bytes: bytes = b""
1527
+ ) -> None:
1528
+ if self.full_node.sync_store.get_sync_mode():
1529
+ return None
1530
+
1531
+ name = std_hash(request_bytes)
1532
+ if name in self.full_node.compact_vdf_requests:
1533
+ self.log.debug("Ignoring NewCompactVDF, already requested: %s %s", peer.get_peer_logging(), request)
1534
+ return None
1535
+ self.full_node.compact_vdf_requests.add(name)
1536
+
1537
+ # this semaphore will only allow a limited number of tasks call
1538
+ # new_compact_vdf() at a time, since it can be expensive
1539
+ try:
1540
+ async with self.full_node.compact_vdf_sem.acquire():
1541
+ try:
1542
+ await self.full_node.new_compact_vdf(request, peer)
1543
+ finally:
1544
+ self.full_node.compact_vdf_requests.remove(name)
1545
+ except LimitedSemaphoreFullError:
1546
+ self.log.debug("Ignoring NewCompactVDF, limited semaphore full: %s %s", peer.get_peer_logging(), request)
1547
+ return None
1548
+
1549
+ return None
1550
+
1551
+ @api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_compact_vdf])
1552
+ async def request_compact_vdf(self, request: full_node_protocol.RequestCompactVDF, peer: WSChiaConnection) -> None:
1553
+ if self.full_node.sync_store.get_sync_mode():
1554
+ return None
1555
+ await self.full_node.request_compact_vdf(request, peer)
1556
+ return None
1557
+
1558
+ @api_request(peer_required=True)
1559
+ async def respond_compact_vdf(self, request: full_node_protocol.RespondCompactVDF, peer: WSChiaConnection) -> None:
1560
+ if self.full_node.sync_store.get_sync_mode():
1561
+ return None
1562
+ await self.full_node.add_compact_vdf(request, peer)
1563
+ return None
1564
+
1565
+ @api_request(peer_required=True)
1566
+ async def register_interest_in_puzzle_hash(
1567
+ self, request: wallet_protocol.RegisterForPhUpdates, peer: WSChiaConnection
1568
+ ) -> Message:
1569
+ trusted = self.is_trusted(peer)
1570
+ max_items = self.max_subscribe_response_items(peer)
1571
+ max_subscriptions = self.max_subscriptions(peer)
1572
+
1573
+ # the returned puzzle hashes are the ones we ended up subscribing to.
1574
+ # It will have filtered duplicates and ones exceeding the subscription
1575
+ # limit.
1576
+ puzzle_hashes = self.full_node.subscriptions.add_puzzle_subscriptions(
1577
+ peer.peer_node_id, request.puzzle_hashes, max_subscriptions
1578
+ )
1579
+
1580
+ start_time = time.monotonic()
1581
+
1582
+ # Note that coin state updates may arrive out-of-order on the client side.
1583
+ # We add the subscription before we're done collecting all the coin
1584
+ # state that goes into the response. CoinState updates may be sent
1585
+ # before we send the response
1586
+
1587
+ # Send all coins with requested puzzle hash that have been created after the specified height
1588
+ states: Set[CoinState] = await self.full_node.coin_store.get_coin_states_by_puzzle_hashes(
1589
+ include_spent_coins=True, puzzle_hashes=puzzle_hashes, min_height=request.min_height, max_items=max_items
1590
+ )
1591
+ max_items -= len(states)
1592
+
1593
+ hint_coin_ids = await self.full_node.hint_store.get_coin_ids_multi(
1594
+ cast(Set[bytes], puzzle_hashes), max_items=max_items
1595
+ )
1596
+
1597
+ hint_states: List[CoinState] = []
1598
+ if len(hint_coin_ids) > 0:
1599
+ hint_states = await self.full_node.coin_store.get_coin_states_by_ids(
1600
+ include_spent_coins=True,
1601
+ coin_ids=hint_coin_ids,
1602
+ min_height=request.min_height,
1603
+ max_items=len(hint_coin_ids),
1604
+ )
1605
+ states.update(hint_states)
1606
+
1607
+ end_time = time.monotonic()
1608
+
1609
+ truncated = max_items <= 0
1610
+
1611
+ if truncated or end_time - start_time > 5:
1612
+ self.log.log(
1613
+ logging.WARNING if trusted and truncated else logging.INFO,
1614
+ "RegisterForPhUpdates resulted in %d coin states. "
1615
+ "Request had %d (unique) puzzle hashes and matched %d hints. %s"
1616
+ "The request took %0.2fs",
1617
+ len(states),
1618
+ len(puzzle_hashes),
1619
+ len(hint_states),
1620
+ "The response was truncated. " if truncated else "",
1621
+ end_time - start_time,
1622
+ )
1623
+
1624
+ response = wallet_protocol.RespondToPhUpdates(request.puzzle_hashes, request.min_height, list(states))
1625
+ msg = make_msg(ProtocolMessageTypes.respond_to_ph_update, response)
1626
+ return msg
1627
+
1628
+ @api_request(peer_required=True)
1629
+ async def register_interest_in_coin(
1630
+ self, request: wallet_protocol.RegisterForCoinUpdates, peer: WSChiaConnection
1631
+ ) -> Message:
1632
+ max_items = self.max_subscribe_response_items(peer)
1633
+ max_subscriptions = self.max_subscriptions(peer)
1634
+
1635
+ # TODO: apparently we have tests that expect to receive a
1636
+ # RespondToCoinUpdates even when subscribing to the same coin multiple
1637
+ # times, so we can't optimize away such DB lookups (yet)
1638
+ self.full_node.subscriptions.add_coin_subscriptions(peer.peer_node_id, request.coin_ids, max_subscriptions)
1639
+
1640
+ states: List[CoinState] = await self.full_node.coin_store.get_coin_states_by_ids(
1641
+ include_spent_coins=True, coin_ids=set(request.coin_ids), min_height=request.min_height, max_items=max_items
1642
+ )
1643
+
1644
+ response = wallet_protocol.RespondToCoinUpdates(request.coin_ids, request.min_height, states)
1645
+ msg = make_msg(ProtocolMessageTypes.respond_to_coin_update, response)
1646
+ return msg
1647
+
1648
+ @api_request()
1649
+ async def request_children(self, request: wallet_protocol.RequestChildren) -> Optional[Message]:
1650
+ coin_records: List[CoinRecord] = await self.full_node.coin_store.get_coin_records_by_parent_ids(
1651
+ True, [request.coin_name]
1652
+ )
1653
+ states = [record.coin_state for record in coin_records]
1654
+ response = wallet_protocol.RespondChildren(states)
1655
+ msg = make_msg(ProtocolMessageTypes.respond_children, response)
1656
+ return msg
1657
+
1658
+ @api_request()
1659
+ async def request_ses_hashes(self, request: wallet_protocol.RequestSESInfo) -> Message:
1660
+ """Returns the start and end height of a sub-epoch for the height specified in request"""
1661
+
1662
+ ses_height = self.full_node.blockchain.get_ses_heights()
1663
+ start_height = request.start_height
1664
+ end_height = request.end_height
1665
+ ses_hash_heights = []
1666
+ ses_reward_hashes = []
1667
+
1668
+ for idx, ses_start_height in enumerate(ses_height):
1669
+ if idx == len(ses_height) - 1:
1670
+ break
1671
+
1672
+ next_ses_height = ses_height[idx + 1]
1673
+ # start_ses_hash
1674
+ if ses_start_height <= start_height < next_ses_height:
1675
+ ses_hash_heights.append([ses_start_height, next_ses_height])
1676
+ ses: SubEpochSummary = self.full_node.blockchain.get_ses(ses_start_height)
1677
+ ses_reward_hashes.append(ses.reward_chain_hash)
1678
+ if ses_start_height < end_height < next_ses_height:
1679
+ break
1680
+ else:
1681
+ if idx == len(ses_height) - 2:
1682
+ break
1683
+ # else add extra ses as request start <-> end spans two ses
1684
+ next_next_height = ses_height[idx + 2]
1685
+ ses_hash_heights.append([next_ses_height, next_next_height])
1686
+ nex_ses: SubEpochSummary = self.full_node.blockchain.get_ses(next_ses_height)
1687
+ ses_reward_hashes.append(nex_ses.reward_chain_hash)
1688
+ break
1689
+
1690
+ response = RespondSESInfo(ses_reward_hashes, ses_hash_heights)
1691
+ msg = make_msg(ProtocolMessageTypes.respond_ses_hashes, response)
1692
+ return msg
1693
+
1694
+ @api_request(reply_types=[ProtocolMessageTypes.respond_fee_estimates])
1695
+ async def request_fee_estimates(self, request: wallet_protocol.RequestFeeEstimates) -> Message:
1696
+ def get_fee_estimates(est: FeeEstimatorInterface, req_times: List[uint64]) -> List[FeeEstimate]:
1697
+ now = datetime.now(timezone.utc)
1698
+ utc_time = now.replace(tzinfo=timezone.utc)
1699
+ utc_now = int(utc_time.timestamp())
1700
+ deltas = [max(0, req_ts - utc_now) for req_ts in req_times]
1701
+ fee_rates = [est.estimate_fee_rate(time_offset_seconds=d) for d in deltas]
1702
+ v1_fee_rates = [fee_rate_v2_to_v1(est) for est in fee_rates]
1703
+ return [FeeEstimate(None, req_ts, fee_rate) for req_ts, fee_rate in zip(req_times, v1_fee_rates)]
1704
+
1705
+ fee_estimates: List[FeeEstimate] = get_fee_estimates(
1706
+ self.full_node.mempool_manager.mempool.fee_estimator, request.time_targets
1707
+ )
1708
+ response = RespondFeeEstimates(FeeEstimateGroup(error=None, estimates=fee_estimates))
1709
+ msg = make_msg(ProtocolMessageTypes.respond_fee_estimates, response)
1710
+ return msg
1711
+
1712
+ @api_request(
1713
+ peer_required=True,
1714
+ reply_types=[ProtocolMessageTypes.respond_remove_puzzle_subscriptions],
1715
+ )
1716
+ async def request_remove_puzzle_subscriptions(
1717
+ self, request: wallet_protocol.RequestRemovePuzzleSubscriptions, peer: WSChiaConnection
1718
+ ) -> Message:
1719
+ peer_id = peer.peer_node_id
1720
+ subs = self.full_node.subscriptions
1721
+
1722
+ if request.puzzle_hashes is None:
1723
+ removed = list(subs.puzzle_subscriptions(peer_id))
1724
+ subs.clear_puzzle_subscriptions(peer_id)
1725
+ else:
1726
+ removed = list(subs.remove_puzzle_subscriptions(peer_id, request.puzzle_hashes))
1727
+
1728
+ response = wallet_protocol.RespondRemovePuzzleSubscriptions(removed)
1729
+ msg = make_msg(ProtocolMessageTypes.respond_remove_puzzle_subscriptions, response)
1730
+ return msg
1731
+
1732
+ @api_request(
1733
+ peer_required=True,
1734
+ reply_types=[ProtocolMessageTypes.respond_remove_coin_subscriptions],
1735
+ )
1736
+ async def request_remove_coin_subscriptions(
1737
+ self, request: wallet_protocol.RequestRemoveCoinSubscriptions, peer: WSChiaConnection
1738
+ ) -> Message:
1739
+ peer_id = peer.peer_node_id
1740
+ subs = self.full_node.subscriptions
1741
+
1742
+ if request.coin_ids is None:
1743
+ removed = list(subs.coin_subscriptions(peer_id))
1744
+ subs.clear_coin_subscriptions(peer_id)
1745
+ else:
1746
+ removed = list(subs.remove_coin_subscriptions(peer_id, request.coin_ids))
1747
+
1748
+ response = wallet_protocol.RespondRemoveCoinSubscriptions(removed)
1749
+ msg = make_msg(ProtocolMessageTypes.respond_remove_coin_subscriptions, response)
1750
+ return msg
1751
+
1752
+ @api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_puzzle_state])
1753
+ async def request_puzzle_state(
1754
+ self, request: wallet_protocol.RequestPuzzleState, peer: WSChiaConnection
1755
+ ) -> Message:
1756
+ max_items = self.max_subscribe_response_items(peer)
1757
+ max_subscriptions = self.max_subscriptions(peer)
1758
+ subs = self.full_node.subscriptions
1759
+
1760
+ request_puzzle_hashes = list(dict.fromkeys(request.puzzle_hashes))
1761
+
1762
+ # This is a limit imposed by `batch_coin_states_by_puzzle_hashes`, due to the SQLite variable limit.
1763
+ # It can be increased in the future, and this protocol should be written and tested in a way that
1764
+ # this increase would not break the API.
1765
+ count = CoinStore.MAX_PUZZLE_HASH_BATCH_SIZE
1766
+ puzzle_hashes = request_puzzle_hashes[:count]
1767
+
1768
+ previous_header_hash = (
1769
+ self.full_node.blockchain.height_to_hash(request.previous_height)
1770
+ if request.previous_height is not None
1771
+ else self.full_node.blockchain.constants.GENESIS_CHALLENGE
1772
+ )
1773
+ assert previous_header_hash is not None
1774
+
1775
+ if request.header_hash != previous_header_hash:
1776
+ rejection = wallet_protocol.RejectPuzzleState(uint8(wallet_protocol.RejectStateReason.REORG))
1777
+ msg = make_msg(ProtocolMessageTypes.reject_puzzle_state, rejection)
1778
+ return msg
1779
+
1780
+ # Check if the request would exceed the subscription limit now.
1781
+ def check_subscription_limit() -> Optional[Message]:
1782
+ new_subscription_count = len(puzzle_hashes) + subs.peer_subscription_count(peer.peer_node_id)
1783
+
1784
+ if request.subscribe_when_finished and new_subscription_count > max_subscriptions:
1785
+ rejection = wallet_protocol.RejectPuzzleState(
1786
+ uint8(wallet_protocol.RejectStateReason.EXCEEDED_SUBSCRIPTION_LIMIT)
1787
+ )
1788
+ msg = make_msg(ProtocolMessageTypes.reject_puzzle_state, rejection)
1789
+ return msg
1790
+
1791
+ return None
1792
+
1793
+ sub_rejection = check_subscription_limit()
1794
+ if sub_rejection is not None:
1795
+ return sub_rejection
1796
+
1797
+ min_height = uint32((request.previous_height + 1) if request.previous_height is not None else 0)
1798
+
1799
+ (coin_states, next_min_height) = await self.full_node.coin_store.batch_coin_states_by_puzzle_hashes(
1800
+ puzzle_hashes,
1801
+ min_height=min_height,
1802
+ include_spent=request.filters.include_spent,
1803
+ include_unspent=request.filters.include_unspent,
1804
+ include_hinted=request.filters.include_hinted,
1805
+ min_amount=request.filters.min_amount,
1806
+ max_items=max_items,
1807
+ )
1808
+ is_done = next_min_height is None
1809
+
1810
+ peak_height = self.full_node.blockchain.get_peak_height()
1811
+ assert peak_height is not None
1812
+
1813
+ height = uint32(next_min_height - 1) if next_min_height is not None else peak_height
1814
+ header_hash = self.full_node.blockchain.height_to_hash(height)
1815
+ assert header_hash is not None
1816
+
1817
+ # Check if the request would exceed the subscription limit.
1818
+ # We do this again since we've crossed an `await` point, to prevent a race condition.
1819
+ sub_rejection = check_subscription_limit()
1820
+ if sub_rejection is not None:
1821
+ return sub_rejection
1822
+
1823
+ if is_done and request.subscribe_when_finished:
1824
+ subs.add_puzzle_subscriptions(peer.peer_node_id, puzzle_hashes, max_subscriptions)
1825
+ await self.mempool_updates_for_puzzle_hashes(peer, set(puzzle_hashes), request.filters.include_hinted)
1826
+
1827
+ response = wallet_protocol.RespondPuzzleState(puzzle_hashes, height, header_hash, is_done, coin_states)
1828
+ msg = make_msg(ProtocolMessageTypes.respond_puzzle_state, response)
1829
+ return msg
1830
+
1831
+ @api_request(peer_required=True, reply_types=[ProtocolMessageTypes.respond_coin_state])
1832
+ async def request_coin_state(self, request: wallet_protocol.RequestCoinState, peer: WSChiaConnection) -> Message:
1833
+ max_items = self.max_subscribe_response_items(peer)
1834
+ max_subscriptions = self.max_subscriptions(peer)
1835
+ subs = self.full_node.subscriptions
1836
+
1837
+ request_coin_ids = list(dict.fromkeys(request.coin_ids))
1838
+ coin_ids = request_coin_ids[:max_items]
1839
+
1840
+ previous_header_hash = (
1841
+ self.full_node.blockchain.height_to_hash(request.previous_height)
1842
+ if request.previous_height is not None
1843
+ else self.full_node.blockchain.constants.GENESIS_CHALLENGE
1844
+ )
1845
+ assert previous_header_hash is not None
1846
+
1847
+ if request.header_hash != previous_header_hash:
1848
+ rejection = wallet_protocol.RejectCoinState(uint8(wallet_protocol.RejectStateReason.REORG))
1849
+ msg = make_msg(ProtocolMessageTypes.reject_coin_state, rejection)
1850
+ return msg
1851
+
1852
+ # Check if the request would exceed the subscription limit now.
1853
+ def check_subscription_limit() -> Optional[Message]:
1854
+ new_subscription_count = len(coin_ids) + subs.peer_subscription_count(peer.peer_node_id)
1855
+
1856
+ if request.subscribe and new_subscription_count > max_subscriptions:
1857
+ rejection = wallet_protocol.RejectCoinState(
1858
+ uint8(wallet_protocol.RejectStateReason.EXCEEDED_SUBSCRIPTION_LIMIT)
1859
+ )
1860
+ msg = make_msg(ProtocolMessageTypes.reject_coin_state, rejection)
1861
+ return msg
1862
+
1863
+ return None
1864
+
1865
+ sub_rejection = check_subscription_limit()
1866
+ if sub_rejection is not None:
1867
+ return sub_rejection
1868
+
1869
+ min_height = uint32(request.previous_height + 1 if request.previous_height is not None else 0)
1870
+
1871
+ coin_states = await self.full_node.coin_store.get_coin_states_by_ids(
1872
+ True, coin_ids, min_height=min_height, max_items=max_items
1873
+ )
1874
+
1875
+ # Check if the request would exceed the subscription limit.
1876
+ # We do this again since we've crossed an `await` point, to prevent a race condition.
1877
+ sub_rejection = check_subscription_limit()
1878
+ if sub_rejection is not None:
1879
+ return sub_rejection
1880
+
1881
+ if request.subscribe:
1882
+ subs.add_coin_subscriptions(peer.peer_node_id, coin_ids, max_subscriptions)
1883
+ await self.mempool_updates_for_coin_ids(peer, set(coin_ids))
1884
+
1885
+ response = wallet_protocol.RespondCoinState(coin_ids, coin_states)
1886
+ msg = make_msg(ProtocolMessageTypes.respond_coin_state, response)
1887
+ return msg
1888
+
1889
+ @api_request(reply_types=[ProtocolMessageTypes.respond_cost_info])
1890
+ async def request_cost_info(self, _request: wallet_protocol.RequestCostInfo) -> Optional[Message]:
1891
+ mempool_manager = self.full_node.mempool_manager
1892
+ response = wallet_protocol.RespondCostInfo(
1893
+ max_transaction_cost=mempool_manager.max_tx_clvm_cost,
1894
+ max_block_cost=mempool_manager.max_block_clvm_cost,
1895
+ max_mempool_cost=uint64(mempool_manager.mempool_max_total_cost),
1896
+ mempool_cost=uint64(mempool_manager.mempool._total_cost),
1897
+ mempool_fee=uint64(mempool_manager.mempool._total_fee),
1898
+ bump_fee_per_cost=uint8(mempool_manager.nonzero_fee_minimum_fpc),
1899
+ )
1900
+ msg = make_msg(ProtocolMessageTypes.respond_cost_info, response)
1901
+ return msg
1902
+
1903
+ async def mempool_updates_for_puzzle_hashes(
1904
+ self, peer: WSChiaConnection, puzzle_hashes: Set[bytes32], include_hints: bool
1905
+ ) -> None:
1906
+ if Capability.MEMPOOL_UPDATES not in peer.peer_capabilities:
1907
+ return
1908
+
1909
+ start_time = time.monotonic()
1910
+
1911
+ async with self.full_node.db_wrapper.reader() as conn:
1912
+ transaction_ids = set(
1913
+ self.full_node.mempool_manager.mempool.items_with_puzzle_hashes(puzzle_hashes, include_hints)
1914
+ )
1915
+
1916
+ hinted_coin_ids: Set[bytes32] = set()
1917
+
1918
+ for batch in to_batches(puzzle_hashes, SQLITE_MAX_VARIABLE_NUMBER):
1919
+ hints_db: Tuple[bytes, ...] = tuple(batch.entries)
1920
+ cursor = await conn.execute(
1921
+ f"SELECT coin_id from hints INDEXED BY hint_index "
1922
+ f'WHERE hint IN ({"?," * (len(batch.entries) - 1)}?)',
1923
+ hints_db,
1924
+ )
1925
+ for row in await cursor.fetchall():
1926
+ hinted_coin_ids.add(bytes32(row[0]))
1927
+ await cursor.close()
1928
+
1929
+ transaction_ids |= set(self.full_node.mempool_manager.mempool.items_with_coin_ids(hinted_coin_ids))
1930
+
1931
+ if len(transaction_ids) > 0:
1932
+ message = wallet_protocol.MempoolItemsAdded(list(transaction_ids))
1933
+ await peer.send_message(make_msg(ProtocolMessageTypes.mempool_items_added, message))
1934
+
1935
+ total_time = time.monotonic() - start_time
1936
+
1937
+ self.log.log(
1938
+ logging.DEBUG if total_time < 2.0 else logging.WARNING,
1939
+ f"Sending initial mempool items to peer {peer.peer_node_id} took {total_time:.4f}s",
1940
+ )
1941
+
1942
+ async def mempool_updates_for_coin_ids(self, peer: WSChiaConnection, coin_ids: Set[bytes32]) -> None:
1943
+ if Capability.MEMPOOL_UPDATES not in peer.peer_capabilities:
1944
+ return
1945
+
1946
+ start_time = time.monotonic()
1947
+
1948
+ transaction_ids = self.full_node.mempool_manager.mempool.items_with_coin_ids(coin_ids)
1949
+
1950
+ if len(transaction_ids) > 0:
1951
+ message = wallet_protocol.MempoolItemsAdded(list(transaction_ids))
1952
+ await peer.send_message(make_msg(ProtocolMessageTypes.mempool_items_added, message))
1953
+
1954
+ total_time = time.monotonic() - start_time
1955
+
1956
+ self.log.log(
1957
+ logging.DEBUG if total_time < 2.0 else logging.WARNING,
1958
+ f"Sending initial mempool items to peer {peer.peer_node_id} took {total_time:.4f}s",
1959
+ )
1960
+
1961
+ def max_subscriptions(self, peer: WSChiaConnection) -> int:
1962
+ if self.is_trusted(peer):
1963
+ return cast(int, self.full_node.config.get("trusted_max_subscribe_items", 2000000))
1964
+ else:
1965
+ return cast(int, self.full_node.config.get("max_subscribe_items", 200000))
1966
+
1967
+ def max_subscribe_response_items(self, peer: WSChiaConnection) -> int:
1968
+ if self.is_trusted(peer):
1969
+ return cast(int, self.full_node.config.get("trusted_max_subscribe_response_items", 500000))
1970
+ else:
1971
+ return cast(int, self.full_node.config.get("max_subscribe_response_items", 100000))
1972
+
1973
+ def is_trusted(self, peer: WSChiaConnection) -> bool:
1974
+ return self.server.is_trusted_peer(peer, self.full_node.config.get("trusted_peers", {}))