chia-blockchain 2.5.2rc2__py3-none-any.whl → 2.5.3__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 (640) hide show
  1. chia/__init__.py +7 -0
  2. chia/_tests/blockchain/blockchain_test_utils.py +1 -1
  3. chia/_tests/blockchain/test_augmented_chain.py +54 -5
  4. chia/_tests/blockchain/test_blockchain.py +5 -12
  5. chia/_tests/blockchain/test_blockchain_transactions.py +3 -5
  6. chia/_tests/blockchain/test_get_block_generator.py +2 -2
  7. chia/_tests/blockchain/test_lookup_fork_chain.py +2 -2
  8. chia/_tests/clvm/benchmark_costs.py +2 -1
  9. chia/_tests/clvm/coin_store.py +4 -3
  10. chia/_tests/clvm/test_chialisp_deserialization.py +2 -2
  11. chia/_tests/clvm/test_curry_and_treehash.py +1 -1
  12. chia/_tests/clvm/test_puzzle_compression.py +2 -2
  13. chia/_tests/clvm/test_puzzles.py +2 -2
  14. chia/_tests/clvm/test_singletons.py +2 -2
  15. chia/_tests/clvm/test_spend_sim.py +1 -1
  16. chia/_tests/cmds/cmd_test_utils.py +2 -2
  17. chia/_tests/cmds/test_click_types.py +2 -2
  18. chia/_tests/cmds/test_cmd_framework.py +6 -6
  19. chia/_tests/cmds/test_show.py +4 -3
  20. chia/_tests/cmds/test_tx_config_args.py +1 -1
  21. chia/_tests/cmds/testing_classes.py +2 -2
  22. chia/_tests/cmds/wallet/test_consts.py +2 -2
  23. chia/_tests/cmds/wallet/test_did.py +2 -2
  24. chia/_tests/cmds/wallet/test_nft.py +2 -2
  25. chia/_tests/cmds/wallet/test_notifications.py +3 -2
  26. chia/_tests/cmds/wallet/test_vcs.py +2 -2
  27. chia/_tests/cmds/wallet/test_wallet.py +4 -8
  28. chia/_tests/conftest.py +4 -3
  29. chia/_tests/connection_utils.py +2 -2
  30. chia/_tests/core/cmds/test_keys.py +1 -2
  31. chia/_tests/core/cmds/test_wallet.py +2 -2
  32. chia/_tests/core/consensus/test_block_creation.py +2 -2
  33. chia/_tests/core/consensus/test_pot_iterations.py +1 -1
  34. chia/_tests/core/custom_types/test_coin.py +2 -2
  35. chia/_tests/core/custom_types/test_proof_of_space.py +2 -2
  36. chia/_tests/core/custom_types/test_spend_bundle.py +2 -2
  37. chia/_tests/core/data_layer/conftest.py +1 -1
  38. chia/_tests/core/data_layer/test_data_layer.py +1 -1
  39. chia/_tests/core/data_layer/test_data_layer_util.py +1 -1
  40. chia/_tests/core/data_layer/test_data_rpc.py +2 -2
  41. chia/_tests/core/data_layer/test_data_store.py +1 -1
  42. chia/_tests/core/data_layer/test_data_store_schema.py +1 -1
  43. chia/_tests/core/data_layer/util.py +2 -1
  44. chia/_tests/core/farmer/test_farmer_api.py +1 -1
  45. chia/_tests/core/full_node/full_sync/test_full_sync.py +1 -7
  46. chia/_tests/core/full_node/ram_db.py +2 -1
  47. chia/_tests/core/full_node/stores/test_block_store.py +2 -2
  48. chia/_tests/core/full_node/stores/test_coin_store.py +2 -2
  49. chia/_tests/core/full_node/stores/test_full_node_store.py +3 -3
  50. chia/_tests/core/full_node/stores/test_hint_store.py +2 -2
  51. chia/_tests/core/full_node/stores/test_sync_store.py +1 -1
  52. chia/_tests/core/full_node/test_address_manager.py +1 -1
  53. chia/_tests/core/full_node/test_block_height_map.py +2 -2
  54. chia/_tests/core/full_node/test_conditions.py +1 -1
  55. chia/_tests/core/full_node/test_full_node.py +346 -164
  56. chia/_tests/core/full_node/test_generator_tools.py +3 -2
  57. chia/_tests/core/full_node/test_hint_management.py +2 -2
  58. chia/_tests/core/full_node/test_performance.py +2 -15
  59. chia/_tests/core/full_node/test_subscriptions.py +1 -1
  60. chia/_tests/core/full_node/test_transactions.py +186 -185
  61. chia/_tests/core/full_node/test_tx_processing_queue.py +1 -1
  62. chia/_tests/core/make_block_generator.py +2 -2
  63. chia/_tests/core/mempool/test_mempool.py +165 -22
  64. chia/_tests/core/mempool/test_mempool_fee_estimator.py +1 -1
  65. chia/_tests/core/mempool/test_mempool_fee_protocol.py +1 -1
  66. chia/_tests/core/mempool/test_mempool_manager.py +476 -66
  67. chia/_tests/core/mempool/test_mempool_performance.py +2 -2
  68. chia/_tests/core/mempool/test_singleton_fast_forward.py +19 -25
  69. chia/_tests/core/node_height.py +2 -1
  70. chia/_tests/core/server/test_capabilities.py +1 -1
  71. chia/_tests/core/server/test_dos.py +36 -28
  72. chia/_tests/core/server/test_loop.py +3 -3
  73. chia/_tests/core/server/test_rate_limits.py +1 -1
  74. chia/_tests/core/server/test_server.py +2 -2
  75. chia/_tests/core/services/test_services.py +1 -1
  76. chia/_tests/core/ssl/test_ssl.py +1 -1
  77. chia/_tests/core/test_coins.py +2 -1
  78. chia/_tests/core/test_cost_calculation.py +2 -2
  79. chia/_tests/core/test_crawler.py +2 -2
  80. chia/_tests/core/test_db_conversion.py +2 -2
  81. chia/_tests/core/test_db_validation.py +26 -13
  82. chia/_tests/core/test_farmer_harvester_rpc.py +2 -2
  83. chia/_tests/core/test_full_node_rpc.py +2 -2
  84. chia/_tests/core/test_merkle_set.py +2 -2
  85. chia/_tests/core/test_program.py +2 -2
  86. chia/_tests/core/test_rpc_util.py +1 -1
  87. chia/_tests/core/test_seeder.py +1 -1
  88. chia/_tests/core/util/test_block_cache.py +3 -3
  89. chia/_tests/core/util/test_jsonify.py +3 -2
  90. chia/_tests/core/util/test_keychain.py +3 -3
  91. chia/_tests/core/util/test_streamable.py +3 -4
  92. chia/_tests/environments/wallet.py +3 -2
  93. chia/_tests/farmer_harvester/test_farmer.py +3 -4
  94. chia/_tests/farmer_harvester/test_farmer_harvester.py +2 -2
  95. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +2 -2
  96. chia/_tests/farmer_harvester/test_third_party_harvesters.py +3 -4
  97. chia/_tests/fee_estimation/test_fee_estimation_integration.py +1 -1
  98. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +2 -2
  99. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +1 -1
  100. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +3 -4
  101. chia/_tests/generator/test_compression.py +20 -10
  102. chia/_tests/generator/test_rom.py +7 -9
  103. chia/_tests/plot_sync/test_delta.py +2 -2
  104. chia/_tests/plot_sync/test_plot_sync.py +2 -2
  105. chia/_tests/plot_sync/test_receiver.py +2 -2
  106. chia/_tests/plot_sync/test_sender.py +2 -2
  107. chia/_tests/plot_sync/test_sync_simulated.py +2 -2
  108. chia/_tests/plot_sync/util.py +3 -2
  109. chia/_tests/plotting/test_plot_manager.py +1 -1
  110. chia/_tests/pools/test_pool_cli_parsing.py +3 -2
  111. chia/_tests/pools/test_pool_cmdline.py +2 -2
  112. chia/_tests/pools/test_pool_puzzles_lifecycle.py +3 -3
  113. chia/_tests/pools/test_pool_rpc.py +4 -5
  114. chia/_tests/pools/test_pool_wallet.py +1 -1
  115. chia/_tests/pools/test_wallet_pool_store.py +2 -2
  116. chia/_tests/rpc/test_rpc_client.py +1 -1
  117. chia/_tests/rpc/test_rpc_server.py +1 -1
  118. chia/_tests/simulation/test_simulation.py +36 -8
  119. chia/_tests/simulation/test_simulator.py +5 -5
  120. chia/_tests/simulation/test_start_simulator.py +2 -2
  121. chia/_tests/timelord/test_new_peak.py +2 -2
  122. chia/_tests/tools/test_run_block.py +3 -2
  123. chia/_tests/util/benchmark_cost.py +2 -2
  124. chia/_tests/util/benchmarks.py +17 -6
  125. chia/_tests/util/blockchain.py +2 -1
  126. chia/_tests/util/blockchain_mock.py +9 -5
  127. chia/_tests/util/build_network_protocol_files.py +2 -1
  128. chia/_tests/util/constants.py +2 -1
  129. chia/_tests/util/full_sync.py +6 -3
  130. chia/_tests/util/gen_ssl_certs.py +2 -2
  131. chia/_tests/util/generator_tools_testing.py +4 -3
  132. chia/_tests/util/get_name_puzzle_conditions.py +2 -2
  133. chia/_tests/util/misc.py +16 -2
  134. chia/_tests/util/network_protocol_data.py +17 -7
  135. chia/_tests/util/run_block.py +6 -8
  136. chia/_tests/util/setup_nodes.py +4 -3
  137. chia/_tests/util/spend_sim.py +9 -5
  138. chia/_tests/util/test_condition_tools.py +2 -2
  139. chia/_tests/util/test_config.py +2 -1
  140. chia/_tests/util/test_errors.py +2 -1
  141. chia/_tests/util/test_full_block_utils.py +17 -7
  142. chia/_tests/util/test_misc.py +1 -1
  143. chia/_tests/util/test_network_protocol_test.py +24 -24
  144. chia/_tests/util/test_replace_str_to_bytes.py +2 -2
  145. chia/_tests/util/test_trusted_peer.py +1 -1
  146. chia/_tests/util/time_out_assert.py +20 -7
  147. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +1 -1
  148. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +2 -2
  149. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +5 -6
  150. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +17 -15
  151. chia/_tests/wallet/cat_wallet/test_trades.py +2 -2
  152. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +2 -2
  153. chia/_tests/wallet/clawback/test_clawback_metadata.py +2 -2
  154. chia/_tests/wallet/conftest.py +3 -3
  155. chia/_tests/wallet/db_wallet/test_db_graftroot.py +3 -5
  156. chia/_tests/wallet/db_wallet/test_dl_offers.py +2 -2
  157. chia/_tests/wallet/db_wallet/test_dl_wallet.py +433 -384
  158. chia/_tests/wallet/did_wallet/test_did.py +3 -3
  159. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +2 -2
  160. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +2 -2
  161. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +3 -4
  162. chia/_tests/wallet/nft_wallet/test_nft_offers.py +1293 -703
  163. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +28 -30
  164. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2 -2
  165. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +2 -2
  166. chia/_tests/wallet/rpc/config.py +1 -1
  167. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +2 -2
  168. chia/_tests/wallet/rpc/test_wallet_rpc.py +20 -77
  169. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +9 -7
  170. chia/_tests/wallet/sync/test_wallet_sync.py +79 -31
  171. chia/_tests/wallet/test_clvm_streamable.py +2 -2
  172. chia/_tests/wallet/test_coin_management.py +7 -7
  173. chia/_tests/wallet/test_coin_selection.py +20 -2
  174. chia/_tests/wallet/test_conditions.py +2 -2
  175. chia/_tests/wallet/test_debug_spend_bundle.py +2 -2
  176. chia/_tests/wallet/test_new_wallet_protocol.py +2 -2
  177. chia/_tests/wallet/test_nft_store.py +2 -2
  178. chia/_tests/wallet/test_notifications.py +2 -2
  179. chia/_tests/wallet/test_puzzle_store.py +2 -2
  180. chia/_tests/wallet/test_sign_coin_spends.py +2 -2
  181. chia/_tests/wallet/test_signer_protocol.py +3 -3
  182. chia/_tests/wallet/test_singleton.py +3 -11
  183. chia/_tests/wallet/test_singleton_lifecycle_fast.py +12 -13
  184. chia/_tests/wallet/test_singleton_store.py +2 -4
  185. chia/_tests/wallet/test_transaction_store.py +2 -2
  186. chia/_tests/wallet/test_util.py +2 -2
  187. chia/_tests/wallet/test_wallet.py +53 -49
  188. chia/_tests/wallet/test_wallet_action_scope.py +24 -6
  189. chia/_tests/wallet/test_wallet_blockchain.py +1 -1
  190. chia/_tests/wallet/test_wallet_coin_store.py +2 -2
  191. chia/_tests/wallet/test_wallet_interested_store.py +2 -2
  192. chia/_tests/wallet/test_wallet_node.py +3 -3
  193. chia/_tests/wallet/test_wallet_retry.py +3 -3
  194. chia/_tests/wallet/test_wallet_state_manager.py +8 -8
  195. chia/_tests/wallet/test_wallet_test_framework.py +1 -1
  196. chia/_tests/wallet/test_wallet_trade_store.py +2 -2
  197. chia/_tests/wallet/test_wallet_utils.py +2 -2
  198. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +3 -2
  199. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +15 -15
  200. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +5 -3
  201. chia/_tests/wallet/wallet_block_tools.py +15 -7
  202. chia/_tests/weight_proof/test_weight_proof.py +3 -3
  203. chia/cmds/chia.py +0 -2
  204. chia/cmds/cmd_classes.py +3 -3
  205. chia/cmds/cmd_helpers.py +4 -4
  206. chia/cmds/cmds_util.py +2 -2
  207. chia/cmds/coin_funcs.py +3 -2
  208. chia/cmds/coins.py +1 -1
  209. chia/cmds/data.py +2 -2
  210. chia/cmds/data_funcs.py +3 -2
  211. chia/cmds/db_upgrade_func.py +2 -2
  212. chia/cmds/db_validate_func.py +15 -8
  213. chia/cmds/farm.py +2 -4
  214. chia/cmds/keys.py +0 -2
  215. chia/cmds/keys_funcs.py +1 -1
  216. chia/cmds/netspace_funcs.py +2 -1
  217. chia/cmds/param_types.py +2 -2
  218. chia/cmds/plotnft.py +2 -2
  219. chia/cmds/plotnft_funcs.py +2 -2
  220. chia/cmds/rpc.py +1 -1
  221. chia/cmds/show.py +1 -2
  222. chia/cmds/show_funcs.py +6 -3
  223. chia/cmds/signer.py +1 -2
  224. chia/cmds/sim.py +1 -2
  225. chia/cmds/sim_funcs.py +2 -2
  226. chia/cmds/wallet.py +2 -2
  227. chia/cmds/wallet_funcs.py +4 -11
  228. chia/consensus/block_body_validation.py +3 -4
  229. chia/consensus/block_creation.py +10 -6
  230. chia/consensus/block_header_validation.py +3 -4
  231. chia/consensus/block_record.py +2 -3
  232. chia/consensus/block_rewards.py +1 -1
  233. chia/consensus/blockchain.py +20 -17
  234. chia/consensus/blockchain_interface.py +5 -4
  235. chia/consensus/coinbase.py +2 -2
  236. chia/consensus/constants.py +1 -1
  237. chia/consensus/cost_calculator.py +2 -1
  238. chia/consensus/default_constants.py +4 -3
  239. chia/consensus/deficit.py +3 -2
  240. chia/consensus/difficulty_adjustment.py +8 -9
  241. chia/consensus/find_fork_point.py +4 -3
  242. chia/consensus/full_block_to_block_record.py +4 -3
  243. chia/consensus/get_block_challenge.py +4 -3
  244. chia/consensus/get_block_generator.py +3 -2
  245. chia/consensus/make_sub_epoch_summary.py +3 -2
  246. chia/consensus/multiprocess_validation.py +9 -4
  247. chia/consensus/pos_quality.py +1 -1
  248. chia/consensus/pot_iterations.py +4 -3
  249. chia/consensus/vdf_info_computation.py +4 -3
  250. chia/daemon/client.py +1 -1
  251. chia/daemon/keychain_server.py +1 -1
  252. chia/daemon/server.py +1 -1
  253. chia/daemon/windows_signal.py +1 -1
  254. chia/data_layer/data_layer.py +4 -3
  255. chia/data_layer/data_layer_errors.py +1 -1
  256. chia/data_layer/data_layer_util.py +2 -2
  257. chia/data_layer/data_layer_wallet.py +47 -69
  258. chia/data_layer/data_store.py +1 -1
  259. chia/data_layer/dl_wallet_store.py +5 -6
  260. chia/data_layer/download_data.py +1 -1
  261. chia/data_layer/s3_plugin_service.py +4 -4
  262. chia/data_layer/singleton_record.py +23 -0
  263. chia/data_layer/util/benchmark.py +2 -1
  264. chia/farmer/farmer.py +4 -6
  265. chia/farmer/farmer_api.py +4 -6
  266. chia/full_node/bitcoin_fee_estimator.py +2 -1
  267. chia/full_node/block_height_map.py +2 -2
  268. chia/full_node/block_store.py +8 -9
  269. chia/{util → full_node}/check_fork_next_block.py +2 -1
  270. chia/full_node/coin_store.py +10 -10
  271. chia/full_node/fee_estimate.py +2 -1
  272. chia/full_node/fee_estimation.py +2 -1
  273. chia/full_node/fee_estimator.py +2 -1
  274. chia/full_node/fee_estimator_interface.py +1 -1
  275. chia/full_node/fee_history.py +2 -1
  276. chia/full_node/fee_tracker.py +2 -1
  277. chia/full_node/full_node.py +15 -13
  278. chia/full_node/full_node_api.py +12 -32
  279. chia/full_node/full_node_store.py +4 -3
  280. chia/full_node/hint_management.py +2 -1
  281. chia/full_node/hint_store.py +3 -3
  282. chia/full_node/mempool.py +80 -12
  283. chia/full_node/mempool_check_conditions.py +6 -7
  284. chia/full_node/mempool_manager.py +168 -21
  285. chia/full_node/pending_tx_cache.py +2 -2
  286. chia/full_node/subscriptions.py +2 -2
  287. chia/full_node/sync_store.py +2 -3
  288. chia/full_node/tx_processing_queue.py +2 -1
  289. chia/full_node/weight_proof.py +5 -8
  290. chia/harvester/harvester.py +5 -3
  291. chia/harvester/harvester_api.py +2 -2
  292. chia/introducer/introducer.py +30 -2
  293. chia/introducer/introducer_api.py +9 -1
  294. chia/legacy/keyring.py +1 -2
  295. chia/plot_sync/exceptions.py +2 -1
  296. chia/plot_sync/receiver.py +2 -2
  297. chia/plot_sync/sender.py +1 -1
  298. chia/plotting/cache.py +2 -2
  299. chia/plotting/check_plots.py +4 -2
  300. chia/plotting/create_plots.py +1 -1
  301. chia/plotting/manager.py +3 -3
  302. chia/plotting/util.py +2 -2
  303. chia/pools/pool_config.py +1 -1
  304. chia/pools/pool_puzzles.py +23 -17
  305. chia/pools/pool_wallet.py +22 -9
  306. chia/pools/pool_wallet_info.py +2 -2
  307. chia/protocols/farmer_protocol.py +3 -6
  308. chia/protocols/full_node_protocol.py +3 -2
  309. chia/protocols/harvester_protocol.py +3 -4
  310. chia/protocols/pool_protocol.py +2 -2
  311. chia/protocols/shared_protocol.py +2 -1
  312. chia/protocols/timelord_protocol.py +4 -4
  313. chia/protocols/wallet_protocol.py +2 -2
  314. chia/rpc/data_layer_rpc_api.py +3 -4
  315. chia/rpc/data_layer_rpc_client.py +3 -2
  316. chia/rpc/farmer_rpc_api.py +2 -2
  317. chia/rpc/farmer_rpc_client.py +2 -1
  318. chia/rpc/full_node_rpc_api.py +3 -2
  319. chia/rpc/full_node_rpc_client.py +3 -2
  320. chia/rpc/harvester_rpc_api.py +2 -1
  321. chia/rpc/rpc_client.py +2 -2
  322. chia/rpc/rpc_server.py +1 -1
  323. chia/rpc/wallet_request_types.py +2 -62
  324. chia/rpc/wallet_rpc_api.py +98 -628
  325. chia/rpc/wallet_rpc_client.py +5 -253
  326. chia/seeder/crawl_store.py +1 -1
  327. chia/seeder/crawler.py +2 -2
  328. chia/seeder/peer_record.py +2 -1
  329. chia/seeder/start_crawler.py +3 -1
  330. chia/server/address_manager.py +2 -1
  331. chia/server/address_manager_store.py +1 -1
  332. chia/server/capabilities.py +2 -1
  333. chia/server/introducer_peers.py +2 -1
  334. chia/server/node_discovery.py +1 -1
  335. chia/server/outbound_message.py +2 -1
  336. chia/server/server.py +2 -2
  337. chia/server/start_data_layer.py +2 -1
  338. chia/server/start_farmer.py +3 -1
  339. chia/server/start_full_node.py +4 -2
  340. chia/server/start_harvester.py +3 -1
  341. chia/server/start_introducer.py +12 -1
  342. chia/server/start_service.py +2 -1
  343. chia/server/start_timelord.py +3 -1
  344. chia/server/start_wallet.py +3 -1
  345. chia/server/upnp.py +1 -2
  346. chia/server/ws_connection.py +3 -4
  347. chia/simulator/add_blocks_in_batches.py +5 -3
  348. chia/simulator/block_tools.py +16 -12
  349. chia/simulator/full_node_simulator.py +9 -14
  350. chia/simulator/setup_services.py +5 -3
  351. chia/simulator/simulator_full_node_rpc_api.py +3 -2
  352. chia/simulator/simulator_full_node_rpc_client.py +3 -2
  353. chia/simulator/simulator_protocol.py +3 -2
  354. chia/simulator/simulator_test_tools.py +2 -2
  355. chia/simulator/start_simulator.py +3 -2
  356. chia/simulator/wallet_tools.py +3 -4
  357. chia/timelord/iters_from_block.py +4 -4
  358. chia/timelord/timelord.py +7 -12
  359. chia/timelord/timelord_api.py +3 -3
  360. chia/timelord/timelord_state.py +4 -3
  361. chia/types/block_protocol.py +2 -2
  362. chia/types/blockchain_format/coin.py +2 -2
  363. chia/types/blockchain_format/program.py +1 -1
  364. chia/types/blockchain_format/proof_of_space.py +3 -4
  365. chia/types/blockchain_format/tree_hash.py +1 -1
  366. chia/types/blockchain_format/vdf.py +3 -4
  367. chia/types/clvm_cost.py +1 -1
  368. chia/types/coin_record.py +4 -3
  369. chia/types/coin_spend.py +1 -1
  370. chia/types/eligible_coin_spends.py +9 -5
  371. chia/types/fee_rate.py +1 -1
  372. chia/types/generator_types.py +3 -3
  373. chia/types/internal_mempool_item.py +3 -2
  374. chia/types/mempool_item.py +10 -3
  375. chia/types/mempool_submission_status.py +2 -1
  376. chia/types/mojos.py +1 -1
  377. chia/types/peer_info.py +2 -1
  378. chia/types/transaction_queue_entry.py +2 -1
  379. chia/types/unfinished_header_block.py +4 -4
  380. chia/types/validation_state.py +2 -1
  381. chia/types/weight_proof.py +1 -9
  382. chia/util/augmented_chain.py +20 -9
  383. chia/util/block_cache.py +8 -4
  384. chia/util/condition_tools.py +2 -2
  385. chia/util/full_block_utils.py +3 -4
  386. chia/util/generator_tools.py +2 -2
  387. chia/util/initial-config.yaml +2 -11
  388. chia/util/network.py +2 -2
  389. chia/util/prev_transaction_block.py +2 -1
  390. chia/util/task_timing.py +1 -1
  391. chia/util/vdf_prover.py +3 -3
  392. chia/util/ws_message.py +1 -1
  393. chia/wallet/cat_wallet/cat_info.py +3 -2
  394. chia/wallet/cat_wallet/cat_outer_puzzle.py +3 -2
  395. chia/wallet/cat_wallet/cat_utils.py +6 -4
  396. chia/wallet/cat_wallet/cat_wallet.py +16 -18
  397. chia/wallet/cat_wallet/lineage_store.py +2 -1
  398. chia/wallet/coin_selection.py +5 -5
  399. chia/wallet/conditions.py +22 -16
  400. chia/wallet/db_wallet/db_wallet_puzzles.py +15 -15
  401. chia/wallet/derivation_record.py +2 -2
  402. chia/wallet/derive_keys.py +2 -2
  403. chia/wallet/did_wallet/did_info.py +3 -2
  404. chia/wallet/did_wallet/did_wallet.py +41 -19
  405. chia/wallet/did_wallet/did_wallet_puzzles.py +18 -12
  406. chia/wallet/driver_protocol.py +1 -1
  407. chia/wallet/lineage_proof.py +3 -2
  408. chia/wallet/nft_wallet/metadata_outer_puzzle.py +6 -7
  409. chia/wallet/nft_wallet/nft_info.py +5 -5
  410. chia/wallet/nft_wallet/nft_puzzle_utils.py +293 -0
  411. chia/wallet/nft_wallet/nft_puzzles.py +21 -298
  412. chia/wallet/nft_wallet/nft_wallet.py +47 -62
  413. chia/wallet/nft_wallet/ownership_outer_puzzle.py +4 -8
  414. chia/wallet/nft_wallet/singleton_outer_puzzle.py +3 -2
  415. chia/wallet/nft_wallet/transfer_program_puzzle.py +6 -10
  416. chia/wallet/nft_wallet/uncurry_nft.py +6 -8
  417. chia/wallet/notification_manager.py +5 -5
  418. chia/wallet/notification_store.py +3 -2
  419. chia/wallet/outer_puzzles.py +2 -1
  420. chia/wallet/puzzles/clawback/drivers.py +21 -8
  421. chia/wallet/puzzles/clawback/metadata.py +3 -2
  422. chia/wallet/puzzles/clawback/puzzle_decorator.py +5 -4
  423. chia/wallet/puzzles/deployed_puzzle_hashes.json +0 -10
  424. chia/wallet/puzzles/p2_conditions.py +3 -2
  425. chia/wallet/puzzles/p2_delegated_conditions.py +3 -2
  426. chia/wallet/puzzles/p2_delegated_puzzle.py +3 -2
  427. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +3 -3
  428. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +3 -2
  429. chia/wallet/puzzles/p2_puzzle_hash.py +4 -3
  430. chia/wallet/puzzles/puzzle_utils.py +3 -2
  431. chia/wallet/puzzles/singleton_top_layer.py +26 -10
  432. chia/wallet/puzzles/singleton_top_layer_v1_1.py +21 -9
  433. chia/wallet/puzzles/tails.py +21 -129
  434. chia/wallet/signer_protocol.py +3 -2
  435. chia/wallet/singleton.py +12 -6
  436. chia/wallet/singleton_record.py +3 -2
  437. chia/wallet/trade_manager.py +31 -55
  438. chia/wallet/trade_record.py +3 -2
  439. chia/wallet/trading/offer.py +14 -13
  440. chia/wallet/trading/trade_store.py +3 -4
  441. chia/wallet/transaction_record.py +2 -2
  442. chia/wallet/util/blind_signer_tl.py +3 -2
  443. chia/wallet/util/compute_hints.py +3 -2
  444. chia/wallet/util/compute_memos.py +2 -2
  445. chia/wallet/util/curry_and_treehash.py +1 -2
  446. chia/wallet/util/merkle_tree.py +1 -1
  447. chia/wallet/util/merkle_utils.py +1 -1
  448. chia/wallet/util/new_peak_queue.py +2 -1
  449. chia/wallet/util/notifications.py +5 -4
  450. chia/wallet/util/peer_request_cache.py +3 -2
  451. chia/wallet/util/puzzle_compression.py +6 -4
  452. chia/wallet/util/puzzle_decorator.py +6 -4
  453. chia/wallet/util/query_filter.py +3 -2
  454. chia/wallet/util/tx_config.py +3 -3
  455. chia/wallet/util/wallet_sync_utils.py +2 -2
  456. chia/wallet/util/wallet_types.py +2 -3
  457. chia/wallet/vc_wallet/cr_cat_drivers.py +18 -22
  458. chia/wallet/vc_wallet/cr_cat_wallet.py +14 -10
  459. chia/wallet/vc_wallet/cr_outer_puzzle.py +2 -2
  460. chia/wallet/vc_wallet/vc_drivers.py +50 -68
  461. chia/wallet/vc_wallet/vc_store.py +2 -2
  462. chia/wallet/vc_wallet/vc_wallet.py +47 -15
  463. chia/wallet/wallet.py +51 -46
  464. chia/wallet/wallet_action_scope.py +4 -0
  465. chia/wallet/wallet_blockchain.py +12 -7
  466. chia/wallet/wallet_coin_record.py +3 -2
  467. chia/wallet/wallet_coin_store.py +3 -2
  468. chia/wallet/wallet_info.py +2 -1
  469. chia/wallet/wallet_interested_store.py +3 -2
  470. chia/wallet/wallet_nft_store.py +4 -4
  471. chia/wallet/wallet_node.py +3 -4
  472. chia/wallet/wallet_pool_store.py +3 -4
  473. chia/wallet/wallet_protocol.py +19 -5
  474. chia/wallet/wallet_puzzle_store.py +2 -2
  475. chia/wallet/wallet_retry_store.py +3 -6
  476. chia/wallet/wallet_singleton_store.py +2 -2
  477. chia/wallet/wallet_state_manager.py +20 -197
  478. chia/wallet/wallet_transaction_store.py +2 -2
  479. chia/wallet/wallet_user_store.py +2 -1
  480. chia/wallet/wallet_weight_proof_handler.py +3 -2
  481. {chia_blockchain-2.5.2rc2.dist-info → chia_blockchain-2.5.3.dist-info}/METADATA +3 -2
  482. chia_blockchain-2.5.3.dist-info/RECORD +891 -0
  483. mozilla-ca/cacert.pem +64 -33
  484. chia/_tests/clvm/test_condition_codes.py +0 -13
  485. chia/_tests/cmds/wallet/test_dao.py +0 -565
  486. chia/_tests/wallet/dao_wallet/__init__.py +0 -0
  487. chia/_tests/wallet/dao_wallet/config.py +0 -3
  488. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +0 -1330
  489. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +0 -3488
  490. chia/cmds/dao.py +0 -1064
  491. chia/cmds/dao_funcs.py +0 -598
  492. chia/consensus/puzzles/__init__.py +0 -0
  493. chia/consensus/puzzles/chialisp_deserialisation.clsp +0 -69
  494. chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +0 -1
  495. chia/consensus/puzzles/rom_bootstrap_generator.clsp +0 -37
  496. chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +0 -1
  497. chia/full_node/puzzles/__init__.py +0 -0
  498. chia/full_node/puzzles/block_program_zero.clsp +0 -14
  499. chia/full_node/puzzles/block_program_zero.clsp.hex +0 -1
  500. chia/full_node/puzzles/decompress_coin_spend_entry.clsp +0 -5
  501. chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +0 -1
  502. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +0 -7
  503. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +0 -1
  504. chia/full_node/puzzles/decompress_puzzle.clsp +0 -6
  505. chia/full_node/puzzles/decompress_puzzle.clsp.hex +0 -1
  506. chia/pools/puzzles/__init__.py +0 -0
  507. chia/pools/puzzles/pool_member_innerpuz.clsp +0 -70
  508. chia/pools/puzzles/pool_member_innerpuz.clsp.hex +0 -1
  509. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +0 -69
  510. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +0 -1
  511. chia/simulator/simulator_constants.py +0 -13
  512. chia/types/blockchain_format/foliage.py +0 -8
  513. chia/types/blockchain_format/pool_target.py +0 -5
  514. chia/types/blockchain_format/reward_chain_block.py +0 -6
  515. chia/types/blockchain_format/sized_bytes.py +0 -11
  516. chia/util/ints.py +0 -19
  517. chia/wallet/cat_wallet/dao_cat_info.py +0 -28
  518. chia/wallet/cat_wallet/dao_cat_wallet.py +0 -669
  519. chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
  520. chia/wallet/cat_wallet/puzzles/cat_truths.clib +0 -31
  521. chia/wallet/cat_wallet/puzzles/cat_v2.clsp +0 -397
  522. chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +0 -1
  523. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +0 -25
  524. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +0 -1
  525. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +0 -15
  526. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +0 -1
  527. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +0 -26
  528. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +0 -1
  529. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +0 -42
  530. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +0 -1
  531. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +0 -24
  532. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +0 -1
  533. chia/wallet/dao_wallet/__init__.py +0 -0
  534. chia/wallet/dao_wallet/dao_info.py +0 -61
  535. chia/wallet/dao_wallet/dao_utils.py +0 -811
  536. chia/wallet/dao_wallet/dao_wallet.py +0 -2119
  537. chia/wallet/did_wallet/puzzles/__init__.py +0 -0
  538. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +0 -135
  539. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +0 -1
  540. chia/wallet/payment.py +0 -33
  541. chia/wallet/puzzles/augmented_condition.clsp +0 -13
  542. chia/wallet/puzzles/augmented_condition.clsp.hex +0 -1
  543. chia/wallet/puzzles/condition_codes.clib +0 -77
  544. chia/wallet/puzzles/curry-and-treehash.clib +0 -102
  545. chia/wallet/puzzles/curry.clib +0 -135
  546. chia/wallet/puzzles/curry_by_index.clib +0 -16
  547. chia/wallet/puzzles/dao_cat_eve.clsp +0 -17
  548. chia/wallet/puzzles/dao_cat_eve.clsp.hex +0 -1
  549. chia/wallet/puzzles/dao_cat_launcher.clsp +0 -36
  550. chia/wallet/puzzles/dao_cat_launcher.clsp.hex +0 -1
  551. chia/wallet/puzzles/dao_finished_state.clsp +0 -35
  552. chia/wallet/puzzles/dao_finished_state.clsp.hex +0 -1
  553. chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +0 -1
  554. chia/wallet/puzzles/dao_lockup.clsp +0 -288
  555. chia/wallet/puzzles/dao_lockup.clsp.hex +0 -1
  556. chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +0 -1
  557. chia/wallet/puzzles/dao_proposal.clsp +0 -377
  558. chia/wallet/puzzles/dao_proposal.clsp.hex +0 -1
  559. chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +0 -1
  560. chia/wallet/puzzles/dao_proposal_timer.clsp +0 -78
  561. chia/wallet/puzzles/dao_proposal_timer.clsp.hex +0 -1
  562. chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +0 -1
  563. chia/wallet/puzzles/dao_proposal_validator.clsp +0 -87
  564. chia/wallet/puzzles/dao_proposal_validator.clsp.hex +0 -1
  565. chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +0 -1
  566. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +0 -240
  567. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +0 -1
  568. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +0 -1
  569. chia/wallet/puzzles/dao_treasury.clsp +0 -115
  570. chia/wallet/puzzles/dao_treasury.clsp.hex +0 -1
  571. chia/wallet/puzzles/dao_update_proposal.clsp +0 -44
  572. chia/wallet/puzzles/dao_update_proposal.clsp.hex +0 -1
  573. chia/wallet/puzzles/json.clib +0 -25
  574. chia/wallet/puzzles/merkle_utils.clib +0 -18
  575. chia/wallet/puzzles/notification.clsp +0 -7
  576. chia/wallet/puzzles/notification.clsp.hex +0 -1
  577. chia/wallet/puzzles/p2_1_of_n.clsp +0 -22
  578. chia/wallet/puzzles/p2_1_of_n.clsp.hex +0 -1
  579. chia/wallet/puzzles/p2_conditions.clsp +0 -3
  580. chia/wallet/puzzles/p2_conditions.clsp.hex +0 -1
  581. chia/wallet/puzzles/p2_delegated_conditions.clsp +0 -18
  582. chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +0 -1
  583. chia/wallet/puzzles/p2_delegated_puzzle.clsp +0 -19
  584. chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +0 -1
  585. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +0 -91
  586. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +0 -1
  587. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +0 -108
  588. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +0 -1
  589. chia/wallet/puzzles/p2_parent.clsp +0 -19
  590. chia/wallet/puzzles/p2_parent.clsp.hex +0 -1
  591. chia/wallet/puzzles/p2_puzzle_hash.clsp +0 -18
  592. chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +0 -1
  593. chia/wallet/puzzles/p2_singleton.clsp +0 -30
  594. chia/wallet/puzzles/p2_singleton.clsp.hex +0 -1
  595. chia/wallet/puzzles/p2_singleton_aggregator.clsp +0 -81
  596. chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +0 -1
  597. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +0 -50
  598. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +0 -1
  599. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +0 -47
  600. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +0 -1
  601. chia/wallet/puzzles/settlement_payments.clsp +0 -49
  602. chia/wallet/puzzles/settlement_payments.clsp.hex +0 -1
  603. chia/wallet/puzzles/sha256tree.clib +0 -11
  604. chia/wallet/puzzles/singleton_launcher.clsp +0 -16
  605. chia/wallet/puzzles/singleton_launcher.clsp.hex +0 -1
  606. chia/wallet/puzzles/singleton_top_layer.clsp +0 -177
  607. chia/wallet/puzzles/singleton_top_layer.clsp.hex +0 -1
  608. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +0 -107
  609. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +0 -1
  610. chia/wallet/puzzles/singleton_truths.clib +0 -21
  611. chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
  612. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +0 -3
  613. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +0 -1
  614. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +0 -304
  615. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +0 -1
  616. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +0 -45
  617. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +0 -1
  618. chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
  619. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +0 -30
  620. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +0 -1
  621. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +0 -75
  622. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +0 -1
  623. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +0 -32
  624. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +0 -1
  625. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +0 -80
  626. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +0 -1
  627. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +0 -163
  628. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +0 -1
  629. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +0 -16
  630. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +0 -1
  631. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +0 -74
  632. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +0 -1
  633. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +0 -23
  634. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +0 -1
  635. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +0 -64
  636. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +0 -1
  637. chia_blockchain-2.5.2rc2.dist-info/RECORD +0 -1042
  638. {chia_blockchain-2.5.2rc2.dist-info → chia_blockchain-2.5.3.dist-info}/LICENSE +0 -0
  639. {chia_blockchain-2.5.2rc2.dist-info → chia_blockchain-2.5.3.dist-info}/WHEEL +0 -0
  640. {chia_blockchain-2.5.2rc2.dist-info → chia_blockchain-2.5.3.dist-info}/entry_points.txt +0 -0
