chia-blockchain 2.5.4rc2__py3-none-any.whl → 2.5.5rc2__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 (453) hide show
  1. chia/_tests/blockchain/blockchain_test_utils.py +2 -3
  2. chia/_tests/blockchain/test_augmented_chain.py +2 -3
  3. chia/_tests/blockchain/test_blockchain.py +261 -44
  4. chia/_tests/blockchain/test_blockchain_transactions.py +4 -3
  5. chia/_tests/blockchain/test_build_chains.py +197 -1
  6. chia/_tests/blockchain/test_get_block_generator.py +1 -1
  7. chia/_tests/blockchain/test_lookup_fork_chain.py +1 -1
  8. chia/_tests/clvm/benchmark_costs.py +1 -1
  9. chia/_tests/clvm/coin_store.py +3 -4
  10. chia/_tests/clvm/test_message_conditions.py +2 -2
  11. chia/_tests/clvm/test_puzzle_compression.py +2 -3
  12. chia/_tests/clvm/test_puzzles.py +1 -2
  13. chia/_tests/clvm/test_singletons.py +2 -3
  14. chia/_tests/clvm/test_spend_sim.py +7 -7
  15. chia/_tests/cmds/cmd_test_utils.py +30 -25
  16. chia/_tests/cmds/test_dev_gh.py +1 -1
  17. chia/_tests/cmds/test_farm_cmd.py +1 -1
  18. chia/_tests/cmds/test_show.py +1 -2
  19. chia/_tests/cmds/wallet/test_did.py +101 -56
  20. chia/_tests/cmds/wallet/test_nft.py +109 -84
  21. chia/_tests/cmds/wallet/test_notifications.py +1 -1
  22. chia/_tests/cmds/wallet/test_offer.toffer +1 -1
  23. chia/_tests/cmds/wallet/test_vcs.py +8 -8
  24. chia/_tests/cmds/wallet/test_wallet.py +100 -46
  25. chia/_tests/conftest.py +31 -20
  26. chia/_tests/connection_utils.py +1 -1
  27. chia/_tests/core/consensus/stores/__init__.py +0 -0
  28. chia/_tests/core/consensus/stores/test_coin_store_protocol.py +40 -0
  29. chia/_tests/core/consensus/test_block_creation.py +2 -31
  30. chia/_tests/core/consensus/test_pot_iterations.py +38 -3
  31. chia/_tests/core/custom_types/test_proof_of_space.py +154 -26
  32. chia/_tests/core/custom_types/test_spend_bundle.py +2 -3
  33. chia/_tests/core/daemon/test_daemon.py +80 -0
  34. chia/_tests/core/data_layer/test_data_layer.py +1 -1
  35. chia/_tests/core/data_layer/test_data_layer_util.py +1 -1
  36. chia/_tests/core/data_layer/test_data_rpc.py +14 -10
  37. chia/_tests/core/data_layer/test_data_store.py +5 -5
  38. chia/_tests/core/farmer/test_farmer_api.py +2 -2
  39. chia/_tests/core/full_node/full_sync/test_full_sync.py +446 -406
  40. chia/_tests/core/full_node/ram_db.py +3 -1
  41. chia/_tests/core/full_node/stores/test_block_store.py +28 -16
  42. chia/_tests/core/full_node/stores/test_coin_store.py +277 -185
  43. chia/_tests/core/full_node/stores/test_full_node_store.py +11 -4
  44. chia/_tests/core/full_node/stores/test_hint_store.py +2 -2
  45. chia/_tests/core/full_node/test_address_manager.py +200 -27
  46. chia/_tests/core/full_node/test_block_height_map.py +2 -2
  47. chia/_tests/core/full_node/test_conditions.py +7 -6
  48. chia/_tests/core/full_node/test_full_node.py +456 -40
  49. chia/_tests/core/full_node/test_generator_tools.py +32 -2
  50. chia/_tests/core/full_node/test_hint_management.py +1 -1
  51. chia/_tests/core/full_node/test_node_load.py +20 -21
  52. chia/_tests/core/full_node/test_performance.py +3 -4
  53. chia/_tests/core/full_node/test_prev_tx_block.py +43 -0
  54. chia/_tests/core/full_node/test_subscriptions.py +1 -2
  55. chia/_tests/core/full_node/test_transactions.py +9 -5
  56. chia/_tests/core/full_node/test_tx_processing_queue.py +1 -2
  57. chia/_tests/core/large_block.py +1 -2
  58. chia/_tests/core/make_block_generator.py +3 -4
  59. chia/_tests/core/mempool/test_mempool.py +36 -86
  60. chia/_tests/core/mempool/test_mempool_fee_estimator.py +1 -1
  61. chia/_tests/core/mempool/test_mempool_item_queries.py +1 -3
  62. chia/_tests/core/mempool/test_mempool_manager.py +421 -69
  63. chia/_tests/core/mempool/test_mempool_performance.py +3 -2
  64. chia/_tests/core/mempool/test_singleton_fast_forward.py +60 -131
  65. chia/_tests/core/server/flood.py +1 -1
  66. chia/_tests/core/server/test_dos.py +1 -1
  67. chia/_tests/core/server/test_node_discovery.py +41 -27
  68. chia/_tests/core/server/test_rate_limits.py +1 -1
  69. chia/_tests/core/server/test_server.py +1 -1
  70. chia/_tests/core/services/test_services.py +5 -5
  71. chia/_tests/core/ssl/test_ssl.py +1 -1
  72. chia/_tests/core/test_cost_calculation.py +6 -6
  73. chia/_tests/core/test_crawler.py +2 -2
  74. chia/_tests/core/test_crawler_rpc.py +1 -1
  75. chia/_tests/core/test_db_conversion.py +3 -1
  76. chia/_tests/core/test_db_validation.py +5 -3
  77. chia/_tests/core/test_farmer_harvester_rpc.py +15 -15
  78. chia/_tests/core/test_filter.py +4 -1
  79. chia/_tests/core/test_full_node_rpc.py +99 -82
  80. chia/_tests/core/test_program.py +2 -2
  81. chia/_tests/core/util/test_block_cache.py +1 -1
  82. chia/_tests/core/util/test_keychain.py +2 -2
  83. chia/_tests/core/util/test_lockfile.py +1 -1
  84. chia/_tests/core/util/test_log_exceptions.py +5 -5
  85. chia/_tests/core/util/test_streamable.py +81 -22
  86. chia/_tests/db/test_db_wrapper.py +1 -3
  87. chia/_tests/environments/wallet.py +5 -5
  88. chia/_tests/farmer_harvester/test_farmer.py +9 -7
  89. chia/_tests/farmer_harvester/test_farmer_harvester.py +11 -4
  90. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +6 -5
  91. chia/_tests/farmer_harvester/test_third_party_harvesters.py +15 -9
  92. chia/_tests/fee_estimation/test_fee_estimation_integration.py +1 -2
  93. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +7 -5
  94. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +1 -1
  95. chia/_tests/generator/test_compression.py +1 -2
  96. chia/_tests/generator/test_rom.py +8 -4
  97. chia/_tests/plot_sync/test_plot_sync.py +3 -3
  98. chia/_tests/plot_sync/test_receiver.py +3 -3
  99. chia/_tests/plot_sync/test_sender.py +1 -1
  100. chia/_tests/plot_sync/test_sync_simulated.py +3 -3
  101. chia/_tests/plot_sync/util.py +2 -2
  102. chia/_tests/pools/test_pool_cmdline.py +48 -21
  103. chia/_tests/pools/test_pool_puzzles_lifecycle.py +2 -3
  104. chia/_tests/pools/test_pool_rpc.py +237 -105
  105. chia/_tests/pools/test_pool_wallet.py +11 -2
  106. chia/_tests/pools/test_wallet_pool_store.py +5 -4
  107. chia/_tests/rpc/test_rpc_client.py +1 -1
  108. chia/_tests/simulation/test_simulation.py +13 -8
  109. chia/_tests/simulation/test_simulator.py +2 -2
  110. chia/_tests/timelord/test_new_peak.py +191 -47
  111. chia/_tests/timelord/test_timelord.py +1 -1
  112. chia/_tests/tools/test_full_sync.py +0 -2
  113. chia/_tests/tools/test_run_block.py +3 -1
  114. chia/_tests/util/benchmark_cost.py +3 -3
  115. chia/_tests/util/benchmarks.py +2 -2
  116. chia/_tests/util/blockchain.py +11 -5
  117. chia/_tests/util/blockchain_mock.py +1 -4
  118. chia/_tests/util/coin_store.py +29 -0
  119. chia/_tests/util/constants.py +2 -18
  120. chia/_tests/util/full_sync.py +3 -3
  121. chia/_tests/util/generator_tools_testing.py +2 -3
  122. chia/_tests/util/key_tool.py +2 -3
  123. chia/_tests/util/misc.py +33 -31
  124. chia/_tests/util/network_protocol_data.py +19 -17
  125. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  126. chia/_tests/util/protocol_messages_json.py +3 -1
  127. chia/_tests/util/run_block.py +2 -2
  128. chia/_tests/util/setup_nodes.py +7 -7
  129. chia/_tests/util/spend_sim.py +47 -55
  130. chia/_tests/util/test_condition_tools.py +5 -4
  131. chia/_tests/util/test_config.py +2 -2
  132. chia/_tests/util/test_dump_keyring.py +1 -1
  133. chia/_tests/util/test_full_block_utils.py +12 -14
  134. chia/_tests/util/test_misc.py +2 -2
  135. chia/_tests/util/test_paginator.py +4 -4
  136. chia/_tests/util/test_priority_mutex.py +2 -2
  137. chia/_tests/util/test_replace_str_to_bytes.py +15 -5
  138. chia/_tests/util/test_ssl_check.py +1 -1
  139. chia/_tests/util/test_testnet_overrides.py +13 -3
  140. chia/_tests/util/time_out_assert.py +4 -2
  141. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +1 -1
  142. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +1 -2
  143. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +352 -432
  144. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +3 -6
  145. chia/_tests/wallet/cat_wallet/test_trades.py +53 -77
  146. chia/_tests/wallet/clawback/test_clawback_decorator.py +3 -1
  147. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +3 -3
  148. chia/_tests/wallet/clawback/test_clawback_metadata.py +4 -2
  149. chia/_tests/wallet/conftest.py +11 -12
  150. chia/_tests/wallet/db_wallet/test_db_graftroot.py +11 -4
  151. chia/_tests/wallet/db_wallet/test_dl_offers.py +433 -130
  152. chia/_tests/wallet/db_wallet/test_dl_wallet.py +3 -3
  153. chia/_tests/wallet/did_wallet/test_did.py +2132 -2000
  154. chia/_tests/wallet/nft_wallet/config.py +1 -1
  155. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1610 -742
  156. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +486 -907
  157. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +4 -4
  158. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +517 -294
  159. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +133 -62
  160. chia/_tests/wallet/rpc/test_wallet_rpc.py +495 -265
  161. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +10 -6
  162. chia/_tests/wallet/sync/test_wallet_sync.py +89 -60
  163. chia/_tests/wallet/test_clvm_casts.py +88 -0
  164. chia/_tests/wallet/test_coin_management.py +1 -1
  165. chia/_tests/wallet/test_coin_selection.py +1 -1
  166. chia/_tests/wallet/test_conditions.py +1 -1
  167. chia/_tests/wallet/test_new_wallet_protocol.py +13 -11
  168. chia/_tests/wallet/test_notifications.py +5 -3
  169. chia/_tests/wallet/test_sign_coin_spends.py +6 -6
  170. chia/_tests/wallet/test_signer_protocol.py +13 -12
  171. chia/_tests/wallet/test_singleton.py +1 -1
  172. chia/_tests/wallet/test_singleton_lifecycle_fast.py +5 -7
  173. chia/_tests/wallet/test_util.py +2 -2
  174. chia/_tests/wallet/test_wallet.py +108 -29
  175. chia/_tests/wallet/test_wallet_action_scope.py +9 -2
  176. chia/_tests/wallet/test_wallet_blockchain.py +2 -3
  177. chia/_tests/wallet/test_wallet_key_val_store.py +1 -2
  178. chia/_tests/wallet/test_wallet_node.py +2 -4
  179. chia/_tests/wallet/test_wallet_retry.py +4 -2
  180. chia/_tests/wallet/test_wallet_state_manager.py +191 -5
  181. chia/_tests/wallet/test_wallet_test_framework.py +1 -1
  182. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +8 -8
  183. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +29 -12
  184. chia/_tests/wallet/wallet_block_tools.py +6 -6
  185. chia/_tests/weight_proof/test_weight_proof.py +10 -48
  186. chia/apis.py +1 -1
  187. chia/cmds/beta.py +1 -1
  188. chia/cmds/chia.py +9 -9
  189. chia/cmds/cmd_classes.py +12 -11
  190. chia/cmds/cmd_helpers.py +1 -1
  191. chia/cmds/cmds_util.py +12 -9
  192. chia/cmds/coin_funcs.py +2 -2
  193. chia/cmds/configure.py +2 -2
  194. chia/cmds/data.py +0 -2
  195. chia/cmds/data_funcs.py +1 -1
  196. chia/cmds/db_validate_func.py +1 -2
  197. chia/cmds/dev/__init__.py +0 -0
  198. chia/cmds/dev/data.py +273 -0
  199. chia/cmds/{gh.py → dev/gh.py} +5 -5
  200. chia/cmds/dev/main.py +22 -0
  201. chia/cmds/dev/mempool.py +78 -0
  202. chia/cmds/dev/mempool_funcs.py +63 -0
  203. chia/cmds/farm_funcs.py +5 -4
  204. chia/cmds/init_funcs.py +11 -11
  205. chia/cmds/keys.py +2 -2
  206. chia/cmds/keys_funcs.py +4 -4
  207. chia/cmds/netspace_funcs.py +1 -1
  208. chia/cmds/peer_funcs.py +2 -2
  209. chia/cmds/plotnft_funcs.py +72 -26
  210. chia/cmds/rpc.py +1 -1
  211. chia/cmds/show_funcs.py +5 -5
  212. chia/cmds/signer.py +8 -7
  213. chia/cmds/sim_funcs.py +8 -9
  214. chia/cmds/wallet.py +2 -2
  215. chia/cmds/wallet_funcs.py +165 -131
  216. chia/{util → consensus}/augmented_chain.py +1 -2
  217. chia/consensus/block_body_validation.py +54 -40
  218. chia/consensus/block_creation.py +42 -76
  219. chia/consensus/block_header_validation.py +32 -26
  220. chia/consensus/block_record.py +0 -3
  221. chia/consensus/blockchain.py +23 -32
  222. chia/consensus/blockchain_interface.py +1 -5
  223. chia/consensus/check_time_locks.py +57 -0
  224. chia/consensus/coin_store_protocol.py +151 -0
  225. chia/consensus/coinbase.py +0 -6
  226. chia/consensus/condition_costs.py +4 -0
  227. chia/{util → consensus}/condition_tools.py +4 -5
  228. chia/consensus/cost_calculator.py +1 -1
  229. chia/consensus/default_constants.py +32 -9
  230. chia/consensus/deficit.py +1 -3
  231. chia/consensus/difficulty_adjustment.py +1 -2
  232. chia/consensus/find_fork_point.py +1 -3
  233. chia/consensus/full_block_to_block_record.py +1 -6
  234. chia/{util → consensus}/generator_tools.py +1 -3
  235. chia/consensus/get_block_challenge.py +30 -7
  236. chia/consensus/make_sub_epoch_summary.py +1 -5
  237. chia/consensus/multiprocess_validation.py +21 -20
  238. chia/consensus/pot_iterations.py +74 -13
  239. chia/{util → consensus}/prev_transaction_block.py +1 -1
  240. chia/consensus/vdf_info_computation.py +1 -3
  241. chia/daemon/keychain_proxy.py +5 -5
  242. chia/daemon/server.py +22 -5
  243. chia/data_layer/data_layer.py +92 -51
  244. chia/{rpc → data_layer}/data_layer_rpc_api.py +1 -1
  245. chia/{rpc → data_layer}/data_layer_rpc_util.py +3 -6
  246. chia/data_layer/data_layer_util.py +4 -6
  247. chia/data_layer/data_layer_wallet.py +42 -69
  248. chia/data_layer/dl_wallet_store.py +12 -6
  249. chia/data_layer/download_data.py +3 -3
  250. chia/data_layer/s3_plugin_service.py +0 -1
  251. chia/farmer/farmer.py +3 -4
  252. chia/farmer/farmer_api.py +11 -7
  253. chia/{rpc → farmer}/farmer_rpc_client.py +1 -1
  254. chia/full_node/block_height_map.py +7 -6
  255. chia/full_node/block_store.py +5 -7
  256. chia/full_node/bundle_tools.py +1 -2
  257. chia/full_node/coin_store.py +143 -124
  258. chia/{types → full_node}/eligible_coin_spends.py +39 -70
  259. chia/full_node/fee_estimator.py +1 -1
  260. chia/full_node/fee_estimator_interface.py +0 -8
  261. chia/full_node/fee_tracker.py +25 -25
  262. chia/full_node/full_node.py +70 -53
  263. chia/full_node/full_node_api.py +57 -40
  264. chia/{rpc → full_node}/full_node_rpc_api.py +87 -8
  265. chia/{rpc → full_node}/full_node_rpc_client.py +7 -6
  266. chia/full_node/full_node_store.py +23 -8
  267. chia/full_node/mempool.py +206 -53
  268. chia/full_node/mempool_check_conditions.py +20 -63
  269. chia/full_node/mempool_manager.py +26 -40
  270. chia/full_node/subscriptions.py +1 -3
  271. chia/full_node/tx_processing_queue.py +50 -3
  272. chia/full_node/weight_proof.py +46 -37
  273. chia/harvester/harvester.py +1 -1
  274. chia/harvester/harvester_api.py +22 -7
  275. chia/introducer/introducer.py +1 -1
  276. chia/introducer/introducer_api.py +1 -1
  277. chia/plot_sync/exceptions.py +1 -1
  278. chia/plot_sync/receiver.py +1 -1
  279. chia/plot_sync/sender.py +2 -2
  280. chia/pools/pool_puzzles.py +13 -18
  281. chia/pools/pool_wallet.py +23 -46
  282. chia/protocols/farmer_protocol.py +11 -3
  283. chia/protocols/full_node_protocol.py +1 -4
  284. chia/protocols/harvester_protocol.py +3 -3
  285. chia/protocols/pool_protocol.py +1 -2
  286. chia/protocols/shared_protocol.py +3 -3
  287. chia/protocols/timelord_protocol.py +1 -3
  288. chia/protocols/wallet_protocol.py +3 -3
  289. chia/rpc/rpc_client.py +7 -8
  290. chia/rpc/rpc_server.py +3 -3
  291. chia/rpc/util.py +3 -1
  292. chia/seeder/crawler.py +1 -1
  293. chia/seeder/crawler_api.py +1 -1
  294. chia/seeder/dns_server.py +2 -0
  295. chia/seeder/start_crawler.py +3 -3
  296. chia/server/address_manager.py +286 -38
  297. chia/server/address_manager_store.py +0 -215
  298. chia/{types → server}/aliases.py +7 -7
  299. chia/server/api_protocol.py +1 -1
  300. chia/server/chia_policy.py +1 -1
  301. chia/server/node_discovery.py +76 -113
  302. chia/server/rate_limits.py +1 -1
  303. chia/server/resolve_peer_info.py +43 -0
  304. chia/server/server.py +5 -5
  305. chia/server/start_data_layer.py +4 -4
  306. chia/server/start_farmer.py +5 -4
  307. chia/server/start_full_node.py +5 -4
  308. chia/server/start_harvester.py +7 -5
  309. chia/server/start_introducer.py +2 -2
  310. chia/server/start_service.py +1 -1
  311. chia/server/start_timelord.py +7 -5
  312. chia/server/start_wallet.py +7 -5
  313. chia/server/ws_connection.py +1 -1
  314. chia/simulator/add_blocks_in_batches.py +2 -2
  315. chia/simulator/block_tools.py +245 -201
  316. chia/simulator/full_node_simulator.py +38 -10
  317. chia/simulator/setup_services.py +12 -12
  318. chia/simulator/simulator_full_node_rpc_api.py +2 -2
  319. chia/simulator/simulator_full_node_rpc_client.py +2 -2
  320. chia/simulator/simulator_test_tools.py +2 -2
  321. chia/simulator/start_simulator.py +1 -1
  322. chia/simulator/wallet_tools.py +10 -18
  323. chia/ssl/create_ssl.py +1 -1
  324. chia/timelord/iters_from_block.py +14 -14
  325. chia/timelord/timelord.py +15 -11
  326. chia/timelord/timelord_api.py +14 -2
  327. chia/timelord/timelord_state.py +20 -14
  328. chia/types/blockchain_format/program.py +53 -10
  329. chia/types/blockchain_format/proof_of_space.py +73 -19
  330. chia/types/coin_spend.py +3 -56
  331. chia/types/generator_types.py +28 -0
  332. chia/types/internal_mempool_item.py +1 -2
  333. chia/types/mempool_item.py +12 -7
  334. chia/types/unfinished_header_block.py +1 -2
  335. chia/types/validation_state.py +1 -2
  336. chia/types/weight_proof.py +1 -3
  337. chia/util/action_scope.py +3 -3
  338. chia/util/block_cache.py +1 -2
  339. chia/util/byte_types.py +1 -1
  340. chia/util/casts.py +21 -0
  341. chia/util/config.py +0 -37
  342. chia/util/db_wrapper.py +8 -1
  343. chia/util/errors.py +3 -2
  344. chia/util/initial-config.yaml +21 -5
  345. chia/util/keychain.py +6 -7
  346. chia/util/keyring_wrapper.py +5 -5
  347. chia/util/limited_semaphore.py +1 -1
  348. chia/util/priority_mutex.py +1 -1
  349. chia/util/streamable.py +63 -5
  350. chia/util/task_timing.py +1 -1
  351. chia/util/virtual_project_analysis.py +1 -1
  352. chia/wallet/cat_wallet/cat_info.py +7 -3
  353. chia/wallet/cat_wallet/cat_outer_puzzle.py +9 -5
  354. chia/wallet/cat_wallet/cat_utils.py +1 -1
  355. chia/wallet/cat_wallet/cat_wallet.py +44 -36
  356. chia/wallet/cat_wallet/lineage_store.py +7 -0
  357. chia/wallet/cat_wallet/r_cat_wallet.py +274 -0
  358. chia/wallet/conditions.py +5 -10
  359. chia/wallet/db_wallet/db_wallet_puzzles.py +4 -4
  360. chia/wallet/derivation_record.py +33 -0
  361. chia/wallet/derive_keys.py +3 -3
  362. chia/wallet/did_wallet/did_info.py +12 -3
  363. chia/wallet/did_wallet/did_wallet.py +132 -101
  364. chia/wallet/did_wallet/did_wallet_puzzles.py +9 -9
  365. chia/wallet/driver_protocol.py +3 -1
  366. chia/{types/spend_bundle.py → wallet/estimate_fees.py} +2 -7
  367. chia/wallet/nft_wallet/metadata_outer_puzzle.py +5 -3
  368. chia/wallet/nft_wallet/nft_puzzle_utils.py +1 -1
  369. chia/wallet/nft_wallet/nft_wallet.py +69 -112
  370. chia/wallet/nft_wallet/ownership_outer_puzzle.py +5 -3
  371. chia/wallet/nft_wallet/singleton_outer_puzzle.py +6 -4
  372. chia/wallet/nft_wallet/transfer_program_puzzle.py +4 -2
  373. chia/wallet/nft_wallet/uncurry_nft.py +4 -6
  374. chia/wallet/notification_manager.py +2 -3
  375. chia/wallet/outer_puzzles.py +7 -2
  376. chia/wallet/puzzle_drivers.py +1 -1
  377. chia/wallet/puzzles/clawback/drivers.py +5 -4
  378. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +1 -1
  379. chia/wallet/puzzles/singleton_top_layer.py +2 -1
  380. chia/wallet/puzzles/singleton_top_layer_v1_1.py +2 -1
  381. chia/wallet/puzzles/tails.py +1 -3
  382. chia/wallet/signer_protocol.py +5 -6
  383. chia/wallet/singleton.py +5 -4
  384. chia/wallet/singleton_record.py +1 -1
  385. chia/wallet/trade_manager.py +18 -20
  386. chia/wallet/trade_record.py +3 -6
  387. chia/wallet/trading/offer.py +12 -13
  388. chia/wallet/uncurried_puzzle.py +2 -2
  389. chia/wallet/util/compute_additions.py +58 -0
  390. chia/wallet/util/compute_hints.py +3 -3
  391. chia/wallet/util/compute_memos.py +4 -4
  392. chia/wallet/util/curry_and_treehash.py +2 -1
  393. chia/wallet/util/debug_spend_bundle.py +1 -1
  394. chia/wallet/util/merkle_tree.py +1 -1
  395. chia/wallet/util/peer_request_cache.py +1 -2
  396. chia/wallet/util/tx_config.py +3 -8
  397. chia/wallet/util/wallet_sync_utils.py +10 -5
  398. chia/wallet/util/wallet_types.py +1 -0
  399. chia/wallet/vc_wallet/cr_cat_drivers.py +17 -18
  400. chia/wallet/vc_wallet/cr_cat_wallet.py +30 -28
  401. chia/wallet/vc_wallet/cr_outer_puzzle.py +5 -3
  402. chia/wallet/vc_wallet/vc_drivers.py +50 -8
  403. chia/wallet/vc_wallet/vc_store.py +3 -5
  404. chia/wallet/vc_wallet/vc_wallet.py +15 -22
  405. chia/wallet/wallet.py +36 -46
  406. chia/wallet/wallet_action_scope.py +73 -4
  407. chia/wallet/wallet_blockchain.py +1 -3
  408. chia/wallet/wallet_interested_store.py +1 -1
  409. chia/wallet/wallet_nft_store.py +3 -3
  410. chia/wallet/wallet_node.py +17 -16
  411. chia/wallet/wallet_node_api.py +4 -5
  412. chia/wallet/wallet_pool_store.py +1 -1
  413. chia/wallet/wallet_protocol.py +2 -0
  414. chia/wallet/wallet_puzzle_store.py +1 -1
  415. chia/{rpc → wallet}/wallet_request_types.py +670 -81
  416. chia/{rpc → wallet}/wallet_rpc_api.py +735 -766
  417. chia/{rpc → wallet}/wallet_rpc_client.py +268 -420
  418. chia/wallet/wallet_singleton_store.py +8 -7
  419. chia/wallet/wallet_spend_bundle.py +4 -3
  420. chia/wallet/wallet_state_manager.py +320 -191
  421. chia/wallet/wallet_weight_proof_handler.py +1 -2
  422. chia/wallet/wsm_apis.py +98 -0
  423. {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc2.dist-info}/METADATA +7 -7
  424. {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc2.dist-info}/RECORD +443 -436
  425. mozilla-ca/cacert.pem +3 -165
  426. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +0 -145
  427. chia/cmds/dev.py +0 -18
  428. chia/types/blockchain_format/slots.py +0 -9
  429. chia/types/blockchain_format/sub_epoch_summary.py +0 -5
  430. chia/types/end_of_slot_bundle.py +0 -5
  431. chia/types/full_block.py +0 -5
  432. chia/types/header_block.py +0 -5
  433. chia/types/spend_bundle_conditions.py +0 -7
  434. chia/types/transaction_queue_entry.py +0 -56
  435. chia/types/unfinished_block.py +0 -5
  436. /chia/cmds/{installers.py → dev/installers.py} +0 -0
  437. /chia/cmds/{sim.py → dev/sim.py} +0 -0
  438. /chia/{util → cmds}/dump_keyring.py +0 -0
  439. /chia/{full_node → consensus}/signage_point.py +0 -0
  440. /chia/{rpc → data_layer}/data_layer_rpc_client.py +0 -0
  441. /chia/{rpc → farmer}/farmer_rpc_api.py +0 -0
  442. /chia/{util → full_node}/full_block_utils.py +0 -0
  443. /chia/{rpc → harvester}/harvester_rpc_api.py +0 -0
  444. /chia/{rpc → harvester}/harvester_rpc_client.py +0 -0
  445. /chia/{full_node → protocols}/fee_estimate.py +0 -0
  446. /chia/{server → protocols}/outbound_message.py +0 -0
  447. /chia/{rpc → seeder}/crawler_rpc_api.py +0 -0
  448. /chia/{util → simulator}/vdf_prover.py +0 -0
  449. /chia/{util → ssl}/ssl_check.py +0 -0
  450. /chia/{rpc → timelord}/timelord_rpc_api.py +0 -0
  451. {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc2.dist-info}/LICENSE +0 -0
  452. {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc2.dist-info}/WHEEL +0 -0
  453. {chia_blockchain-2.5.4rc2.dist-info → chia_blockchain-2.5.5rc2.dist-info}/entry_points.txt +0 -0
@@ -5,21 +5,19 @@ import logging
5
5
  import sqlite3
6
6
  import time
7
7
  from collections.abc import Collection
8
- from typing import Any, Optional
8
+ from typing import Any, ClassVar, Optional
9
9
 
10
10
  import typing_extensions
11
11
  from aiosqlite import Cursor
12
+ from chia_rs import CoinState
12
13
  from chia_rs.sized_bytes import bytes32
13
14
  from chia_rs.sized_ints import uint32, uint64
14
- from clvm.casts import int_from_bytes
15
15
 
16
- from chia.protocols.wallet_protocol import CoinState
17
16
  from chia.types.blockchain_format.coin import Coin
18
17
  from chia.types.coin_record import CoinRecord
19
- from chia.types.eligible_coin_spends import UnspentLineageInfo
18
+ from chia.types.mempool_item import UnspentLineageInfo
20
19
  from chia.util.batches import to_batches
21
20
  from chia.util.db_wrapper import SQLITE_MAX_VARIABLE_NUMBER, DBWrapper2
22
- from chia.util.lru_cache import LRUCache
23
21
 
24
22
  log = logging.getLogger(__name__)
25
23
 
@@ -32,13 +30,12 @@ class CoinStore:
32
30
  """
33
31
 
34
32
  db_wrapper: DBWrapper2
35
- coins_added_at_height_cache: LRUCache[uint32, list[CoinRecord]]
36
33
 
37
34
  @classmethod
38
35
  async def create(cls, db_wrapper: DBWrapper2) -> CoinStore:
39
36
  if db_wrapper.db_version != 2:
40
37
  raise RuntimeError(f"CoinStore does not support database schema v{db_wrapper.db_version}")
41
- self = CoinStore(db_wrapper, LRUCache(100))
38
+ self = CoinStore(db_wrapper)
42
39
 
43
40
  async with self.db_wrapper.writer_maybe_transaction() as conn:
44
41
  log.info("DB: Creating coin store tables and indexes.")
@@ -69,11 +66,28 @@ class CoinStore:
69
66
  log.info("DB: Creating index coin_parent_index")
70
67
  await conn.execute("CREATE INDEX IF NOT EXISTS coin_parent_index on coin_record(coin_parent)")
71
68
 
69
+ async with conn.execute("SELECT 1 FROM coin_record LIMIT 1") as cursor:
70
+ is_new_db = await cursor.fetchone() is None
71
+ if is_new_db:
72
+ log.info("DB: Creating index coin_record_ph_ff_unspent_idx")
73
+ # This partial index optimizes fast forward singleton latest
74
+ # unspent queries. We're only adding it to new DBs to avoid
75
+ # complex migrations that affect the huge coin records table.
76
+ # The performance benefit outweighs the cost of this partial
77
+ # index as it only includes rows where spent_index is -1.
78
+ await conn.execute(
79
+ """
80
+ CREATE INDEX IF NOT EXISTS coin_record_ph_ff_unspent_idx
81
+ ON coin_record(puzzle_hash, spent_index)
82
+ WHERE spent_index = -1
83
+ """
84
+ )
85
+
72
86
  return self
73
87
 
74
88
  async def num_unspent(self) -> int:
75
89
  async with self.db_wrapper.reader_no_transaction() as conn:
76
- async with conn.execute("SELECT COUNT(*) FROM coin_record WHERE spent_index=0") as cursor:
90
+ async with conn.execute("SELECT COUNT(*) FROM coin_record WHERE spent_index <= 0") as cursor:
77
91
  row = await cursor.fetchone()
78
92
  if row is not None:
79
93
  count: int = row[0]
@@ -85,27 +99,33 @@ class CoinStore:
85
99
  height: uint32,
86
100
  timestamp: uint64,
87
101
  included_reward_coins: Collection[Coin],
88
- tx_additions: Collection[Coin],
102
+ tx_additions: Collection[tuple[bytes32, Coin, bool]],
89
103
  tx_removals: list[bytes32],
90
- ) -> list[CoinRecord]:
104
+ ) -> None:
91
105
  """