@@ -1,2119 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import copy
4
- import dataclasses
5
- import json
6
- import logging
7
- import re
8
- import time
9
- from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast
10
-
11
- from chia_rs import AugSchemeMPL, G1Element, G2Element
12
- from clvm.casts import int_from_bytes
13
-
14
- from chia.full_node.full_node_api import FullNodeAPI
15
- from chia.protocols.wallet_protocol import CoinState, RequestBlockHeader, RespondBlockHeader
16
- from chia.server.ws_connection import WSChiaConnection
17
- from chia.types.blockchain_format.coin import Coin
18
- from chia.types.blockchain_format.program import Program
19
- from chia.types.blockchain_format.sized_bytes import bytes32
20
- from chia.types.coin_spend import CoinSpend, make_spend
21
- from chia.types.condition_opcodes import ConditionOpcode
22
- from chia.util.ints import uint32, uint64, uint128
23
- from chia.wallet import singleton
24
- from chia.wallet.cat_wallet.cat_utils import (
25
- CAT_MOD,
26
- SpendableCAT,
27
- construct_cat_puzzle,
28
- unsigned_spend_bundle_for_spendable_cats,
29
- )
30
- from chia.wallet.cat_wallet.cat_utils import get_innerpuzzle_from_puzzle as get_innerpuzzle_from_cat_puzzle
31
- from chia.wallet.cat_wallet.cat_wallet import CATWallet
32
- from chia.wallet.cat_wallet.dao_cat_wallet import DAOCATWallet
33
- from chia.wallet.coin_selection import select_coins
34
- from chia.wallet.conditions import AssertCoinAnnouncement, Condition, parse_timelock_info
35
- from chia.wallet.dao_wallet.dao_info import DAOInfo, DAORules, ProposalInfo, ProposalType
36
- from chia.wallet.dao_wallet.dao_utils import (
37
- DAO_FINISHED_STATE,
38
- DAO_PROPOSAL_MOD,
39
- DAO_TREASURY_MOD,
40
- SINGLETON_LAUNCHER,
41
- create_cat_launcher_for_singleton_id,
42
- curry_cat_eve,
43
- curry_singleton,
44
- generate_cat_tail,
45
- get_active_votes_from_lockup_puzzle,
46
- get_asset_id_from_puzzle,
47
- get_dao_rules_from_update_proposal,
48
- get_finished_state_inner_puzzle,
49
- get_finished_state_puzzle,
50
- get_innerpuz_from_lockup_puzzle,
51
- get_new_puzzle_from_proposal_solution,
52
- get_new_puzzle_from_treasury_solution,
53
- get_p2_singleton_puzhash,
54
- get_p2_singleton_puzzle,
55
- get_proposal_args,
56
- get_proposal_puzzle,
57
- get_proposal_timer_puzzle,
58
- get_proposed_puzzle_reveal_from_solution,
59
- get_treasury_puzzle,
60
- get_treasury_rules_from_puzzle,
61
- match_funding_puzzle,
62
- uncurry_proposal,
63
- uncurry_treasury,
64
- )
65
- from chia.wallet.lineage_proof import LineageProof
66
- from chia.wallet.singleton import (
67
- get_inner_puzzle_from_singleton,
68
- get_most_recent_singleton_coin_from_coin_spend,
69
- get_singleton_id_from_puzzle,
70
- get_singleton_struct_for_id,
71
- )
72
- from chia.wallet.transaction_record import TransactionRecord
73
- from chia.wallet.uncurried_puzzle import uncurry_puzzle
74
- from chia.wallet.util.transaction_type import TransactionType
75
- from chia.wallet.util.wallet_sync_utils import fetch_coin_spend
76
- from chia.wallet.util.wallet_types import WalletType
77
- from chia.wallet.wallet import Wallet
78
- from chia.wallet.wallet_action_scope import WalletActionScope
79
- from chia.wallet.wallet_coin_record import WalletCoinRecord
80
- from chia.wallet.wallet_info import WalletInfo
81
- from chia.wallet.wallet_spend_bundle import WalletSpendBundle
82
-
83
-
84
- class DAOWallet:
85
- """
86
- This is a wallet in the sense that it conforms to the interface needed by WalletStateManager.
87
- It is not a user-facing wallet. A user cannot spend or receive XCH though a wallet of this type.
88
-
89
- Wallets of type CAT and DAO_CAT are the user-facing wallets which hold the voting tokens a user
90
- owns. The DAO Wallet is used for state-tracking of the Treasury Singleton and its associated
91
- Proposals.
92
-
93
- State change Spends (spends this user creates, either from DAOWallet or DAOCATWallet:
94
- * Create a proposal
95
- * Add more votes to a proposal
96
- * Lock / Unlock voting tokens
97
- * Collect finished state of a Proposal - spend to read the oracle result and Get our CAT coins back
98
- * Anyone can send money to the Treasury, whether in possession of a voting CAT or not
99
-
100
- Incoming spends we listen for:
101
- * Update Treasury state if treasury is spent
102
- * Hear about a finished proposal
103
- * Hear about a new proposal -- check interest threshold (how many votes)
104
- * Get Updated Proposal Data
105
- """
106
-
107
- if TYPE_CHECKING:
108
- from chia.wallet.wallet_protocol import WalletProtocol
109
-
110
- _protocol_check: ClassVar[WalletProtocol[DAOInfo]] = cast("DAOWallet", None)
111
-
112
- wallet_state_manager: Any
113
- log: logging.Logger
114
- wallet_info: WalletInfo
115
- dao_info: DAOInfo
116
- dao_rules: DAORules
117
- standard_wallet: Wallet
118
- wallet_id: uint32
119
-
120
- @staticmethod
121
- async def create_new_dao_and_wallet(
122
- wallet_state_manager: Any,
123
- wallet: Wallet,
124
- amount_of_cats: uint64,
125
- dao_rules: DAORules,
126
- action_scope: WalletActionScope,
127
- filter_amount: uint64 = uint64(1),
128
- name: Optional[str] = None,
129
- fee: uint64 = uint64(0),
130
- fee_for_cat: uint64 = uint64(0),
131
- ) -> DAOWallet:
132
- """
133
- Create a brand new DAO wallet
134
- This must be called under the wallet state manager lock
135
- :param wallet_state_manager: Wallet state manager
136
- :param wallet: Standard wallet
137
- :param amount_of_cats: Initial amount of voting CATs
138
- :param dao_rules: The rules which govern the DAO
139
- :param filter_amount: Min votes to see proposal (user defined)
140
- :param name: Wallet name
141
- :param fee: transaction fee
142
- :param fee_for_cat: transaction fee for creating the CATs
143
- :return: DAO wallet
144
- """
145
-
146
- self = DAOWallet()
147
- self.wallet_state_manager = wallet_state_manager
148
- if name is None:
149
- name = self.generate_wallet_name()
150
-
151
- self.standard_wallet = wallet
152
- self.log = logging.getLogger(name if name else __name__)
153
- std_wallet_id = self.standard_wallet.wallet_id
154
- bal = await wallet_state_manager.get_confirmed_balance_for_wallet(std_wallet_id)
155
- if amount_of_cats > bal:
156
- raise ValueError(f"Your balance of {bal} mojos is not enough to create {amount_of_cats} CATs")
157
-
158
- self.dao_info = DAOInfo(
159
- treasury_id=bytes32.zeros,
160
- cat_wallet_id=uint32(0),
161
- dao_cat_wallet_id=uint32(0),
162
- proposals_list=[],
163
- parent_info=[],
164
- current_treasury_coin=None,
165
- current_treasury_innerpuz=None,
166
- singleton_block_height=uint32(0),
167
- filter_below_vote_amount=filter_amount,
168
- assets=[],
169
- current_height=uint64(0),
170
- )
171
- self.dao_rules = dao_rules
172
- info_as_string = json.dumps(self.dao_info.to_json_dict())
173
- self.wallet_info = await wallet_state_manager.user_store.create_wallet(
174
- name, WalletType.DAO.value, info_as_string
175
- )
176
- self.wallet_id = self.wallet_info.id
177
- std_wallet_id = self.standard_wallet.wallet_id
178
-
179
- try:
180
- await self.generate_new_dao(
181
- amount_of_cats,
182
- action_scope,
183
- fee=fee,
184
- fee_for_cat=fee_for_cat,
185
- )
186
- except Exception as e_info: # pragma: no cover
187
- await wallet_state_manager.delete_wallet(self.id())
188
- self.log.exception(f"Failed to create dao wallet: {e_info}")
189
- raise
190
-
191
- await self.wallet_state_manager.add_new_wallet(self)
192
-
193
- # Now the dao wallet is created we can create the dao_cat wallet
194
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
195
- cat_tail = cat_wallet.cat_info.limitations_program_hash
196
- new_dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
197
- self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
198
- )
199
- dao_cat_wallet_id = new_dao_cat_wallet.wallet_info.id
200
- dao_info = dataclasses.replace(
201
- self.dao_info, cat_wallet_id=cat_wallet.id(), dao_cat_wallet_id=dao_cat_wallet_id
202
- )
203
- await self.save_info(dao_info)
204
-
205
- return self
206
-
207
- @staticmethod
208
- async def create_new_dao_wallet_for_existing_dao(
209
- wallet_state_manager: Any,
210
- main_wallet: Wallet,
211
- treasury_id: bytes32,
212
- filter_amount: uint64 = uint64(1),
213
- name: Optional[str] = None,
214
- ) -> DAOWallet:
215
- """
216
- Create a DAO wallet for existing DAO
217
- :param wallet_state_manager: Wallet state manager
218
- :param main_wallet: Standard wallet
219
- :param treasury_id: The singleton ID of the DAO treasury coin
220
- :param filter_amount: Min votes to see proposal (user defined)
221
- :param name: Wallet name
222
- :return: DAO wallet
223
- """
224
- self = DAOWallet()
225
- self.wallet_state_manager = wallet_state_manager
226
- if name is None:
227
- name = self.generate_wallet_name()
228
-
229
- self.standard_wallet = main_wallet
230
- self.log = logging.getLogger(name if name else __name__)
231
- self.log.info("Creating DAO wallet for existent DAO ...")
232
- self.dao_info = DAOInfo(
233
- treasury_id=treasury_id,
234
- cat_wallet_id=uint32(0),
235
- dao_cat_wallet_id=uint32(0),
236
- proposals_list=[],
237
- parent_info=[],
238
- current_treasury_coin=None,
239
- current_treasury_innerpuz=None,
240
- singleton_block_height=uint32(0),
241
- filter_below_vote_amount=filter_amount,
242
- assets=[],
243
- current_height=uint64(0),
244
- )
245
- info_as_string = json.dumps(self.dao_info.to_json_dict())
246
- self.wallet_info = await wallet_state_manager.user_store.create_wallet(
247
- name, WalletType.DAO.value, info_as_string
248
- )
249
- await self.wallet_state_manager.add_new_wallet(self)
250
- await self.resync_treasury_state()
251
- await self.save_info(self.dao_info)
252
- self.wallet_id = self.wallet_info.id
253
-
254
- # Now the dao wallet is created we can create the dao_cat wallet
255
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
256
- cat_tail = cat_wallet.cat_info.limitations_program_hash
257
- new_dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
258
- self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
259
- )
260
- dao_cat_wallet_id = new_dao_cat_wallet.wallet_info.id
261
- dao_info = dataclasses.replace(
262
- self.dao_info, cat_wallet_id=cat_wallet.id(), dao_cat_wallet_id=dao_cat_wallet_id
263
- )
264
- await self.save_info(dao_info)
265
-
266
- # add treasury id to interested puzzle hashes. This is hinted in funding coins so we can track them
267
- funding_inner_hash = get_p2_singleton_puzhash(self.dao_info.treasury_id)
268
- await self.wallet_state_manager.add_interested_puzzle_hashes(
269
- [self.dao_info.treasury_id, funding_inner_hash], [self.id(), self.id()]
270
- )
271
- return self
272
-
273
- @staticmethod
274
- async def create(
275
- wallet_state_manager: Any,
276
- wallet: Wallet,
277
- wallet_info: WalletInfo,
278
- name: Optional[str] = None,
279
- ) -> DAOWallet:
280
- """
281
- Create a DID wallet based on the local database
282
- :param wallet_state_manager: Wallet state manager
283
- :param wallet: Standard wallet
284
- :param wallet_info: Serialized WalletInfo
285
- :param name: Wallet name
286
- :return:
287
- """
288
- self = DAOWallet()
289
- self.log = logging.getLogger(name if name else __name__)
290
- self.wallet_state_manager = wallet_state_manager
291
- self.wallet_info = wallet_info
292
- self.wallet_id = wallet_info.id
293
- self.standard_wallet = wallet
294
- self.dao_info = DAOInfo.from_json_dict(json.loads(wallet_info.data))
295
- self.dao_rules = get_treasury_rules_from_puzzle(self.dao_info.current_treasury_innerpuz)
296
- return self
297
-
298
- @classmethod
299
- def type(cls) -> WalletType:
300
- return WalletType.DAO
301
-
302
- def id(self) -> uint32:
303
- return self.wallet_info.id
304
-
305
- async def set_name(self, new_name: str) -> None:
306
- new_info = dataclasses.replace(self.wallet_info, name=new_name)
307
- self.wallet_info = new_info
308
- await self.wallet_state_manager.user_store.update_wallet(self.wallet_info)
309
-
310
- def get_name(self) -> str:
311
- return self.wallet_info.name
312
-
313
- async def match_hinted_coin(self, coin: Coin, hint: bytes32) -> bool:
314
- raise NotImplementedError("Method not implemented for DAO Wallet") # pragma: no cover
315
-
316
- def puzzle_hash_for_pk(self, pubkey: G1Element) -> bytes32:
317
- raise NotImplementedError("puzzle_hash_for_pk is not available in DAO wallets") # pragma: no cover
318
-
319
- async def get_new_p2_inner_hash(self) -> bytes32:
320
- puzzle = await self.get_new_p2_inner_puzzle()
321
- return puzzle.get_tree_hash()
322
-
323
- async def get_new_p2_inner_puzzle(self) -> Program:
324
- return await self.standard_wallet.get_new_puzzle()
325
-
326
- def get_parent_for_coin(self, coin: Coin) -> Optional[LineageProof]:
327
- parent_info = None
328
- for name, ccparent in self.dao_info.parent_info:
329
- if name == coin.parent_coin_info:
330
- parent_info = ccparent
331
- return parent_info
332
-
333
- async def get_max_send_amount(self, records: Optional[set[WalletCoinRecord]] = None) -> uint128:
334
- return uint128(0) # pragma: no cover
335
-
336
- async def get_spendable_balance(self, unspent_records: Optional[set[WalletCoinRecord]] = None) -> uint128:
337
- # No spendable or receivable value
338
- return uint128(1)
339
-
340
- async def get_confirmed_balance(self, record_list: Optional[set[WalletCoinRecord]] = None) -> uint128:
341
- # No spendable or receivable value
342
- return uint128(1)
343
-
344
- async def select_coins(
345
- self,
346
- amount: uint64,
347
- action_scope: WalletActionScope,
348
- ) -> set[Coin]:
349
- """
350
- Returns a set of coins that can be used for generating a new transaction.
351
- Note: Must be called under wallet state manager lock
352
- There is no need for max/min coin amount or excluded amount because the dao treasury should
353
- always be a single coin with amount 1
354
- """
355
- spendable_amount: uint128 = await self.get_spendable_balance()
356
- if amount > spendable_amount:
357
- self.log.warning(f"Can't select {amount}, from spendable {spendable_amount} for wallet id {self.id()}")
358
- return set()
359
-
360
- spendable_coins: list[WalletCoinRecord] = list(
361
- await self.wallet_state_manager.get_spendable_coins_for_wallet(self.wallet_info.id)
362
- )
363
-
364
- # Try to use coins from the store, if there isn't enough of "unused"
365
- # coins use change coins that are not confirmed yet
366
- unconfirmed_removals: dict[bytes32, Coin] = await self.wallet_state_manager.unconfirmed_removals_for_wallet(
367
- self.wallet_info.id
368
- )
369
- async with action_scope.use() as interface:
370
- coins = await select_coins(
371
- spendable_amount,
372
- action_scope.config.adjust_for_side_effects(interface.side_effects).tx_config.coin_selection_config,
373
- spendable_coins,
374
- unconfirmed_removals,
375
- self.log,
376
- uint128(amount),
377
- )
378
- interface.side_effects.selected_coins.extend([*coins])
379
- assert sum(c.amount for c in coins) >= amount
380
- return coins
381
-
382
- async def get_pending_change_balance(self) -> uint64:
383
- # No spendable or receivable value
384
- return uint64(0)
385
-
386
- async def get_unconfirmed_balance(self, record_list: Optional[set[WalletCoinRecord]] = None) -> uint128:
387
- # No spendable or receivable value
388
- return uint128(1)
389
-
390
- # if asset_id == None: then we get normal XCH
391
- async def get_balance_by_asset_type(self, asset_id: Optional[bytes32] = None) -> uint128:
392
- puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
393
- records = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(puzhash)
394
- return uint128(sum(cr.coin.amount for cr in records if not cr.spent))
395
-
396
- # if asset_id == None: then we get normal XCH
397
- async def select_coins_for_asset_type(
398
- self, amount: uint64, action_scope: WalletActionScope, asset_id: Optional[bytes32] = None
399
- ) -> list[Coin]:
400
- puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
401
- records = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(puzhash)
402
- unspent_records = [r for r in records if not r.spent]
403
- spendable_amount = uint128(sum(r.coin.amount for r in unspent_records))
404
- async with action_scope.use() as interface:
405
- return list(
406
- await select_coins(
407
- spendable_amount,
408
- action_scope.config.adjust_for_side_effects(interface.side_effects).tx_config.coin_selection_config,
409
- unspent_records,
410
- {},
411
- self.log,
412
- uint128(amount),
413
- )
414
- )
415
-
416
- async def coin_added(self, coin: Coin, height: uint32, peer: WSChiaConnection, coin_data: Optional[Any]) -> None:
417
- """
418
- Notification from wallet state manager that a coin has been received.
419
- This can be either a treasury coin update or funds added to the treasury
420
- """
421
- self.log.info(f"DAOWallet.coin_added() called with the coin: {coin.name().hex()}:{coin}.")
422
- wallet_node: Any = self.wallet_state_manager.wallet_node
423
- peer = wallet_node.get_full_node_peer()
424
- if peer is None: # pragma: no cover
425
- raise ValueError("Could not find any peers to request puzzle and solution from")
426
- try:
427
- # Get the parent coin spend
428
- cs = (await wallet_node.get_coin_state([coin.parent_coin_info], peer, height))[0]
429
- parent_spend = await fetch_coin_spend(cs.spent_height, cs.coin, peer)
430
-
431
- uncurried = uncurry_puzzle(parent_spend.puzzle_reveal)
432
- matched_funding_puz = match_funding_puzzle(
433
- uncurried, parent_spend.solution.to_program(), coin, [self.dao_info.treasury_id]
434
- )
435
- if matched_funding_puz:
436
- # funding coin
437
- xch_funds_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
438
- if coin.puzzle_hash == xch_funds_puzhash:
439
- asset_id = None
440
- else:
441
- asset_id = get_asset_id_from_puzzle(parent_spend.puzzle_reveal.to_program())
442
- # to prevent fake p2_singletons being added
443
- assert coin.puzzle_hash == get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
444
- if asset_id not in self.dao_info.assets:
445
- new_asset_list = self.dao_info.assets.copy()
446
- new_asset_list.append(asset_id)
447
- dao_info = dataclasses.replace(self.dao_info, assets=new_asset_list)
448
- await self.save_info(dao_info)
449
- await self.wallet_state_manager.add_interested_puzzle_hashes([coin.puzzle_hash], [self.id()])
450
- self.log.info(f"DAO funding coin added: {coin.name().hex()}:{coin}. Asset ID: {asset_id}")
451
- except Exception as e: # pragma: no cover
452
- self.log.exception(f"Error occurred during dao wallet coin addition: {e}")
453
-
454
- def get_cat_tail_hash(self) -> bytes32:
455
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
456
- return cat_wallet.cat_info.limitations_program_hash
457
-
458
- async def adjust_filter_level(self, new_filter_level: uint64) -> None:
459
- dao_info = dataclasses.replace(self.dao_info, filter_below_vote_amount=new_filter_level)
460
- await self.save_info(dao_info)
461
-
462
- async def clear_finished_proposals_from_memory(self) -> None:
463
- dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
464
- new_list = [
465
- prop_info
466
- for prop_info in self.dao_info.proposals_list
467
- if not prop_info.closed
468
- or prop_info.closed is None
469
- or any(prop_info.proposal_id in lci.active_votes for lci in dao_cat_wallet.dao_cat_info.locked_coins)
470
- ]
471
- dao_info = dataclasses.replace(self.dao_info, proposals_list=new_list)
472
- await self.save_info(dao_info)
473
-
474
- async def resync_treasury_state(self) -> None:
475
- """
476
- This is called during create_new_dao_wallet_for_existing_dao.
477
- When we want to sync to an existing DAO, we provide the treasury coins singleton ID, and then trace all
478
- the child coins until we reach the current DAO treasury coin. We use the puzzle reveal and solution to
479
- get the current state of the DAO, and to work out what the tail of the DAO CAT token is.
480
- This also captures all the proposals that have been created and their state.
481
- """
482
- parent_coin_id: bytes32 = self.dao_info.treasury_id
483
- wallet_node: Any = self.wallet_state_manager.wallet_node
484
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
485
- if peer is None: # pragma: no cover
486
- raise ValueError("Could not find any peers to request puzzle and solution from")
487
-
488
- parent_coin = None
489
- parent_parent_coin = None
490
- while True:
491
- children = await wallet_node.fetch_children(parent_coin_id, peer)
492
- if len(children) == 0:
493
- break
494
-
495
- children_state_list: list[CoinState] = [child for child in children if child.coin.amount % 2 == 1]
496
- # ensure children_state_list has only one odd amount coin (the treasury)
497
- if (len(children_state_list) == 0) or (len(children_state_list) > 1): # pragma: no cover
498
- raise RuntimeError("Could not retrieve child_state")
499
- children_state = children_state_list[0]
500
- assert children_state is not None
501
- child_coin = children_state.coin
502
- if parent_coin is not None:
503
- parent_parent_coin = parent_coin
504
- parent_coin = child_coin
505
- parent_coin_id = child_coin.name()
506
-
507
- if parent_parent_coin is None: # pragma: no cover
508
- raise RuntimeError("could not get parent_parent_coin of %s", children)
509
-
510
- # ensure the child coin is unspent to prevent untrusted nodes sending false coin states
511
- assert children_state.spent_height is None
512
-
513
- # get lineage proof of parent spend, and also current innerpuz
514
- assert children_state.created_height
515
- parent_spend = await fetch_coin_spend(children_state.created_height, parent_parent_coin, peer)
516
- assert parent_spend is not None
517
- parent_inner_puz = get_inner_puzzle_from_singleton(parent_spend.puzzle_reveal)
518
- if parent_inner_puz is None: # pragma: no cover
519
- raise ValueError("get_innerpuzzle_from_puzzle failed")
520
-
521
- if parent_spend.puzzle_reveal.get_tree_hash() == child_coin.puzzle_hash:
522
- current_inner_puz = parent_inner_puz
523
- else: # pragma: no cover
524
- # extract the treasury solution from the full singleton solution
525
- inner_solution = parent_spend.solution.to_program().rest().rest().first()
526
- # reconstruct the treasury puzzle
527
- current_inner_puz = get_new_puzzle_from_treasury_solution(parent_inner_puz, inner_solution)
528
- # set the treasury rules
529
- self.dao_rules = get_treasury_rules_from_puzzle(current_inner_puz)
530
-
531
- current_lineage_proof = LineageProof(
532
- parent_parent_coin.parent_coin_info, parent_inner_puz.get_tree_hash(), parent_parent_coin.amount
533
- )
534
- await self.add_parent(parent_parent_coin.name(), current_lineage_proof)
535
-
536
- # Hack to find the cat tail hash from the memo of the genesis spend
537
- launcher_state = await wallet_node.get_coin_state([self.dao_info.treasury_id], peer)
538
- genesis_coin_id = launcher_state[0].coin.parent_coin_info
539
- genesis_state = await wallet_node.get_coin_state([genesis_coin_id], peer)
540
- genesis_spend = await fetch_coin_spend(genesis_state[0].spent_height, genesis_state[0].coin, peer)
541
- cat_tail_hash = None
542
- conds = genesis_spend.solution.to_program().at("rfr").as_iter()
543
- for cond in conds:
544
- if (cond.first().as_atom() == ConditionOpcode.CREATE_COIN) and (
545
- int_from_bytes(cond.at("rrf").as_atom()) == 1
546
- ):
547
- cat_tail_hash = bytes32(cond.at("rrrff").as_atom())
548
- cat_origin_id = bytes32(cond.at("rrrfrf").as_atom())
549
- # Calculate the CAT tail from the memo data. If someone tries to use a fake tail hash in
550
- # the memo field, it won't match with the DAO's actual treasury ID.
551
- cat_tail = generate_cat_tail(cat_origin_id, self.dao_info.treasury_id)
552
- break
553
- assert cat_tail_hash
554
- assert cat_tail.get_tree_hash() == cat_tail_hash
555
-
556
- cat_wallet: Optional[CATWallet] = None
557
-
558
- # Get or create a cat wallet
559
- for wallet_id in self.wallet_state_manager.wallets:
560
- wallet = self.wallet_state_manager.wallets[wallet_id]
561
- if wallet.type() == WalletType.CAT: # pragma: no cover
562
- assert isinstance(wallet, CATWallet)
563
- if wallet.cat_info.limitations_program_hash == cat_tail_hash:
564
- cat_wallet = wallet
565
- break
566
- else:
567
- # Didn't find a cat wallet, so create one
568
- cat_wallet = await CATWallet.get_or_create_wallet_for_cat(
569
- self.wallet_state_manager, self.standard_wallet, cat_tail_hash.hex()
570
- )
571
-
572
- assert cat_wallet is not None
573
- await cat_wallet.set_tail_program(bytes(cat_tail).hex())
574
- cat_wallet_id = cat_wallet.wallet_info.id
575
- dao_info = dataclasses.replace(
576
- self.dao_info,
577
- cat_wallet_id=uint32(cat_wallet_id),
578
- dao_cat_wallet_id=uint32(0),
579
- current_treasury_coin=child_coin,
580
- current_treasury_innerpuz=current_inner_puz,
581
- )
582
- await self.save_info(dao_info)
583
-
584
- future_parent = LineageProof(
585
- child_coin.parent_coin_info,
586
- dao_info.current_treasury_innerpuz.get_tree_hash(),
587
- uint64(child_coin.amount),
588
- )
589
- await self.add_parent(child_coin.name(), future_parent)
590
- assert self.dao_info.parent_info is not None
591
-
592
- # get existing xch funds for treasury
593
- xch_funds_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
594
- await self.wallet_state_manager.add_interested_puzzle_hashes([xch_funds_puzhash], [self.id()])
595
- await self.wallet_state_manager.add_interested_puzzle_hashes([self.dao_info.treasury_id], [self.id()])
596
- await self.wallet_state_manager.add_interested_puzzle_hashes(
597
- [self.dao_info.current_treasury_coin.puzzle_hash], [self.id()]
598
- )
599
-
600
- # Resync the wallet from when the treasury was created to get the existing funds
601
- # TODO: Maybe split this out as an option for users since it may be slow?
602
- if not wallet_node.is_trusted(peer):
603
- # Untrusted nodes won't automatically send us the history of all the treasury and proposal coins,
604
- # so we have to request them via sync_from_untrusted_close_to_peak
605
- request = RequestBlockHeader(children_state.created_height)
606
- response: Optional[RespondBlockHeader] = await peer.call_api(FullNodeAPI.request_block_header, request)
607
- await wallet_node.sync_from_untrusted_close_to_peak(response.header_block, peer)
608
-
609
- async def generate_new_dao(
610
- self,
611
- amount_of_cats_to_create: Optional[uint64],
612
- action_scope: WalletActionScope,
613
- cat_tail_hash: Optional[bytes32] = None,
614
- fee: uint64 = uint64(0),
615
- fee_for_cat: uint64 = uint64(0),
616
- extra_conditions: tuple[Condition, ...] = tuple(),
617
- ) -> None:
618
- """
619
- Create a new DAO treasury using the dao_rules object. This does the first spend to create the launcher
620
- and eve coins.
621
- The eve spend has to be completed in a separate tx using 'submit_eve_spend' once the number of blocks required
622
- by dao_rules.oracle_spend_delay has passed.
623
- This must be called under the wallet state manager lock
624
- """
625
-
626
- if amount_of_cats_to_create is not None and amount_of_cats_to_create < 0: # pragma: no cover
627
- raise ValueError("amount_of_cats must be >= 0, or None")
628
- if (
629
- amount_of_cats_to_create is None or amount_of_cats_to_create == 0
630
- ) and cat_tail_hash is None: # pragma: no cover
631
- raise ValueError("amount_of_cats must be > 0 or cat_tail_hash must be specified")
632
- if (
633
- amount_of_cats_to_create is not None and amount_of_cats_to_create > 0 and cat_tail_hash is not None
634
- ): # pragma: no cover
635
- raise ValueError("cannot create voting cats and use existing cat_tail_hash")
636
- if self.dao_rules.pass_percentage > 10000 or self.dao_rules.pass_percentage < 0: # pragma: no cover
637
- raise ValueError("proposal pass percentage must be between 0 and 10000")
638
-
639
- if amount_of_cats_to_create is not None and amount_of_cats_to_create > 0:
640
- coins = await self.standard_wallet.select_coins(
641
- uint64(amount_of_cats_to_create + fee + 1),
642
- action_scope,
643
- )
644
- else: # pragma: no cover
645
- coins = await self.standard_wallet.select_coins(uint64(fee + 1), action_scope)
646
-
647
- if coins is None: # pragma: no cover
648
- return None
649
- # origin is normal coin which creates launcher coin
650
- origin = coins.copy().pop()
651
-
652
- genesis_launcher_puz = SINGLETON_LAUNCHER
653
- # launcher coin contains singleton launcher, launcher coin ID == singleton_id == treasury_id
654
- launcher_coin = Coin(origin.name(), genesis_launcher_puz.get_tree_hash(), uint64(1))
655
-
656
- if cat_tail_hash is None:
657
- assert amount_of_cats_to_create is not None
658
- different_coins = await self.standard_wallet.select_coins(
659
- uint64(amount_of_cats_to_create + fee_for_cat),
660
- action_scope,
661
- )
662
- cat_origin = different_coins.copy().pop()
663
- assert origin.name() != cat_origin.name()
664
- cat_tail = generate_cat_tail(cat_origin.name(), launcher_coin.name())
665
- cat_tail_hash = cat_tail.get_tree_hash()
666
-
667
- assert cat_tail_hash is not None
668
-
669
- dao_info: DAOInfo = DAOInfo(
670
- launcher_coin.name(),
671
- self.dao_info.cat_wallet_id,
672
- self.dao_info.dao_cat_wallet_id,
673
- self.dao_info.proposals_list,
674
- self.dao_info.parent_info,
675
- None,
676
- None,
677
- uint32(0),
678
- self.dao_info.filter_below_vote_amount,
679
- self.dao_info.assets,
680
- self.dao_info.current_height,
681
- )
682
- await self.save_info(dao_info)
683
- new_cat_wallet = None
684
- # This will also mint the coins
685
- if amount_of_cats_to_create is not None and different_coins is not None:
686
- cat_tail_info = {
687
- "identifier": "genesis_by_id_or_singleton",
688
- "treasury_id": launcher_coin.name(),
689
- "coins": different_coins,
690
- }
691
- new_cat_wallet = await CATWallet.create_new_cat_wallet(
692
- self.wallet_state_manager,
693
- self.standard_wallet,
694
- cat_tail_info,
695
- amount_of_cats_to_create,
696
- action_scope,
697
- fee=fee_for_cat,
698
- push=False,
699
- )
700
- assert new_cat_wallet is not None
701
- else: # pragma: no cover
702
- for wallet in self.wallet_state_manager.wallets:
703
- if self.wallet_state_manager.wallets[wallet].type() == WalletType.CAT:
704
- if self.wallet_state_manager.wallets[wallet].cat_info.limitations_program_hash == cat_tail_hash:
705
- new_cat_wallet = self.wallet_state_manager.wallets[wallet]
706
-
707
- assert new_cat_wallet is not None
708
- cat_wallet_id = new_cat_wallet.wallet_info.id
709
-
710
- assert cat_tail_hash == new_cat_wallet.cat_info.limitations_program_hash
711
- await new_cat_wallet.set_tail_program(bytes(cat_tail).hex())
712
- dao_info = DAOInfo(
713
- self.dao_info.treasury_id,
714
- cat_wallet_id,
715
- self.dao_info.dao_cat_wallet_id,
716
- self.dao_info.proposals_list,
717
- self.dao_info.parent_info,
718
- None,
719
- None,
720
- uint32(0),
721
- self.dao_info.filter_below_vote_amount,
722
- self.dao_info.assets,
723
- self.dao_info.current_height,
724
- )
725
-
726
- await self.save_info(dao_info)
727
-
728
- dao_treasury_puzzle = get_treasury_puzzle(self.dao_rules, launcher_coin.name(), cat_tail_hash)
729
- full_treasury_puzzle = curry_singleton(launcher_coin.name(), dao_treasury_puzzle)
730
- full_treasury_puzzle_hash = full_treasury_puzzle.get_tree_hash()
731
-
732
- announcement_message = Program.to([full_treasury_puzzle_hash, 1, bytes(0x80)]).get_tree_hash()
733
-
734
- await self.standard_wallet.generate_signed_transaction(
735
- uint64(1),
736
- genesis_launcher_puz.get_tree_hash(),
737
- action_scope,
738
- fee,
739
- origin_id=origin.name(),
740
- coins=set(coins),
741
- memos=[new_cat_wallet.cat_info.limitations_program_hash, cat_origin.name()],
742
- extra_conditions=(
743
- AssertCoinAnnouncement(asserted_id=launcher_coin.name(), asserted_msg=announcement_message),
744
- ),
745
- )
746
-
747
- genesis_launcher_solution = Program.to([full_treasury_puzzle_hash, 1, bytes(0x80)])
748
-
749
- launcher_cs = make_spend(launcher_coin, genesis_launcher_puz, genesis_launcher_solution)
750
- launcher_sb = WalletSpendBundle([launcher_cs], AugSchemeMPL.aggregate([]))
751
-
752
- launcher_proof = LineageProof(
753
- bytes32(launcher_coin.parent_coin_info),
754
- None,
755
- uint64(launcher_coin.amount),
756
- )
757
- await self.add_parent(launcher_coin.name(), launcher_proof)
758
-
759
- eve_coin = Coin(launcher_coin.name(), full_treasury_puzzle_hash, uint64(1))
760
- dao_info = DAOInfo(
761
- launcher_coin.name(),
762
- cat_wallet_id,
763
- self.dao_info.dao_cat_wallet_id,
764
- self.dao_info.proposals_list,
765
- self.dao_info.parent_info,
766
- eve_coin,
767
- dao_treasury_puzzle,
768
- self.dao_info.singleton_block_height,
769
- self.dao_info.filter_below_vote_amount,
770
- self.dao_info.assets,
771
- self.dao_info.current_height,
772
- )
773
- await self.save_info(dao_info)
774
- eve_spend = await self.generate_treasury_eve_spend(dao_treasury_puzzle, eve_coin)
775
- new_spend = WalletSpendBundle.aggregate([launcher_sb, eve_spend])
776
-
777
- treasury_record = TransactionRecord(
778
- confirmed_at_height=uint32(0),
779
- created_at_time=uint64(int(time.time())),
780
- to_puzzle_hash=dao_treasury_puzzle.get_tree_hash(),
781
- amount=uint64(1),
782
- fee_amount=fee,
783
- confirmed=False,
784
- sent=uint32(10),
785
- spend_bundle=new_spend,
786
- additions=new_spend.additions(),
787
- removals=new_spend.removals(),
788
- wallet_id=self.id(),
789
- sent_to=[],
790
- trade_id=None,
791
- type=uint32(TransactionType.INCOMING_TX.value),
792
- name=eve_coin.name(),
793
- memos=[],
794
- valid_times=parse_timelock_info(extra_conditions),
795
- )
796
-
797
- funding_inner_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id)
798
- await self.wallet_state_manager.add_interested_puzzle_hashes([funding_inner_puzhash], [self.id()])
799
- await self.wallet_state_manager.add_interested_puzzle_hashes([launcher_coin.name()], [self.id()])
800
- await self.wallet_state_manager.add_interested_coin_ids([launcher_coin.name()], [self.wallet_id])
801
-
802
- await self.wallet_state_manager.add_interested_coin_ids([eve_coin.name()], [self.wallet_id])
803
- async with action_scope.use() as interface:
804
- interface.side_effects.transactions.append(treasury_record)
805
-
806
- async def generate_treasury_eve_spend(
807
- self, inner_puz: Program, eve_coin: Coin, fee: uint64 = uint64(0)
808
- ) -> WalletSpendBundle:
809
- """
810
- Create the eve spend of the treasury
811
- This can only be completed after a number of blocks > oracle_spend_delay have been farmed
812
- """
813
- if self.dao_info.current_treasury_innerpuz is None: # pragma: no cover
814
- raise ValueError("generate_treasury_eve_spend called with nil self.dao_info.current_treasury_innerpuz")
815
- full_treasury_puzzle = curry_singleton(self.dao_info.treasury_id, inner_puz)
816
- launcher_id, launcher_proof = self.dao_info.parent_info[0]
817
- assert launcher_proof
818
- assert inner_puz
819
- inner_sol = Program.to([0, 0, 0, 0, get_singleton_struct_for_id(launcher_id)])
820
- fullsol = Program.to(
821
- [
822
- launcher_proof.to_program(),
823
- eve_coin.amount,
824
- inner_sol,
825
- ]
826
- )
827
- eve_coin_spend = make_spend(eve_coin, full_treasury_puzzle, fullsol)
828
- eve_spend_bundle = WalletSpendBundle([eve_coin_spend], G2Element())
829
-
830
- next_proof = LineageProof(
831
- eve_coin.parent_coin_info,
832
- inner_puz.get_tree_hash(),
833
- uint64(eve_coin.amount),
834
- )
835
- next_coin = Coin(eve_coin.name(), eve_coin.puzzle_hash, eve_coin.amount)
836
- await self.add_parent(eve_coin.name(), next_proof)
837
- await self.wallet_state_manager.add_interested_coin_ids([next_coin.name()], [self.wallet_id])
838
-
839
- dao_info = dataclasses.replace(self.dao_info, current_treasury_coin=next_coin)
840
- await self.save_info(dao_info)
841
- return eve_spend_bundle
842
-
843
- async def generate_new_proposal(
844
- self,
845
- proposed_puzzle: Program,
846
- action_scope: WalletActionScope,
847
- vote_amount: Optional[uint64] = None,
848
- fee: uint64 = uint64(0),
849
- extra_conditions: tuple[Condition, ...] = tuple(),
850
- ) -> None:
851
- dao_rules = get_treasury_rules_from_puzzle(self.dao_info.current_treasury_innerpuz)
852
- coins = await self.standard_wallet.select_coins(
853
- uint64(fee + dao_rules.proposal_minimum_amount),
854
- action_scope,
855
- )
856
- if coins is None: # pragma: no cover
857
- return None
858
- # origin is normal coin which creates launcher coin
859
- origin = coins.copy().pop()
860
- genesis_launcher_puz = SINGLETON_LAUNCHER
861
- # launcher coin contains singleton launcher, launcher coin ID == singleton_id == treasury_id
862
- launcher_coin = Coin(origin.name(), genesis_launcher_puz.get_tree_hash(), dao_rules.proposal_minimum_amount)
863
-
864
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
865
-
866
- if vote_amount is None: # pragma: no cover
867
- dao_cat_wallet = self.wallet_state_manager.get_wallet(
868
- id=self.dao_info.dao_cat_wallet_id, required_type=DAOCATWallet
869
- )
870
- vote_amount = await dao_cat_wallet.get_votable_balance(include_free_cats=False)
871
- assert vote_amount is not None
872
- cat_tail_hash = cat_wallet.cat_info.limitations_program_hash
873
- assert cat_tail_hash
874
- dao_proposal_puzzle = get_proposal_puzzle(
875
- proposal_id=launcher_coin.name(),
876
- cat_tail_hash=cat_tail_hash,
877
- treasury_id=self.dao_info.treasury_id,
878
- votes_sum=uint64(0),
879
- total_votes=uint64(0),
880
- proposed_puzzle_hash=proposed_puzzle.get_tree_hash(),
881
- )
882
-
883
- full_proposal_puzzle = curry_singleton(launcher_coin.name(), dao_proposal_puzzle)
884
- full_proposal_puzzle_hash = full_proposal_puzzle.get_tree_hash()
885
-
886
- announcement_message = Program.to(
887
- [full_proposal_puzzle_hash, dao_rules.proposal_minimum_amount, bytes(0x80)]
888
- ).get_tree_hash()
889
-
890
- await self.standard_wallet.generate_signed_transaction(
891
- uint64(dao_rules.proposal_minimum_amount),
892
- genesis_launcher_puz.get_tree_hash(),
893
- action_scope,
894
- fee,
895
- origin_id=origin.name(),
896
- coins=coins,
897
- extra_conditions=(
898
- AssertCoinAnnouncement(asserted_id=launcher_coin.name(), asserted_msg=announcement_message),
899
- ),
900
- )
901
-
902
- genesis_launcher_solution = Program.to(
903
- [full_proposal_puzzle_hash, dao_rules.proposal_minimum_amount, bytes(0x80)]
904
- )
905
-
906
- launcher_cs = make_spend(launcher_coin, genesis_launcher_puz, genesis_launcher_solution)
907
- launcher_sb = WalletSpendBundle([launcher_cs], AugSchemeMPL.aggregate([]))
908
- eve_coin = Coin(launcher_coin.name(), full_proposal_puzzle_hash, dao_rules.proposal_minimum_amount)
909
-
910
- future_parent = LineageProof(
911
- eve_coin.parent_coin_info,
912
- dao_proposal_puzzle.get_tree_hash(),
913
- uint64(eve_coin.amount),
914
- )
915
- eve_parent = LineageProof(
916
- bytes32(launcher_coin.parent_coin_info),
917
- bytes32(launcher_coin.puzzle_hash),
918
- uint64(launcher_coin.amount),
919
- )
920
-
921
- await self.add_parent(bytes32(eve_coin.parent_coin_info), eve_parent)
922
- await self.add_parent(eve_coin.name(), future_parent)
923
-
924
- eve_spend = await self.generate_proposal_eve_spend(
925
- eve_coin=eve_coin,
926
- full_proposal_puzzle=full_proposal_puzzle,
927
- dao_proposal_puzzle=dao_proposal_puzzle,
928
- proposed_puzzle_reveal=proposed_puzzle,
929
- launcher_coin=launcher_coin,
930
- vote_amount=vote_amount,
931
- )
932
-
933
- full_spend = WalletSpendBundle.aggregate([eve_spend, launcher_sb])
934
-
935
- async with action_scope.use() as interface:
936
- interface.side_effects.transactions.append(
937
- TransactionRecord(
938
- confirmed_at_height=uint32(0),
939
- created_at_time=uint64(int(time.time())),
940
- to_puzzle_hash=full_proposal_puzzle.get_tree_hash(),
941
- amount=uint64(dao_rules.proposal_minimum_amount),
942
- fee_amount=fee,
943
- confirmed=False,
944
- sent=uint32(10),
945
- spend_bundle=full_spend,
946
- additions=full_spend.additions(),
947
- removals=full_spend.removals(),
948
- wallet_id=self.id(),
949
- sent_to=[],
950
- trade_id=None,
951
- type=uint32(TransactionType.INCOMING_TX.value),
952
- name=full_spend.name(),
953
- memos=[],
954
- valid_times=parse_timelock_info(extra_conditions),
955
- )
956
- )
957
-
958
- async def generate_proposal_eve_spend(
959
- self,
960
- *,
961
- eve_coin: Coin,
962
- full_proposal_puzzle: Program,
963
- dao_proposal_puzzle: Program,
964
- proposed_puzzle_reveal: Program,
965
- launcher_coin: Coin,
966
- vote_amount: uint64,
967
- ) -> WalletSpendBundle:
968
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
969
- cat_tail = cat_wallet.cat_info.limitations_program_hash
970
- dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
971
- self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
972
- )
973
- assert dao_cat_wallet is not None
974
-
975
- dao_cat_spend = await dao_cat_wallet.create_vote_spend(
976
- vote_amount, launcher_coin.name(), True, proposal_puzzle=dao_proposal_puzzle
977
- )
978
- vote_amounts = []
979
- vote_coins = []
980
- previous_votes = []
981
- lockup_inner_puzhashes = []
982
- for spend in dao_cat_spend.coin_spends:
983
- spend_vote_amount = Program.from_bytes(bytes(spend.solution)).at("frrrrrrf").as_int()
984
- vote_amounts.append(spend_vote_amount)
985
- vote_coins.append(spend.coin.name())
986
- previous_votes.append(
987
- get_active_votes_from_lockup_puzzle(
988
- get_innerpuzzle_from_cat_puzzle(Program.from_bytes(bytes(spend.puzzle_reveal)))
989
- )
990
- )
991
- lockup_inner_puz = get_innerpuz_from_lockup_puzzle(
992
- get_innerpuzzle_from_cat_puzzle(Program.from_bytes(bytes(spend.puzzle_reveal)))
993
- )
994
- assert isinstance(lockup_inner_puz, Program)
995
- lockup_inner_puzhashes.append(lockup_inner_puz.get_tree_hash())
996
- inner_sol = Program.to(
997
- [
998
- vote_amounts,
999
- 1,
1000
- vote_coins,
1001
- previous_votes,
1002
- lockup_inner_puzhashes,
1003
- proposed_puzzle_reveal,
1004
- 0,
1005
- 0,
1006
- 0,
1007
- 0,
1008
- eve_coin.amount,
1009
- ]
1010
- )
1011
- # full solution is (lineage_proof my_amount inner_solution)
1012
- fullsol = Program.to(
1013
- [
1014
- [launcher_coin.parent_coin_info, launcher_coin.amount],
1015
- eve_coin.amount,
1016
- inner_sol,
1017
- ]
1018
- )
1019
- list_of_coinspends = [make_spend(eve_coin, full_proposal_puzzle, fullsol)]
1020
- unsigned_spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element())
1021
- return unsigned_spend_bundle.aggregate([unsigned_spend_bundle, dao_cat_spend])
1022
-
1023
- async def generate_proposal_vote_spend(
1024
- self,
1025
- proposal_id: bytes32,
1026
- vote_amount: Optional[uint64],
1027
- is_yes_vote: bool,
1028
- action_scope: WalletActionScope,
1029
- fee: uint64 = uint64(0),
1030
- extra_conditions: tuple[Condition, ...] = tuple(),
1031
- ) -> None:
1032
- self.log.info(f"Trying to create a proposal close spend with ID: {proposal_id}")
1033
- proposal_info = None
1034
- for pi in self.dao_info.proposals_list:
1035
- if pi.proposal_id == proposal_id:
1036
- proposal_info = pi
1037
- break
1038
- if proposal_info is None: # pragma: no cover
1039
- raise ValueError("Unable to find a proposal with that ID.")
1040
- if (proposal_info.timer_coin is None) and (
1041
- proposal_info.current_innerpuz == get_finished_state_puzzle(proposal_info.proposal_id)
1042
- ):
1043
- raise ValueError("This proposal is already closed. Feel free to unlock your coins.") # pragma: no cover
1044
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
1045
- cat_tail = cat_wallet.cat_info.limitations_program_hash
1046
- dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
1047
- self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
1048
- )
1049
- assert dao_cat_wallet is not None
1050
- assert proposal_info.current_innerpuz is not None
1051
-
1052
- if vote_amount is None: # pragma: no cover
1053
- vote_amount = await dao_cat_wallet.get_votable_balance(proposal_id)
1054
- assert vote_amount is not None
1055
- dao_cat_spend = await dao_cat_wallet.create_vote_spend(
1056
- vote_amount, proposal_id, is_yes_vote, proposal_puzzle=proposal_info.current_innerpuz
1057
- )
1058
- vote_amounts = []
1059
- vote_coins = []
1060
- previous_votes = []
1061
- lockup_inner_puzhashes = []
1062
- assert dao_cat_spend is not None
1063
- for spend in dao_cat_spend.coin_spends:
1064
- vote_amounts.append(
1065
- Program.from_bytes(bytes(spend.solution)).at("frrrrrrf")
1066
- ) # this is the vote_amount field of the solution
1067
- vote_coins.append(spend.coin.name())
1068
- previous_votes.append(
1069
- get_active_votes_from_lockup_puzzle(
1070
- get_innerpuzzle_from_cat_puzzle(Program.from_bytes(bytes(spend.puzzle_reveal)))
1071
- )
1072
- )
1073
- lockup_inner_puz = get_innerpuz_from_lockup_puzzle(
1074
- get_innerpuzzle_from_cat_puzzle(Program.from_bytes(bytes(spend.puzzle_reveal)))
1075
- )
1076
- assert isinstance(lockup_inner_puz, Program)
1077
- lockup_inner_puzhashes.append(lockup_inner_puz.get_tree_hash())
1078
- inner_sol = Program.to(
1079
- [
1080
- vote_amounts,
1081
- 1 if is_yes_vote else 0,
1082
- vote_coins,
1083
- previous_votes,
1084
- lockup_inner_puzhashes,
1085
- 0,
1086
- 0,
1087
- 0,
1088
- 0,
1089
- 0,
1090
- proposal_info.current_coin.amount,
1091
- ]
1092
- )
1093
- parent_info = self.get_parent_for_coin(proposal_info.current_coin)
1094
- assert parent_info is not None
1095
- # full solution is (lineage_proof my_amount inner_solution)
1096
- fullsol = Program.to(
1097
- [
1098
- [
1099
- parent_info.parent_name,
1100
- parent_info.inner_puzzle_hash,
1101
- parent_info.amount,
1102
- ],
1103
- proposal_info.current_coin.amount,
1104
- inner_sol,
1105
- ]
1106
- )
1107
- full_proposal_puzzle = curry_singleton(proposal_id, proposal_info.current_innerpuz)
1108
- list_of_coinspends = [
1109
- make_spend(proposal_info.current_coin, full_proposal_puzzle, fullsol),
1110
- *dao_cat_spend.coin_spends,
1111
- ]
1112
- spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element())
1113
- if fee > 0:
1114
- await self.standard_wallet.create_tandem_xch_tx(
1115
- fee,
1116
- action_scope,
1117
- )
1118
-
1119
- record = TransactionRecord(
1120
- confirmed_at_height=uint32(0),
1121
- created_at_time=uint64(int(time.time())),
1122
- to_puzzle_hash=full_proposal_puzzle.get_tree_hash(),
1123
- amount=uint64(1),
1124
- fee_amount=fee,
1125
- confirmed=False,
1126
- sent=uint32(10),
1127
- spend_bundle=spend_bundle,
1128
- additions=spend_bundle.additions(),
1129
- removals=spend_bundle.removals(),
1130
- wallet_id=self.id(),
1131
- sent_to=[],
1132
- trade_id=None,
1133
- type=uint32(TransactionType.INCOMING_TX.value),
1134
- name=spend_bundle.name(),
1135
- memos=[],
1136
- valid_times=parse_timelock_info(extra_conditions),
1137
- )
1138
- async with action_scope.use() as interface:
1139
- interface.side_effects.transactions.append(record)
1140
-
1141
- async def create_proposal_close_spend(
1142
- self,
1143
- proposal_id: bytes32,
1144
- action_scope: WalletActionScope,
1145
- genesis_id: Optional[bytes32] = None,
1146
- fee: uint64 = uint64(0),
1147
- self_destruct: bool = False,
1148
- extra_conditions: tuple[Condition, ...] = tuple(),
1149
- ) -> None:
1150
- self.log.info(f"Trying to create a proposal close spend with ID: {proposal_id}")
1151
- proposal_info = None
1152
- for pi in self.dao_info.proposals_list:
1153
- if pi.proposal_id == proposal_id:
1154
- proposal_info = pi
1155
- break
1156
- if proposal_info is None: # pragma: no cover
1157
- raise ValueError("Unable to find a proposal with that ID.")
1158
- if proposal_info.timer_coin is None: # pragma: no cover
1159
- raise ValueError("This proposal is already closed. Feel free to unlock your coins.")
1160
- assert self.dao_info.current_treasury_innerpuz is not None
1161
- curried_args = uncurry_treasury(self.dao_info.current_treasury_innerpuz)
1162
- (
1163
- _DAO_TREASURY_MOD_HASH,
1164
- proposal_validator,
1165
- proposal_timelock,
1166
- soft_close_length,
1167
- attendance_required,
1168
- pass_percentage,
1169
- self_destruct_length,
1170
- oracle_spend_delay,
1171
- ) = curried_args
1172
- proposal_state = await self.get_proposal_state(proposal_id)
1173
- if not proposal_state["closable"]: # pragma: no cover
1174
- raise ValueError(f"This proposal is not ready to be closed. proposal_id: {proposal_id}")
1175
- if proposal_state["passed"]:
1176
- self.log.info(f"Closing passed proposal: {proposal_id}")
1177
- else:
1178
- self.log.info(f"Closing failed proposal: {proposal_id}")
1179
- assert proposal_info.current_innerpuz is not None
1180
- full_proposal_puzzle = curry_singleton(proposal_id, proposal_info.current_innerpuz)
1181
- assert proposal_info.current_coin.puzzle_hash == full_proposal_puzzle.get_tree_hash()
1182
- solution = Program.to(
1183
- [
1184
- proposal_validator.get_tree_hash(),
1185
- 0,
1186
- proposal_timelock,
1187
- pass_percentage,
1188
- attendance_required,
1189
- 0,
1190
- soft_close_length,
1191
- self_destruct_length,
1192
- oracle_spend_delay,
1193
- 1 if self_destruct else 0,
1194
- ]
1195
- )
1196
- parent_info = self.get_parent_for_coin(proposal_info.current_coin)
1197
- assert parent_info is not None
1198
- fullsol = Program.to(
1199
- [
1200
- [
1201
- parent_info.parent_name,
1202
- parent_info.inner_puzzle_hash,
1203
- parent_info.amount,
1204
- ],
1205
- proposal_info.current_coin.amount,
1206
- solution,
1207
- ]
1208
- )
1209
- proposal_cs = make_spend(proposal_info.current_coin, full_proposal_puzzle, fullsol)
1210
- if not self_destruct:
1211
- timer_puzzle = get_proposal_timer_puzzle(
1212
- self.get_cat_tail_hash(),
1213
- proposal_info.proposal_id,
1214
- self.dao_info.treasury_id,
1215
- )
1216
- c_a, curried_args_prg = uncurry_proposal(proposal_info.current_innerpuz)
1217
- (
1218
- _SELF_HASH,
1219
- _PROPOSAL_ID,
1220
- PROPOSED_PUZ_HASH,
1221
- YES_VOTES,
1222
- TOTAL_VOTES,
1223
- ) = c_a.as_iter()
1224
-
1225
- if TOTAL_VOTES.as_int() < attendance_required.as_int(): # pragma: no cover
1226
- raise ValueError("Unable to pass this proposal as it has not met the minimum vote attendance.")
1227
- timer_solution = Program.to(
1228
- [
1229
- YES_VOTES,
1230
- TOTAL_VOTES,
1231
- PROPOSED_PUZ_HASH,
1232
- proposal_timelock,
1233
- proposal_id,
1234
- proposal_info.current_coin.amount,
1235
- ]
1236
- )
1237
- timer_cs = make_spend(proposal_info.timer_coin, timer_puzzle, timer_solution)
1238
-
1239
- full_treasury_puz = curry_singleton(self.dao_info.treasury_id, self.dao_info.current_treasury_innerpuz)
1240
- assert isinstance(self.dao_info.current_treasury_coin, Coin)
1241
- assert full_treasury_puz.get_tree_hash() == self.dao_info.current_treasury_coin.puzzle_hash
1242
-
1243
- cat_spend_bundle = None
1244
- delegated_puzzle_sb = None
1245
- puzzle_reveal = await self.fetch_proposed_puzzle_reveal(proposal_id)
1246
- if proposal_state["passed"] and not self_destruct:
1247
- validator_solution = Program.to(
1248
- [
1249
- proposal_id,
1250
- TOTAL_VOTES,
1251
- YES_VOTES,
1252
- proposal_info.current_coin.parent_coin_info,
1253
- proposal_info.current_coin.amount,
1254
- ]
1255
- )
1256
-
1257
- proposal_type, curried_args_prg = get_proposal_args(puzzle_reveal)
1258
- if proposal_type == ProposalType.SPEND:
1259
- (
1260
- _TREASURY_SINGLETON_STRUCT,
1261
- _CAT_MOD_HASH,
1262
- CONDITIONS,
1263
- LIST_OF_TAILHASH_CONDITIONS,
1264
- _P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH,
1265
- ) = curried_args_prg.as_iter()
1266
-
1267
- sum = 0
1268
- coin_spends = []
1269
- xch_parent_amount_list = []
1270
- tailhash_parent_amount_list = []
1271
- treasury_inner_puzhash = self.dao_info.current_treasury_innerpuz.get_tree_hash()
1272
- p2_singleton_puzzle = get_p2_singleton_puzzle(self.dao_info.treasury_id)
1273
- cat_launcher = create_cat_launcher_for_singleton_id(self.dao_info.treasury_id)
1274
-
1275
- # handle CAT minting
1276
- for cond in CONDITIONS.as_iter():
1277
- if cond.first().as_int() == 51:
1278
- if cond.rest().first().as_atom() == cat_launcher.get_tree_hash():
1279
- cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
1280
- cat_tail_hash = cat_wallet.cat_info.limitations_program_hash
1281
- mint_amount = uint64(cond.rest().rest().first().as_int())
1282
- new_cat_puzhash = bytes32(cond.rest().rest().rest().first().first().as_atom())
1283
- eve_puzzle = curry_cat_eve(new_cat_puzhash)
1284
- if genesis_id is None:
1285
- tail_reconstruction = cat_wallet.cat_info.my_tail
1286
- else: # pragma: no cover
1287
- tail_reconstruction = generate_cat_tail(genesis_id, self.dao_info.treasury_id)
1288
- assert tail_reconstruction is not None
1289
- assert tail_reconstruction.get_tree_hash() == cat_tail_hash
1290
- assert isinstance(self.dao_info.current_treasury_coin, Coin)
1291
- cat_launcher_coin = Coin(
1292
- self.dao_info.current_treasury_coin.name(),
1293
- cat_launcher.get_tree_hash(),
1294
- uint64(mint_amount),
1295
- )
1296
- full_puz = construct_cat_puzzle(CAT_MOD, cat_tail_hash, eve_puzzle)
1297
-
1298
- solution = Program.to(
1299
- [
1300
- treasury_inner_puzhash,
1301
- self.dao_info.current_treasury_coin.parent_coin_info,
1302
- full_puz.get_tree_hash(),
1303
- mint_amount,
1304
- ]
1305
- )
1306
- coin_spends.append(make_spend(cat_launcher_coin, cat_launcher, solution))
1307
- eve_coin = Coin(cat_launcher_coin.name(), full_puz.get_tree_hash(), uint64(mint_amount))
1308
- tail_solution = Program.to([cat_launcher_coin.parent_coin_info, cat_launcher_coin.amount])
1309
- solution = Program.to([mint_amount, tail_reconstruction, tail_solution])
1310
- new_spendable_cat = SpendableCAT(
1311
- eve_coin,
1312
- cat_tail_hash,
1313
- eve_puzzle,
1314
- solution,
1315
- )
1316
- if cat_spend_bundle is None:
1317
- cat_spend_bundle = unsigned_spend_bundle_for_spendable_cats(
1318
- CAT_MOD, [new_spendable_cat]
1319
- )
1320
- else: # pragma: no cover
1321
- cat_spend_bundle = cat_spend_bundle.aggregate(
1322
- [
1323
- cat_spend_bundle,
1324
- unsigned_spend_bundle_for_spendable_cats(CAT_MOD, [new_spendable_cat]),
1325
- ]
1326
- )
1327
-
1328
- for condition_statement in CONDITIONS.as_iter():
1329
- if condition_statement.first().as_int() == 51:
1330
- sum += condition_statement.rest().rest().first().as_int()
1331
- if sum > 0:
1332
- xch_coins = await self.select_coins_for_asset_type(uint64(sum), action_scope)
1333
- for xch_coin in xch_coins:
1334
- xch_parent_amount_list.append([xch_coin.parent_coin_info, xch_coin.amount])
1335
- solution = Program.to(
1336
- [
1337
- 0,
1338
- treasury_inner_puzhash,
1339
- 0,
1340
- 0,
1341
- xch_coin.name(),
1342
- ]
1343
- )
1344
- coin_spends.append(make_spend(xch_coin, p2_singleton_puzzle, solution))
1345
- delegated_puzzle_sb = WalletSpendBundle(coin_spends, AugSchemeMPL.aggregate([]))
1346
- for tail_hash_conditions_pair in LIST_OF_TAILHASH_CONDITIONS.as_iter():
1347
- tail_hash = bytes32(tail_hash_conditions_pair.first().as_atom())
1348
- conditions: Program = tail_hash_conditions_pair.rest().first()
1349
- sum_of_conditions = 0
1350
- sum_of_coins = 0
1351
- spendable_cat_list = []
1352
- for condition in conditions.as_iter():
1353
- if condition.first().as_int() == 51:
1354
- sum_of_conditions += condition.rest().rest().first().as_int()
1355
- cat_coins = await self.select_coins_for_asset_type(
1356
- uint64(sum_of_conditions), action_scope, tail_hash
1357
- )
1358
- parent_amount_list = []
1359
- for cat_coin in cat_coins:
1360
- sum_of_coins += cat_coin.amount
1361
- parent_amount_list.append([cat_coin.parent_coin_info, cat_coin.amount])
1362
- lineage_proof = await self.fetch_cat_lineage_proof(cat_coin)
1363
- if cat_coin == cat_coins[-1]: # the last coin is the one that makes the conditions
1364
- if sum_of_coins - sum_of_conditions > 0:
1365
- p2_singleton_puzhash = p2_singleton_puzzle.get_tree_hash()
1366
- change_condition = Program.to(
1367
- [
1368
- 51,
1369
- p2_singleton_puzhash,
1370
- sum_of_coins - sum_of_conditions,
1371
- [p2_singleton_puzhash],
1372
- ]
1373
- )
1374
- delegated_puzzle = Program.to((1, change_condition.cons(conditions)))
1375
- else: # pragma: no cover
1376
- delegated_puzzle = Program.to((1, conditions))
1377
-
1378
- solution = Program.to(
1379
- [
1380
- 0,
1381
- treasury_inner_puzhash,
1382
- delegated_puzzle,
1383
- 0,
1384
- cat_coin.name(),
1385
- ]
1386
- )
1387
- else:
1388
- solution = Program.to(
1389
- [
1390
- 0,
1391
- treasury_inner_puzhash,
1392
- 0,
1393
- 0,
1394
- cat_coin.name(),
1395
- ]
1396
- )
1397
- new_spendable_cat = SpendableCAT(
1398
- cat_coin,
1399
- tail_hash,
1400
- p2_singleton_puzzle,
1401
- solution,
1402
- lineage_proof=lineage_proof,
1403
- )
1404
- spendable_cat_list.append(new_spendable_cat)
1405
- # create or merge with other CAT spends
1406
- if cat_spend_bundle is None:
1407
- cat_spend_bundle = unsigned_spend_bundle_for_spendable_cats(CAT_MOD, spendable_cat_list)
1408
- else:
1409
- cat_spend_bundle = cat_spend_bundle.aggregate(
1410
- [cat_spend_bundle, unsigned_spend_bundle_for_spendable_cats(CAT_MOD, spendable_cat_list)]
1411
- )
1412
- tailhash_parent_amount_list.append([tail_hash, parent_amount_list])
1413
-
1414
- delegated_solution = Program.to(
1415
- [
1416
- xch_parent_amount_list,
1417
- tailhash_parent_amount_list,
1418
- treasury_inner_puzhash,
1419
- ]
1420
- )
1421
-
1422
- elif proposal_type == ProposalType.UPDATE:
1423
- (
1424
- _TREASURY_MOD_HASH,
1425
- _VALIDATOR_MOD_HASH,
1426
- _SINGLETON_STRUCT,
1427
- _PROPOSAL_SELF_HASH,
1428
- _PROPOSAL_MINIMUM_AMOUNT,
1429
- _PROPOSAL_EXCESS_PAYOUT_PUZHASH,
1430
- _PROPOSAL_LENGTH,
1431
- _PROPOSAL_SOFTCLOSE_LENGTH,
1432
- _ATTENDANCE_REQUIRED,
1433
- _PASS_MARGIN,
1434
- _PROPOSAL_SELF_DESTRUCT_TIME,
1435
- _ORACLE_SPEND_DELAY,
1436
- ) = curried_args_prg.as_iter()
1437
- coin_spends = []
1438
- treasury_inner_puzhash = self.dao_info.current_treasury_innerpuz.get_tree_hash()
1439
- delegated_solution = Program.to([])
1440
-
1441
- else:
1442
- raise Exception(f"Unknown proposal type: {proposal_type!r}")
1443
-
1444
- treasury_solution = Program.to(
1445
- [
1446
- [proposal_info.current_coin.name(), PROPOSED_PUZ_HASH.as_atom(), 0],
1447
- validator_solution,
1448
- puzzle_reveal,
1449
- delegated_solution,
1450
- ]
1451
- )
1452
- else:
1453
- treasury_solution = Program.to([0, 0, 0, 0, 0, 0])
1454
-
1455
- assert self.dao_info.current_treasury_coin is not None
1456
- parent_info = self.get_parent_for_coin(self.dao_info.current_treasury_coin)
1457
- assert parent_info is not None
1458
- full_treasury_solution = Program.to(
1459
- [
1460
- [
1461
- parent_info.parent_name,
1462
- parent_info.inner_puzzle_hash,
1463
- parent_info.amount,
1464
- ],
1465
- self.dao_info.current_treasury_coin.amount,
1466
- treasury_solution,
1467
- ]
1468
- )
1469
-
1470
- treasury_cs = make_spend(self.dao_info.current_treasury_coin, full_treasury_puz, full_treasury_solution)
1471
-
1472
- if self_destruct:
1473
- spend_bundle = WalletSpendBundle([proposal_cs, treasury_cs], AugSchemeMPL.aggregate([]))
1474
- else:
1475
- # TODO: maybe we can refactor this to provide clarity around timer_cs having been defined
1476
- spend_bundle = WalletSpendBundle([proposal_cs, timer_cs, treasury_cs], AugSchemeMPL.aggregate([]))
1477
- if fee > 0:
1478
- await self.standard_wallet.create_tandem_xch_tx(fee, action_scope)
1479
- full_spend = spend_bundle
1480
- if cat_spend_bundle is not None:
1481
- full_spend = full_spend.aggregate([full_spend, cat_spend_bundle])
1482
- if delegated_puzzle_sb is not None:
1483
- full_spend = full_spend.aggregate([full_spend, delegated_puzzle_sb])
1484
-
1485
- record = TransactionRecord(
1486
- confirmed_at_height=uint32(0),
1487
- created_at_time=uint64(int(time.time())),
1488
- to_puzzle_hash=get_finished_state_puzzle(proposal_info.proposal_id).get_tree_hash(),
1489
- amount=uint64(1),
1490
- fee_amount=fee,
1491
- confirmed=False,
1492
- sent=uint32(10),
1493
- spend_bundle=full_spend,
1494
- additions=full_spend.additions(),
1495
- removals=full_spend.removals(),
1496
- wallet_id=self.id(),
1497
- sent_to=[],
1498
- trade_id=None,
1499
- type=uint32(TransactionType.INCOMING_TX.value),
1500
- name=full_spend.name(),
1501
- memos=[],
1502
- valid_times=parse_timelock_info(extra_conditions),
1503
- )
1504
- async with action_scope.use() as interface:
1505
- interface.side_effects.transactions.append(record)
1506
-
1507
- async def fetch_proposed_puzzle_reveal(self, proposal_id: bytes32) -> Program:
1508
- wallet_node: Any = self.wallet_state_manager.wallet_node
1509
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
1510
- if peer is None: # pragma: no cover
1511
- raise ValueError("Could not find any peers to request puzzle and solution from")
1512
- # The proposal_id is launcher coin, so proposal_id's child is eve and the eve spend contains the reveal
1513
- children = await wallet_node.fetch_children(proposal_id, peer)
1514
- eve_state = children[0]
1515
-
1516
- eve_spend = await fetch_coin_spend(eve_state.created_height, eve_state.coin, peer)
1517
- puzzle_reveal = get_proposed_puzzle_reveal_from_solution(eve_spend.solution.to_program())
1518
- return puzzle_reveal
1519
-
1520
- async def fetch_cat_lineage_proof(self, cat_coin: Coin) -> LineageProof:
1521
- wallet_node: Any = self.wallet_state_manager.wallet_node
1522
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
1523
- if peer is None: # pragma: no cover
1524
- raise ValueError("Could not find any peers to request puzzle and solution from")
1525
- state = await wallet_node.get_coin_state([cat_coin.parent_coin_info], peer)
1526
- assert state is not None
1527
- # CoinState contains Coin, spent_height, and created_height,
1528
- parent_spend = await fetch_coin_spend(state[0].spent_height, state[0].coin, peer)
1529
- parent_inner_puz = get_innerpuzzle_from_cat_puzzle(parent_spend.puzzle_reveal.to_program())
1530
- return LineageProof(state[0].coin.parent_coin_info, parent_inner_puz.get_tree_hash(), state[0].coin.amount)
1531
-
1532
- async def _create_treasury_fund_transaction(
1533
- self,
1534
- funding_wallet: WalletProtocol[Any],
1535
- amount: uint64,
1536
- action_scope: WalletActionScope,
1537
- fee: uint64 = uint64(0),
1538
- extra_conditions: tuple[Condition, ...] = tuple(),
1539
- ) -> None:
1540
- if funding_wallet.type() == WalletType.STANDARD_WALLET.value:
1541
- p2_singleton_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
1542
- wallet: Wallet = funding_wallet # type: ignore[assignment]
1543
- await wallet.generate_signed_transaction(
1544
- amount,
1545
- p2_singleton_puzhash,
1546
- action_scope,
1547
- fee=fee,
1548
- memos=[p2_singleton_puzhash],
1549
- )
1550
- elif funding_wallet.type() == WalletType.CAT.value:
1551
- cat_wallet: CATWallet = funding_wallet # type: ignore[assignment]
1552
- # generate_signed_transaction has a different type signature in Wallet and CATWallet
1553
- # CATWallet uses a List of amounts and a List of puzhashes as the first two arguments
1554
- p2_singleton_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id)
1555
- await cat_wallet.generate_signed_transaction(
1556
- [amount],
1557
- [p2_singleton_puzhash],
1558
- action_scope,
1559
- fee=fee,
1560
- extra_conditions=extra_conditions,
1561
- )
1562
- else: # pragma: no cover
1563
- raise ValueError(f"Assets of type {funding_wallet.type()} are not currently supported.")
1564
-
1565
- async def create_add_funds_to_treasury_spend(
1566
- self,
1567
- amount: uint64,
1568
- action_scope: WalletActionScope,
1569
- fee: uint64 = uint64(0),
1570
- funding_wallet_id: uint32 = uint32(1),
1571
- extra_conditions: tuple[Condition, ...] = tuple(),
1572
- ) -> None:
1573
- # set up the p2_singleton
1574
- funding_wallet = self.wallet_state_manager.wallets[funding_wallet_id]
1575
- await self._create_treasury_fund_transaction(
1576
- funding_wallet, amount, action_scope, fee, extra_conditions=extra_conditions
1577
- )
1578
-
1579
- async def fetch_singleton_lineage_proof(self, coin: Coin) -> LineageProof:
1580
- wallet_node: Any = self.wallet_state_manager.wallet_node
1581
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
1582
- if peer is None: # pragma: no cover
1583
- raise ValueError("Could not find any peers to request puzzle and solution from")
1584
- state = await wallet_node.get_coin_state([coin.parent_coin_info], peer)
1585
- assert state is not None
1586
- # CoinState contains Coin, spent_height, and created_height,
1587
- parent_spend = await fetch_coin_spend(state[0].spent_height, state[0].coin, peer)
1588
- parent_inner_puz = get_inner_puzzle_from_singleton(parent_spend.puzzle_reveal)
1589
- assert isinstance(parent_inner_puz, Program)
1590
- return LineageProof(state[0].coin.parent_coin_info, parent_inner_puz.get_tree_hash(), state[0].coin.amount)
1591
-
1592
- async def free_coins_from_finished_proposals(
1593
- self,
1594
- action_scope: WalletActionScope,
1595
- fee: uint64 = uint64(0),
1596
- extra_conditions: tuple[Condition, ...] = tuple(),
1597
- ) -> None:
1598
- dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
1599
- spends = []
1600
- closed_list = []
1601
- finished_puz = None
1602
- for proposal_info in self.dao_info.proposals_list:
1603
- if proposal_info.closed:
1604
- closed_list.append(proposal_info.proposal_id)
1605
- inner_solution = Program.to(
1606
- [
1607
- proposal_info.current_coin.amount,
1608
- ]
1609
- )
1610
- lineage_proof: LineageProof = await self.fetch_singleton_lineage_proof(proposal_info.current_coin)
1611
- solution = Program.to([lineage_proof.to_program(), proposal_info.current_coin.amount, inner_solution])
1612
- finished_puz = get_finished_state_puzzle(proposal_info.proposal_id)
1613
- cs = make_spend(proposal_info.current_coin, finished_puz, solution)
1614
- prop_sb = WalletSpendBundle([cs], AugSchemeMPL.aggregate([]))
1615
- spends.append(prop_sb)
1616
-
1617
- sb = await dao_cat_wallet.remove_active_proposal(closed_list, action_scope=action_scope)
1618
- spends.append(sb)
1619
-
1620
- if not spends: # pragma: no cover
1621
- raise ValueError("No proposals are available for release")
1622
-
1623
- full_spend = WalletSpendBundle.aggregate(spends)
1624
- if fee > 0:
1625
- await self.standard_wallet.create_tandem_xch_tx(fee, action_scope)
1626
-
1627
- assert isinstance(finished_puz, Program)
1628
- record = TransactionRecord(
1629
- confirmed_at_height=uint32(0),
1630
- created_at_time=uint64(int(time.time())),
1631
- to_puzzle_hash=finished_puz.get_tree_hash(),
1632
- amount=uint64(1),
1633
- fee_amount=fee,
1634
- confirmed=False,
1635
- sent=uint32(10),
1636
- spend_bundle=full_spend,
1637
- additions=full_spend.additions(),
1638
- removals=full_spend.removals(),
1639
- wallet_id=self.id(),
1640
- sent_to=[],
1641
- trade_id=None,
1642
- type=uint32(TransactionType.INCOMING_TX.value),
1643
- name=full_spend.name(),
1644
- memos=[],
1645
- valid_times=parse_timelock_info(extra_conditions),
1646
- )
1647
- async with action_scope.use() as interface:
1648
- interface.side_effects.transactions.append(record)
1649
-
1650
- async def parse_proposal(self, proposal_id: bytes32) -> dict[str, Any]:
1651
- for prop_info in self.dao_info.proposals_list:
1652
- if prop_info.proposal_id == proposal_id:
1653
- state = await self.get_proposal_state(proposal_id)
1654
- proposed_puzzle_reveal = await self.fetch_proposed_puzzle_reveal(proposal_id)
1655
- proposal_type, curried_args = get_proposal_args(proposed_puzzle_reveal)
1656
- if proposal_type == ProposalType.SPEND:
1657
- cat_launcher = create_cat_launcher_for_singleton_id(self.dao_info.treasury_id)
1658
- (
1659
- _TREASURY_SINGLETON_STRUCT,
1660
- _CAT_MOD_HASH,
1661
- CONDITIONS,
1662
- LIST_OF_TAILHASH_CONDITIONS,
1663
- _P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH,
1664
- ) = curried_args.as_iter()
1665
- mint_amount = None
1666
- new_cat_puzhash = None
1667
- xch_created_coins = []
1668
- for cond in CONDITIONS.as_iter():
1669
- if cond.first().as_int() == 51:
1670
- if cond.rest().first().as_atom() == cat_launcher.get_tree_hash():
1671
- mint_amount = cond.rest().rest().first().as_int()
1672
- new_cat_puzhash = cond.rest().rest().rest().first().first().as_atom()
1673
- else:
1674
- cc = {"puzzle_hash": cond.at("rf").as_atom(), "amount": cond.at("rrf").as_int()}
1675
- xch_created_coins.append(cc)
1676
-
1677
- asset_create_coins: list[dict[Any, Any]] = []
1678
- for asset in LIST_OF_TAILHASH_CONDITIONS.as_iter():
1679
- if asset == Program.to(0): # pragma: no cover
1680
- asset_dict: Optional[dict[str, Any]] = None
1681
- else:
1682
- asset_id = asset.first().as_atom()
1683
- cc_list = []
1684
- for cond in asset.rest().first().as_iter():
1685
- if cond.first().as_int() == 51:
1686
- asset_dict = {
1687
- "puzzle_hash": cond.at("rf").as_atom(),
1688
- "amount": cond.at("rrf").as_int(),
1689
- }
1690
- # cc_list.append([asset_id, asset_dict])
1691
- cc_list.append(asset_dict)
1692
- asset_create_coins.append({"asset_id": asset_id, "conditions": cc_list})
1693
- dictionary: dict[str, Any] = {
1694
- "state": state,
1695
- "proposal_type": proposal_type.value,
1696
- "proposed_puzzle_reveal": proposed_puzzle_reveal,
1697
- "xch_conditions": xch_created_coins,
1698
- "asset_conditions": asset_create_coins,
1699
- }
1700
- if mint_amount is not None and new_cat_puzhash is not None:
1701
- dictionary["mint_amount"] = mint_amount
1702
- dictionary["new_cat_puzhash"] = new_cat_puzhash
1703
- elif proposal_type == ProposalType.UPDATE:
1704
- dao_rules = get_dao_rules_from_update_proposal(proposed_puzzle_reveal)
1705
- dictionary = {
1706
- "state": state,
1707
- "proposal_type": proposal_type.value,
1708
- "dao_rules": dao_rules,
1709
- }
1710
- return dictionary
1711
- raise ValueError(f"Unable to find proposal with id: {proposal_id.hex()}") # pragma: no cover
1712
-
1713
- async def add_parent(self, name: bytes32, parent: Optional[LineageProof]) -> None:
1714
- self.log.info(f"Adding parent {name}: {parent}")
1715
- current_list = self.dao_info.parent_info.copy()
1716
- current_list.append((name, parent))
1717
- dao_info: DAOInfo = DAOInfo(
1718
- self.dao_info.treasury_id,
1719
- self.dao_info.cat_wallet_id,
1720
- self.dao_info.dao_cat_wallet_id,
1721
- self.dao_info.proposals_list,
1722
- current_list,
1723
- self.dao_info.current_treasury_coin,
1724
- self.dao_info.current_treasury_innerpuz,
1725
- self.dao_info.singleton_block_height,
1726
- self.dao_info.filter_below_vote_amount,
1727
- self.dao_info.assets,
1728
- self.dao_info.current_height,
1729
- )
1730
- await self.save_info(dao_info)
1731
-
1732
- async def save_info(self, dao_info: DAOInfo) -> None:
1733
- self.dao_info = dao_info
1734
- current_info = self.wallet_info
1735
- data_str = json.dumps(dao_info.to_json_dict())
1736
- wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
1737
- self.wallet_info = wallet_info
1738
- await self.wallet_state_manager.user_store.update_wallet(wallet_info)
1739
-
1740
- def generate_wallet_name(self) -> str:
1741
- """
1742
- Generate a new DAO wallet name
1743
- :return: wallet name
1744
- """
1745
- max_num = 0
1746
- for wallet in self.wallet_state_manager.wallets.values():
1747
- if wallet.type() == WalletType.DAO: # pragma: no cover
1748
- matched = re.search(r"^Profile (\d+)$", wallet.wallet_info.name) # TODO: bug: wallet.wallet_info
1749
- if matched and int(matched.group(1)) > max_num:
1750
- max_num = int(matched.group(1))
1751
- return f"Profile {max_num + 1}"
1752
-
1753
- def require_derivation_paths(self) -> bool:
1754
- return False
1755
-
1756
- def get_cat_wallet_id(self) -> uint32:
1757
- return self.dao_info.cat_wallet_id
1758
-
1759
- async def enter_dao_cat_voting_mode(
1760
- self,
1761
- amount: uint64,
1762
- action_scope: WalletActionScope,
1763
- ) -> list[TransactionRecord]:
1764
- dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
1765
- return await dao_cat_wallet.enter_dao_cat_voting_mode(amount, action_scope)
1766
-
1767
- @staticmethod
1768
- def get_next_interesting_coin(spend: CoinSpend) -> Optional[Coin]: # pragma: no cover
1769
- # CoinSpend of one of the coins that we cared about. This coin was spent in a block, but might be in a reorg
1770
- # If we return a value, it is a coin that we are also interested in (to support two transitions per block)
1771
- return get_most_recent_singleton_coin_from_coin_spend(spend)
1772
-
1773
- async def add_or_update_proposal_info(
1774
- self,
1775
- new_state: CoinSpend,
1776
- block_height: uint32,
1777
- ) -> None:
1778
- new_dao_info = copy.copy(self.dao_info)
1779
- puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
1780
- if puzzle is None: # pragma: no cover
1781
- raise ValueError("get_innerpuzzle_from_puzzle failed")
1782
- solution = (
1783
- Program.from_bytes(bytes(new_state.solution)).rest().rest().first()
1784
- ) # get proposal solution from full singleton solution
1785
- singleton_id = singleton.get_singleton_id_from_puzzle(new_state.puzzle_reveal)
1786
- if singleton_id is None: # pragma: no cover
1787
- raise ValueError("get_singleton_id_from_puzzle failed")
1788
- ended = False
1789
- dao_rules = get_treasury_rules_from_puzzle(self.dao_info.current_treasury_innerpuz)
1790
- current_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
1791
- if current_coin is None: # pragma: no cover
1792
- raise ValueError("get_most_recent_singleton_coin_from_coin_spend failed")
1793
-
1794
- current_innerpuz = get_new_puzzle_from_proposal_solution(puzzle, solution)
1795
- assert isinstance(current_innerpuz, Program)
1796
- assert current_coin.puzzle_hash == curry_singleton(singleton_id, current_innerpuz).get_tree_hash()
1797
- # check if our parent puzzle was the finished state
1798
- if puzzle.uncurry()[0] == DAO_FINISHED_STATE:
1799
- ended = True
1800
- index = 0
1801
- for current_info in new_dao_info.proposals_list:
1802
- # Search for current proposal_info
1803
- if current_info.proposal_id == singleton_id:
1804
- new_proposal_info = ProposalInfo(
1805
- singleton_id,
1806
- puzzle,
1807
- current_info.amount_voted,
1808
- current_info.yes_votes,
1809
- current_coin,
1810
- current_innerpuz,
1811
- current_info.timer_coin,
1812
- block_height,
1813
- current_info.passed,
1814
- ended,
1815
- )
1816
- new_dao_info.proposals_list[index] = new_proposal_info
1817
- await self.save_info(new_dao_info)
1818
- future_parent = LineageProof(
1819
- new_state.coin.parent_coin_info,
1820
- puzzle.get_tree_hash(),
1821
- uint64(new_state.coin.amount),
1822
- )
1823
- await self.add_parent(new_state.coin.name(), future_parent)
1824
- return
1825
- index += 1
1826
-
1827
- # check if we are the finished state
1828
- if current_innerpuz == get_finished_state_inner_puzzle(singleton_id):
1829
- ended = True
1830
-
1831
- c_a, curried_args = uncurry_proposal(puzzle)
1832
- (
1833
- _DAO_PROPOSAL_TIMER_MOD_HASH,
1834
- _SINGLETON_MOD_HASH,
1835
- _SINGLETON_LAUNCHER_PUZHASH,
1836
- _CAT_MOD_HASH,
1837
- _DAO_FINISHED_STATE_HASH,
1838
- _DAO_TREASURY_MOD_HASH,
1839
- _lockup_self_hash,
1840
- cat_tail_hash,
1841
- _treasury_id,
1842
- ) = curried_args.as_iter()
1843
- (
1844
- _curry_one,
1845
- _proposal_id,
1846
- _proposed_puzzle_hash,
1847
- yes_votes,
1848
- total_votes,
1849
- ) = c_a.as_iter()
1850
-
1851
- if current_coin is None: # pragma: no cover
1852
- raise RuntimeError(f"get_most_recent_singleton_coin_from_coin_spend({new_state}) failed")
1853
-
1854
- timer_coin = None
1855
- if solution.at("rrrrrrf").as_int() == 0:
1856
- # we need to add the vote amounts from the solution to get accurate totals
1857
- is_yes_vote = solution.at("rf").as_int()
1858
- votes_added = 0
1859
- for vote_amount in solution.first().as_iter():
1860
- votes_added += vote_amount.as_int()
1861
- else:
1862
- # If we have entered the finished state
1863
- # TODO: we need to alert the user that they can free up their coins
1864
- is_yes_vote = 0
1865
- votes_added = 0
1866
-
1867
- if current_coin.amount < dao_rules.proposal_minimum_amount and not ended: # pragma: no cover
1868
- raise ValueError("this coin does not meet the minimum requirements and can be ignored")
1869
- new_total_votes = total_votes.as_int() + votes_added
1870
- if new_total_votes < self.dao_info.filter_below_vote_amount: # pragma: no cover
1871
- return # ignore all proposals below the filter amount
1872
-
1873
- if is_yes_vote == 1:
1874
- new_yes_votes = yes_votes.as_int() + votes_added
1875
- else:
1876
- new_yes_votes = yes_votes.as_int()
1877
-
1878
- required_yes_votes = (self.dao_rules.attendance_required * self.dao_rules.pass_percentage) // 10000
1879
- yes_votes_needed = max(0, required_yes_votes - new_yes_votes)
1880
-
1881
- passed = True if yes_votes_needed == 0 else False
1882
-
1883
- index = 0
1884
- for current_info in new_dao_info.proposals_list:
1885
- # Search for current proposal_info
1886
- if current_info.proposal_id == singleton_id:
1887
- # If we are receiving a voting spend update
1888
- new_proposal_info = ProposalInfo(
1889
- singleton_id,
1890
- puzzle,
1891
- uint64(new_total_votes),
1892
- uint64(new_yes_votes),
1893
- current_coin,
1894
- current_innerpuz,
1895
- current_info.timer_coin,
1896
- block_height,
1897
- passed,
1898
- ended,
1899
- )
1900
- new_dao_info.proposals_list[index] = new_proposal_info
1901
- await self.save_info(new_dao_info)
1902
- future_parent = LineageProof(
1903
- new_state.coin.parent_coin_info,
1904
- puzzle.get_tree_hash(),
1905
- uint64(new_state.coin.amount),
1906
- )
1907
- await self.add_parent(new_state.coin.name(), future_parent)
1908
- return
1909
- index += 1
1910
-
1911
- # Search for the timer coin
1912
- if not ended:
1913
- wallet_node: Any = self.wallet_state_manager.wallet_node
1914
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
1915
- if peer is None: # pragma: no cover
1916
- raise ValueError("Could not find any peers to request puzzle and solution from")
1917
- children = await wallet_node.fetch_children(singleton_id, peer)
1918
- assert len(children) > 0
1919
- found = False
1920
- parent_coin_id = singleton_id
1921
-
1922
- if self.dao_info.current_treasury_innerpuz is None: # pragma: no cover
1923
- raise ValueError("self.dao_info.current_treasury_innerpuz is None")
1924
-
1925
- timer_coin_puzhash = get_proposal_timer_puzzle(
1926
- bytes32(cat_tail_hash.as_atom()),
1927
- singleton_id,
1928
- self.dao_info.treasury_id,
1929
- ).get_tree_hash()
1930
-
1931
- while not found and len(children) > 0:
1932
- children = await wallet_node.fetch_children(parent_coin_id, peer)
1933
- if len(children) == 0: # pragma: no cover
1934
- break
1935
- children_state = [child for child in children if child.coin.amount % 2 == 1]
1936
- assert children_state is not None
1937
- assert len(children_state) > 0
1938
- child_state = children_state[0]
1939
- for child in children:
1940
- if child.coin.puzzle_hash == timer_coin_puzhash:
1941
- found = True
1942
- timer_coin = child.coin
1943
- break
1944
- child_coin = child_state.coin
1945
- parent_coin_id = child_coin.name()
1946
-
1947
- # If we reach here then we don't currently know about this coin
1948
- # We only want to add this coin if it has a timer coin since fake proposals without a timer can
1949
- # be created.
1950
- if found:
1951
- new_proposal_info = ProposalInfo(
1952
- singleton_id,
1953
- puzzle,
1954
- uint64(new_total_votes),
1955
- uint64(new_yes_votes),
1956
- current_coin,
1957
- current_innerpuz,
1958
- timer_coin, # if this is None then the proposal has finished
1959
- block_height, # block height that current proposal singleton coin was created
1960
- passed,
1961
- ended,
1962
- )
1963
- new_dao_info.proposals_list.append(new_proposal_info)
1964
- await self.save_info(new_dao_info)
1965
- future_parent = LineageProof(
1966
- new_state.coin.parent_coin_info,
1967
- puzzle.get_tree_hash(),
1968
- uint64(new_state.coin.amount),
1969
- )
1970
- await self.add_parent(new_state.coin.name(), future_parent)
1971
- return
1972
-
1973
- async def update_closed_proposal_coin(self, new_state: CoinSpend, block_height: uint32) -> None:
1974
- new_dao_info = copy.copy(self.dao_info)
1975
- puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
1976
- proposal_id = singleton.get_singleton_id_from_puzzle(new_state.puzzle_reveal)
1977
- current_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
1978
- index = 0
1979
- for pi in self.dao_info.proposals_list:
1980
- if pi.proposal_id == proposal_id:
1981
- assert isinstance(current_coin, Coin)
1982
- new_info = ProposalInfo(
1983
- proposal_id,
1984
- pi.inner_puzzle,
1985
- pi.amount_voted,
1986
- pi.yes_votes,
1987
- current_coin,
1988
- pi.current_innerpuz,
1989
- pi.timer_coin,
1990
- pi.singleton_block_height,
1991
- pi.passed,
1992
- pi.closed,
1993
- )
1994
- new_dao_info.proposals_list[index] = new_info
1995
- await self.save_info(new_dao_info)
1996
- assert isinstance(puzzle, Program)
1997
- future_parent = LineageProof(
1998
- new_state.coin.parent_coin_info,
1999
- puzzle.get_tree_hash(),
2000
- uint64(new_state.coin.amount),
2001
- )
2002
- await self.add_parent(new_state.coin.name(), future_parent)
2003
- return
2004
- index += 1
2005
-
2006
- async def get_proposal_state(self, proposal_id: bytes32) -> dict[str, Union[int, bool]]:
2007
- """
2008
- Use this to figure out whether a proposal has passed or failed and whether it can be closed
2009
- Given a proposal_id:
2010
- - if required yes votes are recorded then proposal passed.
2011
- - if timelock and attendance are met then proposal can close
2012
- Returns a dict of passed and closable bools, and the remaining votes/blocks needed
2013
-
2014
- Note that a proposal can be in a passed and closable state now, but become failed if a large number of
2015
- 'no' votes are recieved before the soft close is reached.
2016
- """
2017
- for prop in self.dao_info.proposals_list:
2018
- if prop.proposal_id == proposal_id:
2019
- is_closed = prop.closed
2020
- break
2021
- else: # pragma: no cover
2022
- raise ValueError(f"Proposal not found for id {proposal_id}")
2023
-
2024
- wallet_node = self.wallet_state_manager.wallet_node
2025
- peer: WSChiaConnection = wallet_node.get_full_node_peer()
2026
- if peer is None: # pragma: no cover
2027
- raise ValueError("Could not find any peers to request puzzle and solution from")
2028
- assert isinstance(prop.timer_coin, Coin)
2029
- timer_cs = (await wallet_node.get_coin_state([prop.timer_coin.name()], peer))[0]
2030
- peak = await self.wallet_state_manager.blockchain.get_peak_block()
2031
- blocks_elapsed = peak.height - timer_cs.created_height
2032
-
2033
- required_yes_votes = (self.dao_rules.attendance_required * self.dao_rules.pass_percentage) // 10000
2034
- total_votes_needed = max(0, self.dao_rules.attendance_required - prop.amount_voted)
2035
- yes_votes_needed = max(0, required_yes_votes - prop.yes_votes)
2036
- blocks_needed = max(0, self.dao_rules.proposal_timelock - blocks_elapsed)
2037
-
2038
- passed = True if yes_votes_needed == 0 else False
2039
- closable = True if total_votes_needed == blocks_needed == 0 else False
2040
- proposal_state = {
2041
- "total_votes_needed": total_votes_needed,
2042
- "yes_votes_needed": yes_votes_needed,
2043
- "blocks_needed": blocks_needed,
2044
- "passed": passed,
2045
- "closable": closable,
2046
- "closed": is_closed,
2047
- }
2048
- return proposal_state
2049
-
2050
- async def update_treasury_info(
2051
- self,
2052
- new_state: CoinSpend,
2053
- block_height: uint32,
2054
- ) -> None:
2055
- if self.dao_info.singleton_block_height <= block_height:
2056
- # TODO: what do we do here?
2057
- # return
2058
- pass
2059
- puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
2060
- if puzzle is None: # pragma: no cover
2061
- raise ValueError("get_innerpuzzle_from_puzzle failed")
2062
- solution = (
2063
- Program.from_bytes(bytes(new_state.solution)).rest().rest().first()
2064
- ) # get proposal solution from full singleton solution
2065
- new_innerpuz = get_new_puzzle_from_treasury_solution(puzzle, solution)
2066
- child_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
2067
- assert isinstance(child_coin, Coin)
2068
- assert isinstance(self.dao_info.current_treasury_coin, Coin)
2069
- if child_coin.puzzle_hash != self.dao_info.current_treasury_coin.puzzle_hash:
2070
- # update dao rules
2071
- assert isinstance(new_innerpuz, Program)
2072
- self.dao_rules = get_treasury_rules_from_puzzle(new_innerpuz)
2073
- dao_info = dataclasses.replace(
2074
- self.dao_info,
2075
- current_treasury_coin=child_coin,
2076
- current_treasury_innerpuz=new_innerpuz,
2077
- singleton_block_height=block_height,
2078
- )
2079
- await self.save_info(dao_info)
2080
- future_parent = LineageProof(
2081
- new_state.coin.parent_coin_info,
2082
- puzzle.get_tree_hash(),
2083
- uint64(new_state.coin.amount),
2084
- )
2085
- await self.add_parent(new_state.coin.name(), future_parent)
2086
-
2087
- async def apply_state_transition(self, new_state: CoinSpend, block_height: uint32) -> bool:
2088
- """
2089
- We are being notified of a singleton state transition. A Singleton has been spent.
2090
- Returns True iff the spend is a valid transition spend for the singleton, False otherwise.
2091
- """
2092
-
2093
- self.log.info(
2094
- f"DAOWallet.apply_state_transition called with the height: {block_height} "
2095
- f"and CoinSpend of {new_state.coin.name()}."
2096
- )
2097
- singleton_id = get_singleton_id_from_puzzle(new_state.puzzle_reveal)
2098
- if not singleton_id: # pragma: no cover
2099
- raise ValueError("Received a non singleton coin for dao wallet")
2100
-
2101
- # Consume new DAOBlockchainInfo
2102
- # Determine if this is a treasury spend or a proposal spend
2103
- puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
2104
- assert puzzle
2105
- try:
2106
- mod, _curried_args = puzzle.uncurry()
2107
- except ValueError as e: # pragma: no cover
2108
- self.log.warning("Cannot uncurry puzzle in DAO Wallet: error: %s", e)
2109
- raise e
2110
- if mod == DAO_TREASURY_MOD:
2111
- await self.update_treasury_info(new_state, block_height)
2112
- elif (mod == DAO_PROPOSAL_MOD) or (mod.uncurry()[0] == DAO_PROPOSAL_MOD):
2113
- await self.add_or_update_proposal_info(new_state, block_height)
2114
- elif mod == DAO_FINISHED_STATE:
2115
- await self.update_closed_proposal_coin(new_state, block_height)
2116
- else: # pragma: no cover
2117
- raise ValueError(f"Unsupported spend in DAO Wallet: {self.id()}")
2118
-
2119
- return True