92
106
  Only called for blocks which are blocks (and thus have rewards and transactions)
93
- Returns a list of the CoinRecords that were added by this block
94
107
  """
95
108
 
96
109
  start = time.monotonic()
97
110
 
98
- additions = []
111
+ db_values_to_insert = []
99
112
 
100
- for coin in tx_additions:
101
- record: CoinRecord = CoinRecord(
102
- coin,
103
- height,
104
- uint32(0),
105
- False,
106
- timestamp,
113
+ for coin_id, coin, same_as_parent in tx_additions:
114
+ db_values_to_insert.append(
115
+ (
116
+ coin_id,
117
+ # confirmed_index
118
+ height,
119
+ # spent_index
120
+ -1 if same_as_parent else 0,
121
+ # coinbase
122
+ 0,
123
+ coin.puzzle_hash,
124
+ coin.parent_coin_info,
125
+ coin.amount.stream_to_bytes(),
126
+ timestamp,
127
+ )
107
128
  )
108
- additions.append(record)
109
129
 
110
130
  if height == 0:
111
131
  assert len(included_reward_coins) == 0
@@ -113,16 +133,24 @@ class CoinStore:
113
133
  assert len(included_reward_coins) >= 2
114
134
 
115
135
  for coin in included_reward_coins:
116
- reward_coin_r: CoinRecord = CoinRecord(
117
- coin,
118
- height,
119
- uint32(0),
120
- True,
121
- timestamp,
136
+ db_values_to_insert.append(
137
+ (
138
+ coin.name(),
139
+ # confirmed_index
140
+ height,
141
+ # spent_index
142
+ 0,
143
+ # coinbase
144
+ 1,
145
+ coin.puzzle_hash,
146
+ coin.parent_coin_info,
147
+ coin.amount.stream_to_bytes(),
148
+ timestamp,
149
+ )
122
150
  )
123
- additions.append(reward_coin_r)
124
151
 
125
- await self._add_coin_records(additions)
152
+ async with self.db_wrapper.writer_maybe_transaction() as conn:
153
+ await conn.executemany("INSERT INTO coin_record VALUES(?, ?, ?, ?, ?, ?, ?, ?)", db_values_to_insert)
126
154
  await self._set_spent(tx_removals, height)
127
155
 
128
156
  end = time.monotonic()
@@ -133,8 +161,6 @@ class CoinStore:
133
161
  + "blockchain database is on a fast drive",
134
162
  )
135
163
 
136
- return additions
137
-
138
164
  # Checks DB and DiffStores for CoinRecord with coin_name and returns it
139
165
  async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]:
140
166
  async with self.db_wrapper.reader_no_transaction() as conn:
@@ -146,7 +172,8 @@ class CoinStore:
146
172
  row = await cursor.fetchone()
147
173
  if row is not None:
148
174
  coin = self.row_to_coin(row)
149
- return CoinRecord(coin, row[0], row[1], row[2], row[6])
175
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
176
+ return CoinRecord(coin, row[0], spent_index, row[2], row[6])
150
177
  return None
151
178
 
152
179
  async def get_coin_records(self, names: Collection[bytes32]) -> list[CoinRecord]:
@@ -171,16 +198,13 @@ class CoinStore:
171
198
  for cursor in cursors:
172
199
  for row in await cursor.fetchall():
173
200
  coin = self.row_to_coin(row)
174
- record = CoinRecord(coin, row[0], row[1], row[2], row[6])
201
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
202
+ record = CoinRecord(coin, row[0], spent_index, row[2], row[6])
175
203
  coins.append(record)
176
204
 
177
205
  return coins
178
206
 
179
207
  async def get_coins_added_at_height(self, height: uint32) -> list[CoinRecord]:
180
- coins_added: Optional[list[CoinRecord]] = self.coins_added_at_height_cache.get(height)
181
- if coins_added is not None:
182
- return coins_added
183
-
184
208
  async with self.db_wrapper.reader_no_transaction() as conn:
185
209
  async with conn.execute(
186
210
  "SELECT confirmed_index, spent_index, coinbase, puzzle_hash, "
@@ -191,8 +215,8 @@ class CoinStore:
191
215
  coins = []
192
216
  for row in rows:
193
217
  coin = self.row_to_coin(row)
194
- coins.append(CoinRecord(coin, row[0], row[1], row[2], row[6]))
195
- self.coins_added_at_height_cache.put(height, coins)
218
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
219
+ coins.append(CoinRecord(coin, row[0], spent_index, row[2], row[6]))
196
220
  return coins
197
221
 
198
222
  async def get_coins_removed_at_height(self, height: uint32) -> list[CoinRecord]:
@@ -207,29 +231,12 @@ class CoinStore:
207
231
  ) as cursor:
208
232
  coins = []
209
233
  for row in await cursor.fetchall():
210
- if row[1] != 0:
234
+ if row[1] > 0:
211
235
  coin = self.row_to_coin(row)
212
236
  coin_record = CoinRecord(coin, row[0], row[1], row[2], row[6])
213
237
  coins.append(coin_record)
214
238
  return coins
215
239
 
216
- async def get_all_coins(self, include_spent_coins: bool) -> list[CoinRecord]:
217
- # WARNING: this should only be used for testing or in a simulation,
218
- # running it on a synced testnet or mainnet node will most likely result in an OOM error.
219
- coins = set()
220
-
221
- async with self.db_wrapper.reader_no_transaction() as conn:
222
- async with conn.execute(
223
- f"SELECT confirmed_index, spent_index, coinbase, puzzle_hash, "
224
- f"coin_parent, amount, timestamp FROM coin_record "
225
- f"{'' if include_spent_coins else 'INDEXED BY coin_spent_index WHERE spent_index=0'}"
226
- f" ORDER BY confirmed_index"
227
- ) as cursor:
228
- for row in await cursor.fetchall():
229
- coin = self.row_to_coin(row)
230
- coins.add(CoinRecord(coin, row[0], row[1], row[2], row[6]))
231
- return list(coins)
232
-
233
240
  # Checks DB and DiffStores for CoinRecords with puzzle_hash and returns them
234
241
  async def get_coin_records_by_puzzle_hash(
235
242
  self,
@@ -245,12 +252,13 @@ class CoinStore:
245
252
  f"SELECT confirmed_index, spent_index, coinbase, puzzle_hash, "
246
253
  f"coin_parent, amount, timestamp FROM coin_record INDEXED BY coin_puzzle_hash WHERE puzzle_hash=? "
247
254
  f"AND confirmed_index>=? AND confirmed_index<? "
248
- f"{'' if include_spent_coins else 'AND spent_index=0'}",
255
+ f"{'' if include_spent_coins else 'AND spent_index <= 0'}",
249
256
  (puzzle_hash, start_height, end_height),
250
257
  ) as cursor:
251
258
  for row in await cursor.fetchall():
252
259
  coin = self.row_to_coin(row)
253
- coins.add(CoinRecord(coin, row[0], row[1], row[2], row[6]))
260
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
261
+ coins.add(CoinRecord(coin, row[0], spent_index, row[2], row[6]))
254
262
  return list(coins)
255
263
 
256
264
  async def get_coin_records_by_puzzle_hashes(
@@ -273,12 +281,13 @@ class CoinStore:
273
281
  f"coin_parent, amount, timestamp FROM coin_record INDEXED BY coin_puzzle_hash "
274
282
  f"WHERE puzzle_hash in ({'?,' * (len(puzzle_hashes) - 1)}?) "
275
283
  f"AND confirmed_index>=? AND confirmed_index<? "
276
- f"{'' if include_spent_coins else 'AND spent_index=0'}",
284
+ f"{'' if include_spent_coins else 'AND spent_index <= 0'}",
277
285
  (*puzzle_hashes_db, start_height, end_height),
278
286
  ) as cursor:
279
287
  for row in await cursor.fetchall():
280
288
  coin = self.row_to_coin(row)
281
- coins.add(CoinRecord(coin, row[0], row[1], row[2], row[6]))
289
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
290
+ coins.add(CoinRecord(coin, row[0], spent_index, row[2], row[6]))
282
291
  return list(coins)
283
292
 
284
293
  async def get_coin_records_by_names(
@@ -299,12 +308,13 @@ class CoinStore:
299
308
  f"coin_parent, amount, timestamp FROM coin_record INDEXED BY sqlite_autoindex_coin_record_1 "
300
309
  f"WHERE coin_name in ({'?,' * (len(names) - 1)}?) "
301
310
  f"AND confirmed_index>=? AND confirmed_index<? "
302
- f"{'' if include_spent_coins else 'AND spent_index=0'}",
311
+ f"{'' if include_spent_coins else 'AND spent_index <= 0'}",
303
312
  [*names, start_height, end_height],
304
313
  ) as cursor:
305
314
  for row in await cursor.fetchall():
306
315
  coin = self.row_to_coin(row)
307
- coins.add(CoinRecord(coin, row[0], row[1], row[2], row[6]))
316
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
317
+ coins.add(CoinRecord(coin, row[0], spent_index, row[2], row[6]))
308
318
 
309
319
  return list(coins)
310
320
 
@@ -314,7 +324,7 @@ class CoinStore:
314
324
  def row_to_coin_state(self, row: sqlite3.Row) -> CoinState:
315
325
  coin = self.row_to_coin(row)
316
326
  spent_h = None
317
- if row[1] != 0:
327
+ if row[1] > 0:
318
328
  spent_h = row[1]
319
329
  return CoinState(coin, spent_h, row[0])
320
330
 
@@ -338,7 +348,7 @@ class CoinStore:
338
348
  f"coin_parent, amount, timestamp FROM coin_record INDEXED BY coin_puzzle_hash "
339
349
  f"WHERE puzzle_hash in ({'?,' * (len(batch.entries) - 1)}?) "
340
350
  f"AND (confirmed_index>=? OR spent_index>=?)"
341
- f"{'' if include_spent_coins else 'AND spent_index=0'}"
351
+ f"{'' if include_spent_coins else ' AND spent_index <= 0'}"
342
352
  " LIMIT ?",
343
353
  (*puzzle_hashes_db, min_height, min_height, max_items - len(coins)),
344
354
  ) as cursor:
@@ -369,12 +379,13 @@ class CoinStore:
369
379
  f"SELECT confirmed_index, spent_index, coinbase, puzzle_hash, coin_parent, amount, timestamp "
370
380
  f"FROM coin_record WHERE coin_parent in ({'?,' * (len(batch.entries) - 1)}?) "
371
381
  f"AND confirmed_index>=? AND confirmed_index<? "
372
- f"{'' if include_spent_coins else 'AND spent_index=0'}",
382
+ f"{'' if include_spent_coins else 'AND spent_index <= 0'}",
373
383
  (*parent_ids_db, start_height, end_height),
374
384
  ) as cursor:
375
385
  async for row in cursor:
376
386
  coin = self.row_to_coin(row)
377
- coins.add(CoinRecord(coin, row[0], row[1], row[2], row[6]))
387
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
388
+ coins.add(CoinRecord(coin, row[0], spent_index, row[2], row[6]))
378
389
 
379
390
  return list(coins)
380
391
 
@@ -403,7 +414,7 @@ class CoinStore:
403
414
  f"SELECT confirmed_index, spent_index, coinbase, puzzle_hash, coin_parent, amount, timestamp "
404
415
  f"FROM coin_record WHERE coin_name in ({'?,' * (len(batch.entries) - 1)}?) "
405
416
  f"AND (confirmed_index>=? OR spent_index>=?) {max_height_sql}"
406
- f"{'' if include_spent_coins else 'AND spent_index=0'}"
417
+ f"{'' if include_spent_coins else 'AND spent_index <= 0'}"
407
418
  " LIMIT ?",
408
419
  (*coin_ids_db, min_height, min_height, max_items - len(coins)),
409
420
  ) as cursor:
@@ -414,7 +425,7 @@ class CoinStore:
414
425
 
415
426
  return coins
416
427
 
417
- MAX_PUZZLE_HASH_BATCH_SIZE = SQLITE_MAX_VARIABLE_NUMBER - 10
428
+ MAX_PUZZLE_HASH_BATCH_SIZE: ClassVar[int] = SQLITE_MAX_VARIABLE_NUMBER - 10
418
429
 
419
430
  async def batch_coin_states_by_puzzle_hashes(
420
431
  self,
@@ -448,7 +459,7 @@ class CoinStore:
448
459
  puzzle_hash_count = len(puzzle_hashes_db)
449
460
 
450
461
  require_spent = "spent_index>0"
451
- require_unspent = "spent_index=0"
462
+ require_unspent = "spent_index <= 0"
452
463
  amount_filter = "AND amount>=? " if min_amount > 0 else ""
453
464
 
454
465
  if include_spent and include_unspent:
@@ -531,66 +542,70 @@ class CoinStore:
531
542
 
532
543
  return coin_states, next_height
533
544
 
534
- async def rollback_to_block(self, block_index: int) -> list[CoinRecord]:
545
+ async def rollback_to_block(self, block_index: int) -> dict[bytes32, CoinRecord]:
535
546
  """
536
547
  Note that block_index can be negative, in which case everything is rolled back
537
- Returns the list of coin records that have been modified
548
+ Returns a map of coin ID to coin record for modified items.
538
549
  """
539
550
 
540
551
  coin_changes: dict[bytes32, CoinRecord] = {}
541
552
  # Add coins that are confirmed in the reverted blocks to the list of updated coins.
542
553
  async with self.db_wrapper.writer_maybe_transaction() as conn:
543
- async with conn.execute(
554
+ rows = await conn.execute_fetchall(
544
555
  "SELECT confirmed_index, spent_index, coinbase, puzzle_hash, "
545
- "coin_parent, amount, timestamp FROM coin_record WHERE confirmed_index>?",
556
+ "coin_parent, amount, timestamp, coin_name FROM coin_record WHERE confirmed_index>?",
546
557
  (block_index,),
547
- ) as cursor:
548
- for row in await cursor.fetchall():
549
- coin = self.row_to_coin(row)
550
- record = CoinRecord(coin, uint32(0), row[1], row[2], uint64(0))
551
- coin_changes[record.name] = record
558
+ )
559
+ for row in rows:
560
+ coin = self.row_to_coin(row)
561
+ spent_index = uint32(0) if row[1] <= 0 else uint32(row[1])
562
+ record = CoinRecord(coin, uint32(0), spent_index, row[2], uint64(0))
563
+ coin_name = bytes32(row[7])
564
+ coin_changes[coin_name] = record
552
565
 
553
566
  # Delete reverted blocks from storage
554
567
  await conn.execute("DELETE FROM coin_record WHERE confirmed_index>?", (block_index,))
555
568
 
556
569
  # Add coins that are confirmed in the reverted blocks to the list of changed coins.
557
- async with conn.execute(
570
+ rows = await conn.execute_fetchall(
558
571
  "SELECT confirmed_index, spent_index, coinbase, puzzle_hash, "
559
- "coin_parent, amount, timestamp FROM coin_record WHERE spent_index>?",
572
+ "coin_parent, amount, timestamp, coin_name FROM coin_record WHERE spent_index>?",
560
573
  (block_index,),
561
- ) as cursor:
562
- for row in await cursor.fetchall():
563
- coin = self.row_to_coin(row)
564
- record = CoinRecord(coin, row[0], uint32(0), row[2], row[6])
565
- if record.name not in coin_changes:
566
- coin_changes[record.name] = record
567
-
568
- await conn.execute("UPDATE coin_record SET spent_index=0 WHERE spent_index>?", (block_index,))
569
- self.coins_added_at_height_cache = LRUCache(self.coins_added_at_height_cache.capacity)
570
- return list(coin_changes.values())
571
-
572
- # Store CoinRecord in DB
573
- async def _add_coin_records(self, records: list[CoinRecord]) -> None:
574
- values2 = []
575
- for record in records:
576
- values2.append(
577
- (
578
- record.coin.name(),
579
- record.confirmed_block_index,
580
- record.spent_block_index,
581
- int(record.coinbase),
582
- record.coin.puzzle_hash,
583
- record.coin.parent_coin_info,
584
- uint64(record.coin.amount).stream_to_bytes(),
585
- record.timestamp,
586
- )
587
574
  )
588
- if len(values2) > 0:
589
- async with self.db_wrapper.writer_maybe_transaction() as conn:
590
- await conn.executemany(
591
- "INSERT INTO coin_record VALUES(?, ?, ?, ?, ?, ?, ?, ?)",
592
- values2,
593
- )
575
+ for row in rows:
576
+ coin = self.row_to_coin(row)
577
+ record = CoinRecord(coin, row[0], uint32(0), row[2], row[6])
578
+ coin_name = bytes32(row[7])
579
+ if coin_name not in coin_changes:
580
+ coin_changes[coin_name] = record
581
+
582
+ # If the coin to update is not a reward coin and its parent is
583
+ # spent and has the same puzzle hash and amount, we set its
584
+ # spent_index to -1 as a potential fast forward singleton unspent
585
+ # otherwise we set it to 0 as a normal unspent.
586
+ await conn.execute(
587
+ """
588
+ UPDATE coin_record INDEXED BY coin_spent_index
589
+ SET spent_index = CASE
590
+ WHEN
591
+ coinbase = 0 AND
592
+ EXISTS (
593
+ SELECT 1
594
+ FROM coin_record AS parent INDEXED BY sqlite_autoindex_coin_record_1
595
+ WHERE
596
+ parent.coin_name = coin_record.coin_parent AND
597
+ parent.puzzle_hash = coin_record.puzzle_hash AND
598
+ parent.amount = coin_record.amount AND
599
+ parent.spent_index > 0
600
+ )
601
+ THEN -1
602
+ ELSE 0
603
+ END
604
+ WHERE spent_index > ?
605
+ """,
606
+ (block_index,),
607
+ )
608
+ return coin_changes
594
609
 
595
610
  # Update coin_record to be spent in DB
596
611
  async def _set_spent(self, coin_names: list[bytes32], index: uint32) -> None:
@@ -606,7 +621,7 @@ class CoinStore:
606
621
  ret: Cursor = await conn.execute(
607
622
  f"UPDATE coin_record INDEXED BY sqlite_autoindex_coin_record_1 "
608
623
  f"SET spent_index={index} "
609
- f"WHERE spent_index=0 "
624
+ f"WHERE spent_index <= 0 "
610
625
  f"AND coin_name IN ({name_params})",
611
626
  batch.entries,
612
627
  )
@@ -621,27 +636,31 @@ class CoinStore:
621
636
  async with self.db_wrapper.reader_no_transaction() as conn:
622
637
  async with conn.execute(
623
638
  "SELECT unspent.coin_name, "
624
- "unspent.amount, "
625
639
  "unspent.coin_parent, "
626
- "parent.amount, "
627
640
  "parent.coin_parent "
628
- "FROM coin_record AS unspent INDEXED BY coin_puzzle_hash "
641
+ "FROM coin_record AS unspent "
629
642
  "LEFT JOIN coin_record AS parent ON unspent.coin_parent = parent.coin_name "
630
- "WHERE unspent.spent_index = 0 "
643
+ "WHERE unspent.spent_index = -1 "
631
644
  "AND parent.spent_index > 0 "
632
645
  "AND unspent.puzzle_hash = ? "
633
- "AND parent.puzzle_hash = unspent.puzzle_hash",
646
+ "AND parent.puzzle_hash = unspent.puzzle_hash "
647
+ "AND parent.amount = unspent.amount",
634
648
  (puzzle_hash,),
635
649
  ) as cursor:
636
650
  rows = list(await cursor.fetchall())
637
651
  if len(rows) != 1:
638
652
  log.debug("Expected 1 unspent with puzzle hash %s, but found %s", puzzle_hash.hex(), len(rows))
639
653
  return None
640
- coin_id, coin_amount, parent_id, parent_amount, parent_parent_id = rows[0]
654
+ coin_id, parent_id, parent_parent_id = rows[0]
641
655
  return UnspentLineageInfo(
642
- coin_id=bytes32(coin_id),
643
- coin_amount=uint64(int_from_bytes(coin_amount)),
644
- parent_id=bytes32(parent_id),
645
- parent_amount=uint64(int_from_bytes(parent_amount)),
646
- parent_parent_id=bytes32(parent_parent_id),
656
+ coin_id=bytes32(coin_id), parent_id=bytes32(parent_id), parent_parent_id=bytes32(parent_parent_id)
647
657
  )
658
+
659
+ async def is_empty(self) -> bool:
660
+ """
661
+ Returns True if the coin store is empty, False otherwise.
662
+ """
663
+ async with self.db_wrapper.reader_no_transaction() as conn:
664
+ async with conn.execute("SELECT coin_name FROM coin_record LIMIT 1") as cursor:
665
+ row = await cursor.fetchone()
666
+ return row is None or len(row) == 0