chia-blockchain 2.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1028) hide show
  1. chia/__init__.py +10 -0
  2. chia/__main__.py +5 -0
  3. chia/_tests/README.md +53 -0
  4. chia/_tests/__init__.py +0 -0
  5. chia/_tests/blockchain/__init__.py +0 -0
  6. chia/_tests/blockchain/blockchain_test_utils.py +197 -0
  7. chia/_tests/blockchain/config.py +4 -0
  8. chia/_tests/blockchain/test_augmented_chain.py +147 -0
  9. chia/_tests/blockchain/test_blockchain.py +4100 -0
  10. chia/_tests/blockchain/test_blockchain_transactions.py +1050 -0
  11. chia/_tests/blockchain/test_build_chains.py +61 -0
  12. chia/_tests/blockchain/test_get_block_generator.py +72 -0
  13. chia/_tests/blockchain/test_lookup_fork_chain.py +195 -0
  14. chia/_tests/build-init-files.py +93 -0
  15. chia/_tests/build-job-matrix.py +204 -0
  16. chia/_tests/check_pytest_monitor_output.py +34 -0
  17. chia/_tests/check_sql_statements.py +73 -0
  18. chia/_tests/chia-start-sim +42 -0
  19. chia/_tests/clvm/__init__.py +0 -0
  20. chia/_tests/clvm/benchmark_costs.py +23 -0
  21. chia/_tests/clvm/coin_store.py +147 -0
  22. chia/_tests/clvm/test_chialisp_deserialization.py +101 -0
  23. chia/_tests/clvm/test_clvm_step.py +37 -0
  24. chia/_tests/clvm/test_condition_codes.py +13 -0
  25. chia/_tests/clvm/test_curry_and_treehash.py +57 -0
  26. chia/_tests/clvm/test_program.py +150 -0
  27. chia/_tests/clvm/test_puzzle_compression.py +144 -0
  28. chia/_tests/clvm/test_puzzle_drivers.py +45 -0
  29. chia/_tests/clvm/test_puzzles.py +247 -0
  30. chia/_tests/clvm/test_singletons.py +540 -0
  31. chia/_tests/clvm/test_spend_sim.py +181 -0
  32. chia/_tests/cmds/__init__.py +0 -0
  33. chia/_tests/cmds/cmd_test_utils.py +472 -0
  34. chia/_tests/cmds/config.py +3 -0
  35. chia/_tests/cmds/conftest.py +23 -0
  36. chia/_tests/cmds/test_click_types.py +195 -0
  37. chia/_tests/cmds/test_cmd_framework.py +400 -0
  38. chia/_tests/cmds/test_cmds_util.py +97 -0
  39. chia/_tests/cmds/test_daemon.py +92 -0
  40. chia/_tests/cmds/test_farm_cmd.py +67 -0
  41. chia/_tests/cmds/test_show.py +116 -0
  42. chia/_tests/cmds/test_sim.py +207 -0
  43. chia/_tests/cmds/test_timelock_args.py +75 -0
  44. chia/_tests/cmds/test_tx_config_args.py +153 -0
  45. chia/_tests/cmds/testing_classes.py +59 -0
  46. chia/_tests/cmds/wallet/__init__.py +0 -0
  47. chia/_tests/cmds/wallet/test_coins.py +195 -0
  48. chia/_tests/cmds/wallet/test_consts.py +47 -0
  49. chia/_tests/cmds/wallet/test_dao.py +565 -0
  50. chia/_tests/cmds/wallet/test_did.py +403 -0
  51. chia/_tests/cmds/wallet/test_nft.py +470 -0
  52. chia/_tests/cmds/wallet/test_notifications.py +124 -0
  53. chia/_tests/cmds/wallet/test_offer.toffer +1 -0
  54. chia/_tests/cmds/wallet/test_tx_decorators.py +27 -0
  55. chia/_tests/cmds/wallet/test_vcs.py +376 -0
  56. chia/_tests/cmds/wallet/test_wallet.py +1126 -0
  57. chia/_tests/cmds/wallet/test_wallet_check.py +111 -0
  58. chia/_tests/conftest.py +1304 -0
  59. chia/_tests/connection_utils.py +124 -0
  60. chia/_tests/core/__init__.py +0 -0
  61. chia/_tests/core/cmds/__init__.py +0 -0
  62. chia/_tests/core/cmds/test_beta.py +382 -0
  63. chia/_tests/core/cmds/test_keys.py +1734 -0
  64. chia/_tests/core/cmds/test_wallet.py +126 -0
  65. chia/_tests/core/config.py +3 -0
  66. chia/_tests/core/consensus/__init__.py +0 -0
  67. chia/_tests/core/consensus/test_block_creation.py +56 -0
  68. chia/_tests/core/consensus/test_pot_iterations.py +117 -0
  69. chia/_tests/core/custom_types/__init__.py +0 -0
  70. chia/_tests/core/custom_types/test_coin.py +109 -0
  71. chia/_tests/core/custom_types/test_proof_of_space.py +144 -0
  72. chia/_tests/core/custom_types/test_spend_bundle.py +71 -0
  73. chia/_tests/core/daemon/__init__.py +0 -0
  74. chia/_tests/core/daemon/config.py +4 -0
  75. chia/_tests/core/daemon/test_daemon.py +2128 -0
  76. chia/_tests/core/daemon/test_daemon_register.py +109 -0
  77. chia/_tests/core/daemon/test_keychain_proxy.py +100 -0
  78. chia/_tests/core/data_layer/__init__.py +0 -0
  79. chia/_tests/core/data_layer/config.py +5 -0
  80. chia/_tests/core/data_layer/conftest.py +105 -0
  81. chia/_tests/core/data_layer/test_data_cli.py +57 -0
  82. chia/_tests/core/data_layer/test_data_layer.py +83 -0
  83. chia/_tests/core/data_layer/test_data_layer_util.py +219 -0
  84. chia/_tests/core/data_layer/test_data_rpc.py +3865 -0
  85. chia/_tests/core/data_layer/test_data_store.py +2423 -0
  86. chia/_tests/core/data_layer/test_data_store_schema.py +381 -0
  87. chia/_tests/core/data_layer/test_plugin.py +91 -0
  88. chia/_tests/core/data_layer/util.py +232 -0
  89. chia/_tests/core/farmer/__init__.py +0 -0
  90. chia/_tests/core/farmer/config.py +3 -0
  91. chia/_tests/core/farmer/test_farmer_api.py +101 -0
  92. chia/_tests/core/full_node/__init__.py +0 -0
  93. chia/_tests/core/full_node/config.py +4 -0
  94. chia/_tests/core/full_node/dos/__init__.py +0 -0
  95. chia/_tests/core/full_node/dos/config.py +3 -0
  96. chia/_tests/core/full_node/full_sync/__init__.py +0 -0
  97. chia/_tests/core/full_node/full_sync/config.py +4 -0
  98. chia/_tests/core/full_node/full_sync/test_full_sync.py +448 -0
  99. chia/_tests/core/full_node/ram_db.py +27 -0
  100. chia/_tests/core/full_node/stores/__init__.py +0 -0
  101. chia/_tests/core/full_node/stores/config.py +4 -0
  102. chia/_tests/core/full_node/stores/test_block_store.py +488 -0
  103. chia/_tests/core/full_node/stores/test_coin_store.py +888 -0
  104. chia/_tests/core/full_node/stores/test_full_node_store.py +1215 -0
  105. chia/_tests/core/full_node/stores/test_hint_store.py +230 -0
  106. chia/_tests/core/full_node/stores/test_sync_store.py +135 -0
  107. chia/_tests/core/full_node/test_address_manager.py +588 -0
  108. chia/_tests/core/full_node/test_block_height_map.py +556 -0
  109. chia/_tests/core/full_node/test_conditions.py +558 -0
  110. chia/_tests/core/full_node/test_full_node.py +2445 -0
  111. chia/_tests/core/full_node/test_generator_tools.py +82 -0
  112. chia/_tests/core/full_node/test_hint_management.py +104 -0
  113. chia/_tests/core/full_node/test_node_load.py +34 -0
  114. chia/_tests/core/full_node/test_performance.py +182 -0
  115. chia/_tests/core/full_node/test_subscriptions.py +492 -0
  116. chia/_tests/core/full_node/test_transactions.py +203 -0
  117. chia/_tests/core/full_node/test_tx_processing_queue.py +154 -0
  118. chia/_tests/core/large_block.py +2388 -0
  119. chia/_tests/core/make_block_generator.py +72 -0
  120. chia/_tests/core/mempool/__init__.py +0 -0
  121. chia/_tests/core/mempool/config.py +4 -0
  122. chia/_tests/core/mempool/test_mempool.py +3180 -0
  123. chia/_tests/core/mempool/test_mempool_fee_estimator.py +104 -0
  124. chia/_tests/core/mempool/test_mempool_fee_protocol.py +55 -0
  125. chia/_tests/core/mempool/test_mempool_item_queries.py +192 -0
  126. chia/_tests/core/mempool/test_mempool_manager.py +2054 -0
  127. chia/_tests/core/mempool/test_mempool_performance.py +65 -0
  128. chia/_tests/core/mempool/test_singleton_fast_forward.py +567 -0
  129. chia/_tests/core/node_height.py +28 -0
  130. chia/_tests/core/server/__init__.py +0 -0
  131. chia/_tests/core/server/config.py +3 -0
  132. chia/_tests/core/server/flood.py +82 -0
  133. chia/_tests/core/server/serve.py +132 -0
  134. chia/_tests/core/server/test_capabilities.py +68 -0
  135. chia/_tests/core/server/test_dos.py +320 -0
  136. chia/_tests/core/server/test_event_loop.py +109 -0
  137. chia/_tests/core/server/test_loop.py +290 -0
  138. chia/_tests/core/server/test_node_discovery.py +74 -0
  139. chia/_tests/core/server/test_rate_limits.py +370 -0
  140. chia/_tests/core/server/test_server.py +225 -0
  141. chia/_tests/core/server/test_upnp.py +8 -0
  142. chia/_tests/core/services/__init__.py +0 -0
  143. chia/_tests/core/services/config.py +3 -0
  144. chia/_tests/core/services/test_services.py +166 -0
  145. chia/_tests/core/ssl/__init__.py +0 -0
  146. chia/_tests/core/ssl/config.py +3 -0
  147. chia/_tests/core/ssl/test_ssl.py +198 -0
  148. chia/_tests/core/test_coins.py +33 -0
  149. chia/_tests/core/test_cost_calculation.py +314 -0
  150. chia/_tests/core/test_crawler.py +175 -0
  151. chia/_tests/core/test_crawler_rpc.py +53 -0
  152. chia/_tests/core/test_daemon_rpc.py +24 -0
  153. chia/_tests/core/test_db_conversion.py +129 -0
  154. chia/_tests/core/test_db_validation.py +161 -0
  155. chia/_tests/core/test_farmer_harvester_rpc.py +504 -0
  156. chia/_tests/core/test_filter.py +37 -0
  157. chia/_tests/core/test_full_node_rpc.py +794 -0
  158. chia/_tests/core/test_merkle_set.py +343 -0
  159. chia/_tests/core/test_program.py +49 -0
  160. chia/_tests/core/test_rpc_util.py +87 -0
  161. chia/_tests/core/test_seeder.py +308 -0
  162. chia/_tests/core/test_setproctitle.py +13 -0
  163. chia/_tests/core/util/__init__.py +0 -0
  164. chia/_tests/core/util/config.py +4 -0
  165. chia/_tests/core/util/test_block_cache.py +44 -0
  166. chia/_tests/core/util/test_cached_bls.py +57 -0
  167. chia/_tests/core/util/test_config.py +337 -0
  168. chia/_tests/core/util/test_file_keyring_synchronization.py +105 -0
  169. chia/_tests/core/util/test_files.py +391 -0
  170. chia/_tests/core/util/test_jsonify.py +146 -0
  171. chia/_tests/core/util/test_keychain.py +514 -0
  172. chia/_tests/core/util/test_keyring_wrapper.py +490 -0
  173. chia/_tests/core/util/test_lockfile.py +380 -0
  174. chia/_tests/core/util/test_log_exceptions.py +187 -0
  175. chia/_tests/core/util/test_lru_cache.py +56 -0
  176. chia/_tests/core/util/test_significant_bits.py +40 -0
  177. chia/_tests/core/util/test_streamable.py +883 -0
  178. chia/_tests/db/__init__.py +0 -0
  179. chia/_tests/db/test_db_wrapper.py +565 -0
  180. chia/_tests/environments/__init__.py +0 -0
  181. chia/_tests/environments/common.py +35 -0
  182. chia/_tests/environments/full_node.py +47 -0
  183. chia/_tests/environments/wallet.py +368 -0
  184. chia/_tests/ether.py +19 -0
  185. chia/_tests/farmer_harvester/__init__.py +0 -0
  186. chia/_tests/farmer_harvester/config.py +3 -0
  187. chia/_tests/farmer_harvester/test_farmer.py +1264 -0
  188. chia/_tests/farmer_harvester/test_farmer_harvester.py +292 -0
  189. chia/_tests/farmer_harvester/test_filter_prefix_bits.py +130 -0
  190. chia/_tests/farmer_harvester/test_third_party_harvesters.py +501 -0
  191. chia/_tests/farmer_harvester/test_third_party_harvesters_data.json +29 -0
  192. chia/_tests/fee_estimation/__init__.py +0 -0
  193. chia/_tests/fee_estimation/config.py +3 -0
  194. chia/_tests/fee_estimation/test_fee_estimation_integration.py +262 -0
  195. chia/_tests/fee_estimation/test_fee_estimation_rpc.py +287 -0
  196. chia/_tests/fee_estimation/test_fee_estimation_unit_tests.py +145 -0
  197. chia/_tests/fee_estimation/test_mempoolitem_height_added.py +146 -0
  198. chia/_tests/generator/__init__.py +0 -0
  199. chia/_tests/generator/puzzles/__init__.py +0 -0
  200. chia/_tests/generator/puzzles/test_generator_deserialize.clsp +3 -0
  201. chia/_tests/generator/puzzles/test_generator_deserialize.clsp.hex +1 -0
  202. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp +19 -0
  203. chia/_tests/generator/puzzles/test_multiple_generator_input_arguments.clsp.hex +1 -0
  204. chia/_tests/generator/test_compression.py +218 -0
  205. chia/_tests/generator/test_generator_types.py +44 -0
  206. chia/_tests/generator/test_rom.py +182 -0
  207. chia/_tests/plot_sync/__init__.py +0 -0
  208. chia/_tests/plot_sync/config.py +3 -0
  209. chia/_tests/plot_sync/test_delta.py +102 -0
  210. chia/_tests/plot_sync/test_plot_sync.py +617 -0
  211. chia/_tests/plot_sync/test_receiver.py +451 -0
  212. chia/_tests/plot_sync/test_sender.py +116 -0
  213. chia/_tests/plot_sync/test_sync_simulated.py +450 -0
  214. chia/_tests/plot_sync/util.py +67 -0
  215. chia/_tests/plotting/__init__.py +0 -0
  216. chia/_tests/plotting/config.py +3 -0
  217. chia/_tests/plotting/test_plot_manager.py +738 -0
  218. chia/_tests/plotting/util.py +13 -0
  219. chia/_tests/pools/__init__.py +0 -0
  220. chia/_tests/pools/config.py +5 -0
  221. chia/_tests/pools/test_pool_cmdline.py +23 -0
  222. chia/_tests/pools/test_pool_config.py +44 -0
  223. chia/_tests/pools/test_pool_puzzles_lifecycle.py +398 -0
  224. chia/_tests/pools/test_pool_rpc.py +1010 -0
  225. chia/_tests/pools/test_pool_wallet.py +201 -0
  226. chia/_tests/pools/test_wallet_pool_store.py +161 -0
  227. chia/_tests/process_junit.py +349 -0
  228. chia/_tests/rpc/__init__.py +0 -0
  229. chia/_tests/rpc/test_rpc_client.py +81 -0
  230. chia/_tests/simulation/__init__.py +0 -0
  231. chia/_tests/simulation/config.py +6 -0
  232. chia/_tests/simulation/test_simulation.py +501 -0
  233. chia/_tests/simulation/test_simulator.py +234 -0
  234. chia/_tests/simulation/test_start_simulator.py +106 -0
  235. chia/_tests/testconfig.py +13 -0
  236. chia/_tests/timelord/__init__.py +0 -0
  237. chia/_tests/timelord/config.py +3 -0
  238. chia/_tests/timelord/test_new_peak.py +437 -0
  239. chia/_tests/timelord/test_timelord.py +11 -0
  240. chia/_tests/tools/1315537.json +170 -0
  241. chia/_tests/tools/1315544.json +160 -0
  242. chia/_tests/tools/1315630.json +150 -0
  243. chia/_tests/tools/300000.json +105 -0
  244. chia/_tests/tools/442734.json +140 -0
  245. chia/_tests/tools/466212.json +130 -0
  246. chia/_tests/tools/__init__.py +0 -0
  247. chia/_tests/tools/config.py +5 -0
  248. chia/_tests/tools/test-blockchain-db.sqlite +0 -0
  249. chia/_tests/tools/test_full_sync.py +30 -0
  250. chia/_tests/tools/test_legacy_keyring.py +82 -0
  251. chia/_tests/tools/test_run_block.py +129 -0
  252. chia/_tests/util/__init__.py +0 -0
  253. chia/_tests/util/benchmark_cost.py +170 -0
  254. chia/_tests/util/benchmarks.py +154 -0
  255. chia/_tests/util/bip39_test_vectors.json +148 -0
  256. chia/_tests/util/blockchain.py +133 -0
  257. chia/_tests/util/blockchain_mock.py +132 -0
  258. chia/_tests/util/build_network_protocol_files.py +302 -0
  259. chia/_tests/util/clvm_generator.bin +0 -0
  260. chia/_tests/util/config.py +3 -0
  261. chia/_tests/util/constants.py +20 -0
  262. chia/_tests/util/db_connection.py +36 -0
  263. chia/_tests/util/full_sync.py +245 -0
  264. chia/_tests/util/gen_ssl_certs.py +115 -0
  265. chia/_tests/util/generator_tools_testing.py +47 -0
  266. chia/_tests/util/key_tool.py +37 -0
  267. chia/_tests/util/misc.py +722 -0
  268. chia/_tests/util/network_protocol_data.py +1074 -0
  269. chia/_tests/util/protocol_messages_bytes-v1.0 +0 -0
  270. chia/_tests/util/protocol_messages_json.py +2700 -0
  271. chia/_tests/util/rpc.py +23 -0
  272. chia/_tests/util/run_block.py +163 -0
  273. chia/_tests/util/setup_nodes.py +479 -0
  274. chia/_tests/util/split_managers.py +99 -0
  275. chia/_tests/util/temp_file.py +14 -0
  276. chia/_tests/util/test_action_scope.py +143 -0
  277. chia/_tests/util/test_async_pool.py +366 -0
  278. chia/_tests/util/test_build_job_matrix.py +43 -0
  279. chia/_tests/util/test_build_network_protocol_files.py +7 -0
  280. chia/_tests/util/test_chia_version.py +50 -0
  281. chia/_tests/util/test_collection.py +11 -0
  282. chia/_tests/util/test_condition_tools.py +231 -0
  283. chia/_tests/util/test_config.py +426 -0
  284. chia/_tests/util/test_dump_keyring.py +60 -0
  285. chia/_tests/util/test_errors.py +10 -0
  286. chia/_tests/util/test_full_block_utils.py +271 -0
  287. chia/_tests/util/test_installed.py +20 -0
  288. chia/_tests/util/test_limited_semaphore.py +52 -0
  289. chia/_tests/util/test_logging_filter.py +43 -0
  290. chia/_tests/util/test_misc.py +444 -0
  291. chia/_tests/util/test_network.py +74 -0
  292. chia/_tests/util/test_network_protocol_files.py +579 -0
  293. chia/_tests/util/test_network_protocol_json.py +266 -0
  294. chia/_tests/util/test_network_protocol_test.py +257 -0
  295. chia/_tests/util/test_paginator.py +72 -0
  296. chia/_tests/util/test_pprint.py +17 -0
  297. chia/_tests/util/test_priority_mutex.py +487 -0
  298. chia/_tests/util/test_recursive_replace.py +116 -0
  299. chia/_tests/util/test_replace_str_to_bytes.py +137 -0
  300. chia/_tests/util/test_service_groups.py +15 -0
  301. chia/_tests/util/test_ssl_check.py +31 -0
  302. chia/_tests/util/test_testnet_overrides.py +19 -0
  303. chia/_tests/util/test_tests_misc.py +38 -0
  304. chia/_tests/util/test_timing.py +37 -0
  305. chia/_tests/util/test_trusted_peer.py +51 -0
  306. chia/_tests/util/time_out_assert.py +154 -0
  307. chia/_tests/wallet/__init__.py +0 -0
  308. chia/_tests/wallet/cat_wallet/__init__.py +0 -0
  309. chia/_tests/wallet/cat_wallet/config.py +4 -0
  310. chia/_tests/wallet/cat_wallet/test_cat_lifecycle.py +468 -0
  311. chia/_tests/wallet/cat_wallet/test_cat_outer_puzzle.py +69 -0
  312. chia/_tests/wallet/cat_wallet/test_cat_wallet.py +1738 -0
  313. chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +291 -0
  314. chia/_tests/wallet/cat_wallet/test_trades.py +2578 -0
  315. chia/_tests/wallet/clawback/__init__.py +0 -0
  316. chia/_tests/wallet/clawback/config.py +3 -0
  317. chia/_tests/wallet/clawback/test_clawback_decorator.py +80 -0
  318. chia/_tests/wallet/clawback/test_clawback_lifecycle.py +292 -0
  319. chia/_tests/wallet/clawback/test_clawback_metadata.py +51 -0
  320. chia/_tests/wallet/config.py +4 -0
  321. chia/_tests/wallet/conftest.py +217 -0
  322. chia/_tests/wallet/dao_wallet/__init__.py +0 -0
  323. chia/_tests/wallet/dao_wallet/config.py +3 -0
  324. chia/_tests/wallet/dao_wallet/test_dao_clvm.py +1322 -0
  325. chia/_tests/wallet/dao_wallet/test_dao_wallets.py +3488 -0
  326. chia/_tests/wallet/db_wallet/__init__.py +0 -0
  327. chia/_tests/wallet/db_wallet/config.py +3 -0
  328. chia/_tests/wallet/db_wallet/test_db_graftroot.py +143 -0
  329. chia/_tests/wallet/db_wallet/test_dl_offers.py +491 -0
  330. chia/_tests/wallet/db_wallet/test_dl_wallet.py +823 -0
  331. chia/_tests/wallet/did_wallet/__init__.py +0 -0
  332. chia/_tests/wallet/did_wallet/config.py +4 -0
  333. chia/_tests/wallet/did_wallet/test_did.py +1481 -0
  334. chia/_tests/wallet/nft_wallet/__init__.py +0 -0
  335. chia/_tests/wallet/nft_wallet/config.py +4 -0
  336. chia/_tests/wallet/nft_wallet/test_nft_1_offers.py +1492 -0
  337. chia/_tests/wallet/nft_wallet/test_nft_bulk_mint.py +1014 -0
  338. chia/_tests/wallet/nft_wallet/test_nft_lifecycle.py +376 -0
  339. chia/_tests/wallet/nft_wallet/test_nft_offers.py +1209 -0
  340. chia/_tests/wallet/nft_wallet/test_nft_puzzles.py +172 -0
  341. chia/_tests/wallet/nft_wallet/test_nft_wallet.py +2558 -0
  342. chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +70 -0
  343. chia/_tests/wallet/rpc/__init__.py +0 -0
  344. chia/_tests/wallet/rpc/config.py +4 -0
  345. chia/_tests/wallet/rpc/test_dl_wallet_rpc.py +287 -0
  346. chia/_tests/wallet/rpc/test_wallet_rpc.py +3106 -0
  347. chia/_tests/wallet/simple_sync/__init__.py +0 -0
  348. chia/_tests/wallet/simple_sync/config.py +3 -0
  349. chia/_tests/wallet/simple_sync/test_simple_sync_protocol.py +719 -0
  350. chia/_tests/wallet/sync/__init__.py +0 -0
  351. chia/_tests/wallet/sync/config.py +4 -0
  352. chia/_tests/wallet/sync/test_wallet_sync.py +1529 -0
  353. chia/_tests/wallet/test_address_type.py +189 -0
  354. chia/_tests/wallet/test_bech32m.py +45 -0
  355. chia/_tests/wallet/test_clvm_streamable.py +244 -0
  356. chia/_tests/wallet/test_coin_selection.py +589 -0
  357. chia/_tests/wallet/test_conditions.py +388 -0
  358. chia/_tests/wallet/test_debug_spend_bundle.py +76 -0
  359. chia/_tests/wallet/test_new_wallet_protocol.py +1176 -0
  360. chia/_tests/wallet/test_nft_store.py +193 -0
  361. chia/_tests/wallet/test_notifications.py +196 -0
  362. chia/_tests/wallet/test_offer_parsing_performance.py +48 -0
  363. chia/_tests/wallet/test_puzzle_store.py +133 -0
  364. chia/_tests/wallet/test_sign_coin_spends.py +159 -0
  365. chia/_tests/wallet/test_signer_protocol.py +948 -0
  366. chia/_tests/wallet/test_singleton.py +122 -0
  367. chia/_tests/wallet/test_singleton_lifecycle_fast.py +772 -0
  368. chia/_tests/wallet/test_singleton_store.py +152 -0
  369. chia/_tests/wallet/test_taproot.py +19 -0
  370. chia/_tests/wallet/test_transaction_store.py +941 -0
  371. chia/_tests/wallet/test_util.py +181 -0
  372. chia/_tests/wallet/test_wallet.py +2139 -0
  373. chia/_tests/wallet/test_wallet_action_scope.py +85 -0
  374. chia/_tests/wallet/test_wallet_blockchain.py +113 -0
  375. chia/_tests/wallet/test_wallet_coin_store.py +1002 -0
  376. chia/_tests/wallet/test_wallet_interested_store.py +43 -0
  377. chia/_tests/wallet/test_wallet_key_val_store.py +40 -0
  378. chia/_tests/wallet/test_wallet_node.py +783 -0
  379. chia/_tests/wallet/test_wallet_retry.py +95 -0
  380. chia/_tests/wallet/test_wallet_state_manager.py +252 -0
  381. chia/_tests/wallet/test_wallet_test_framework.py +275 -0
  382. chia/_tests/wallet/test_wallet_trade_store.py +218 -0
  383. chia/_tests/wallet/test_wallet_user_store.py +34 -0
  384. chia/_tests/wallet/test_wallet_utils.py +155 -0
  385. chia/_tests/wallet/vc_wallet/__init__.py +0 -0
  386. chia/_tests/wallet/vc_wallet/config.py +3 -0
  387. chia/_tests/wallet/vc_wallet/test_cr_outer_puzzle.py +70 -0
  388. chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py +883 -0
  389. chia/_tests/wallet/vc_wallet/test_vc_wallet.py +801 -0
  390. chia/_tests/wallet/wallet_block_tools.py +327 -0
  391. chia/_tests/weight_proof/__init__.py +0 -0
  392. chia/_tests/weight_proof/config.py +3 -0
  393. chia/_tests/weight_proof/test_weight_proof.py +528 -0
  394. chia/clvm/__init__.py +0 -0
  395. chia/clvm/spend_sim.py +488 -0
  396. chia/cmds/__init__.py +0 -0
  397. chia/cmds/beta.py +183 -0
  398. chia/cmds/beta_funcs.py +133 -0
  399. chia/cmds/check_wallet_db.py +418 -0
  400. chia/cmds/chia.py +143 -0
  401. chia/cmds/cmd_classes.py +315 -0
  402. chia/cmds/cmds_util.py +498 -0
  403. chia/cmds/coin_funcs.py +260 -0
  404. chia/cmds/coins.py +220 -0
  405. chia/cmds/completion.py +49 -0
  406. chia/cmds/configure.py +331 -0
  407. chia/cmds/dao.py +1008 -0
  408. chia/cmds/dao_funcs.py +576 -0
  409. chia/cmds/data.py +707 -0
  410. chia/cmds/data_funcs.py +380 -0
  411. chia/cmds/db.py +86 -0
  412. chia/cmds/db_backup_func.py +77 -0
  413. chia/cmds/db_upgrade_func.py +452 -0
  414. chia/cmds/db_validate_func.py +184 -0
  415. chia/cmds/dev.py +16 -0
  416. chia/cmds/farm.py +87 -0
  417. chia/cmds/farm_funcs.py +207 -0
  418. chia/cmds/init.py +70 -0
  419. chia/cmds/init_funcs.py +367 -0
  420. chia/cmds/installers.py +129 -0
  421. chia/cmds/keys.py +510 -0
  422. chia/cmds/keys_funcs.py +864 -0
  423. chia/cmds/netspace.py +47 -0
  424. chia/cmds/netspace_funcs.py +53 -0
  425. chia/cmds/options.py +32 -0
  426. chia/cmds/param_types.py +228 -0
  427. chia/cmds/passphrase.py +130 -0
  428. chia/cmds/passphrase_funcs.py +346 -0
  429. chia/cmds/peer.py +50 -0
  430. chia/cmds/peer_funcs.py +129 -0
  431. chia/cmds/plotnft.py +206 -0
  432. chia/cmds/plotnft_funcs.py +374 -0
  433. chia/cmds/plots.py +222 -0
  434. chia/cmds/plotters.py +17 -0
  435. chia/cmds/rpc.py +188 -0
  436. chia/cmds/show.py +71 -0
  437. chia/cmds/show_funcs.py +214 -0
  438. chia/cmds/signer.py +304 -0
  439. chia/cmds/sim.py +217 -0
  440. chia/cmds/sim_funcs.py +509 -0
  441. chia/cmds/start.py +24 -0
  442. chia/cmds/start_funcs.py +112 -0
  443. chia/cmds/stop.py +61 -0
  444. chia/cmds/units.py +11 -0
  445. chia/cmds/wallet.py +1745 -0
  446. chia/cmds/wallet_funcs.py +1800 -0
  447. chia/consensus/__init__.py +0 -0
  448. chia/consensus/block_body_validation.py +515 -0
  449. chia/consensus/block_creation.py +525 -0
  450. chia/consensus/block_header_validation.py +1064 -0
  451. chia/consensus/block_record.py +32 -0
  452. chia/consensus/block_rewards.py +53 -0
  453. chia/consensus/block_root_validation.py +46 -0
  454. chia/consensus/blockchain.py +1100 -0
  455. chia/consensus/blockchain_interface.py +56 -0
  456. chia/consensus/coinbase.py +30 -0
  457. chia/consensus/condition_costs.py +9 -0
  458. chia/consensus/constants.py +49 -0
  459. chia/consensus/cost_calculator.py +15 -0
  460. chia/consensus/default_constants.py +90 -0
  461. chia/consensus/deficit.py +55 -0
  462. chia/consensus/difficulty_adjustment.py +412 -0
  463. chia/consensus/find_fork_point.py +111 -0
  464. chia/consensus/full_block_to_block_record.py +167 -0
  465. chia/consensus/get_block_challenge.py +106 -0
  466. chia/consensus/get_block_generator.py +26 -0
  467. chia/consensus/make_sub_epoch_summary.py +210 -0
  468. chia/consensus/multiprocess_validation.py +365 -0
  469. chia/consensus/pos_quality.py +19 -0
  470. chia/consensus/pot_iterations.py +67 -0
  471. chia/consensus/puzzles/__init__.py +0 -0
  472. chia/consensus/puzzles/chialisp_deserialisation.clsp +69 -0
  473. chia/consensus/puzzles/chialisp_deserialisation.clsp.hex +1 -0
  474. chia/consensus/puzzles/rom_bootstrap_generator.clsp +37 -0
  475. chia/consensus/puzzles/rom_bootstrap_generator.clsp.hex +1 -0
  476. chia/consensus/vdf_info_computation.py +156 -0
  477. chia/daemon/__init__.py +0 -0
  478. chia/daemon/client.py +233 -0
  479. chia/daemon/keychain_proxy.py +501 -0
  480. chia/daemon/keychain_server.py +365 -0
  481. chia/daemon/server.py +1616 -0
  482. chia/daemon/windows_signal.py +56 -0
  483. chia/data_layer/__init__.py +0 -0
  484. chia/data_layer/data_layer.py +1303 -0
  485. chia/data_layer/data_layer_api.py +25 -0
  486. chia/data_layer/data_layer_errors.py +50 -0
  487. chia/data_layer/data_layer_server.py +170 -0
  488. chia/data_layer/data_layer_util.py +985 -0
  489. chia/data_layer/data_layer_wallet.py +1315 -0
  490. chia/data_layer/data_store.py +2267 -0
  491. chia/data_layer/dl_wallet_store.py +407 -0
  492. chia/data_layer/download_data.py +389 -0
  493. chia/data_layer/puzzles/__init__.py +0 -0
  494. chia/data_layer/puzzles/graftroot_dl_offers.clsp +100 -0
  495. chia/data_layer/puzzles/graftroot_dl_offers.clsp.hex +1 -0
  496. chia/data_layer/s3_plugin_config.yml +33 -0
  497. chia/data_layer/s3_plugin_service.py +468 -0
  498. chia/data_layer/util/__init__.py +0 -0
  499. chia/data_layer/util/benchmark.py +108 -0
  500. chia/data_layer/util/plugin.py +41 -0
  501. chia/farmer/__init__.py +0 -0
  502. chia/farmer/farmer.py +920 -0
  503. chia/farmer/farmer_api.py +814 -0
  504. chia/full_node/__init__.py +0 -0
  505. chia/full_node/bitcoin_fee_estimator.py +85 -0
  506. chia/full_node/block_height_map.py +271 -0
  507. chia/full_node/block_store.py +570 -0
  508. chia/full_node/bundle_tools.py +19 -0
  509. chia/full_node/coin_store.py +646 -0
  510. chia/full_node/fee_estimate.py +54 -0
  511. chia/full_node/fee_estimate_store.py +24 -0
  512. chia/full_node/fee_estimation.py +93 -0
  513. chia/full_node/fee_estimator.py +90 -0
  514. chia/full_node/fee_estimator_constants.py +38 -0
  515. chia/full_node/fee_estimator_interface.py +42 -0
  516. chia/full_node/fee_history.py +26 -0
  517. chia/full_node/fee_tracker.py +564 -0
  518. chia/full_node/full_node.py +3052 -0
  519. chia/full_node/full_node_api.py +1974 -0
  520. chia/full_node/full_node_store.py +1033 -0
  521. chia/full_node/hint_management.py +56 -0
  522. chia/full_node/hint_store.py +94 -0
  523. chia/full_node/mempool.py +583 -0
  524. chia/full_node/mempool_check_conditions.py +177 -0
  525. chia/full_node/mempool_manager.py +858 -0
  526. chia/full_node/pending_tx_cache.py +112 -0
  527. chia/full_node/puzzles/__init__.py +0 -0
  528. chia/full_node/puzzles/block_program_zero.clsp +14 -0
  529. chia/full_node/puzzles/block_program_zero.clsp.hex +1 -0
  530. chia/full_node/puzzles/decompress_coin_spend_entry.clsp +5 -0
  531. chia/full_node/puzzles/decompress_coin_spend_entry.clsp.hex +1 -0
  532. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp +7 -0
  533. chia/full_node/puzzles/decompress_coin_spend_entry_with_prefix.clsp.hex +1 -0
  534. chia/full_node/puzzles/decompress_puzzle.clsp +6 -0
  535. chia/full_node/puzzles/decompress_puzzle.clsp.hex +1 -0
  536. chia/full_node/signage_point.py +16 -0
  537. chia/full_node/subscriptions.py +248 -0
  538. chia/full_node/sync_store.py +145 -0
  539. chia/full_node/tx_processing_queue.py +78 -0
  540. chia/full_node/weight_proof.py +1719 -0
  541. chia/harvester/__init__.py +0 -0
  542. chia/harvester/harvester.py +271 -0
  543. chia/harvester/harvester_api.py +374 -0
  544. chia/introducer/__init__.py +0 -0
  545. chia/introducer/introducer.py +120 -0
  546. chia/introducer/introducer_api.py +64 -0
  547. chia/legacy/__init__.py +0 -0
  548. chia/legacy/keyring.py +154 -0
  549. chia/plot_sync/__init__.py +0 -0
  550. chia/plot_sync/delta.py +61 -0
  551. chia/plot_sync/exceptions.py +56 -0
  552. chia/plot_sync/receiver.py +385 -0
  553. chia/plot_sync/sender.py +337 -0
  554. chia/plot_sync/util.py +43 -0
  555. chia/plotters/__init__.py +0 -0
  556. chia/plotters/bladebit.py +388 -0
  557. chia/plotters/chiapos.py +63 -0
  558. chia/plotters/madmax.py +224 -0
  559. chia/plotters/plotters.py +577 -0
  560. chia/plotters/plotters_util.py +131 -0
  561. chia/plotting/__init__.py +0 -0
  562. chia/plotting/cache.py +212 -0
  563. chia/plotting/check_plots.py +283 -0
  564. chia/plotting/create_plots.py +278 -0
  565. chia/plotting/manager.py +436 -0
  566. chia/plotting/util.py +324 -0
  567. chia/pools/__init__.py +0 -0
  568. chia/pools/pool_config.py +110 -0
  569. chia/pools/pool_puzzles.py +459 -0
  570. chia/pools/pool_wallet.py +926 -0
  571. chia/pools/pool_wallet_info.py +118 -0
  572. chia/pools/puzzles/__init__.py +0 -0
  573. chia/pools/puzzles/pool_member_innerpuz.clsp +70 -0
  574. chia/pools/puzzles/pool_member_innerpuz.clsp.hex +1 -0
  575. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp +69 -0
  576. chia/pools/puzzles/pool_waitingroom_innerpuz.clsp.hex +1 -0
  577. chia/protocols/__init__.py +0 -0
  578. chia/protocols/farmer_protocol.py +102 -0
  579. chia/protocols/full_node_protocol.py +219 -0
  580. chia/protocols/harvester_protocol.py +216 -0
  581. chia/protocols/introducer_protocol.py +26 -0
  582. chia/protocols/pool_protocol.py +177 -0
  583. chia/protocols/protocol_message_types.py +139 -0
  584. chia/protocols/protocol_state_machine.py +87 -0
  585. chia/protocols/protocol_timing.py +7 -0
  586. chia/protocols/shared_protocol.py +86 -0
  587. chia/protocols/timelord_protocol.py +93 -0
  588. chia/protocols/wallet_protocol.py +401 -0
  589. chia/py.typed +0 -0
  590. chia/rpc/__init__.py +0 -0
  591. chia/rpc/crawler_rpc_api.py +75 -0
  592. chia/rpc/data_layer_rpc_api.py +639 -0
  593. chia/rpc/data_layer_rpc_client.py +188 -0
  594. chia/rpc/data_layer_rpc_util.py +62 -0
  595. chia/rpc/farmer_rpc_api.py +360 -0
  596. chia/rpc/farmer_rpc_client.py +86 -0
  597. chia/rpc/full_node_rpc_api.py +954 -0
  598. chia/rpc/full_node_rpc_client.py +292 -0
  599. chia/rpc/harvester_rpc_api.py +136 -0
  600. chia/rpc/harvester_rpc_client.py +54 -0
  601. chia/rpc/rpc_client.py +144 -0
  602. chia/rpc/rpc_server.py +447 -0
  603. chia/rpc/timelord_rpc_api.py +27 -0
  604. chia/rpc/util.py +293 -0
  605. chia/rpc/wallet_request_types.py +688 -0
  606. chia/rpc/wallet_rpc_api.py +4779 -0
  607. chia/rpc/wallet_rpc_client.py +1844 -0
  608. chia/seeder/__init__.py +0 -0
  609. chia/seeder/crawl_store.py +427 -0
  610. chia/seeder/crawler.py +423 -0
  611. chia/seeder/crawler_api.py +129 -0
  612. chia/seeder/dns_server.py +544 -0
  613. chia/seeder/peer_record.py +146 -0
  614. chia/seeder/start_crawler.py +88 -0
  615. chia/server/__init__.py +0 -0
  616. chia/server/address_manager.py +658 -0
  617. chia/server/address_manager_store.py +237 -0
  618. chia/server/api_protocol.py +11 -0
  619. chia/server/capabilities.py +24 -0
  620. chia/server/chia_policy.py +345 -0
  621. chia/server/introducer_peers.py +76 -0
  622. chia/server/node_discovery.py +718 -0
  623. chia/server/outbound_message.py +33 -0
  624. chia/server/rate_limit_numbers.py +204 -0
  625. chia/server/rate_limits.py +113 -0
  626. chia/server/server.py +720 -0
  627. chia/server/signal_handlers.py +117 -0
  628. chia/server/ssl_context.py +32 -0
  629. chia/server/start_data_layer.py +137 -0
  630. chia/server/start_farmer.py +86 -0
  631. chia/server/start_full_node.py +106 -0
  632. chia/server/start_harvester.py +80 -0
  633. chia/server/start_introducer.py +69 -0
  634. chia/server/start_service.py +328 -0
  635. chia/server/start_timelord.py +82 -0
  636. chia/server/start_wallet.py +109 -0
  637. chia/server/upnp.py +117 -0
  638. chia/server/ws_connection.py +752 -0
  639. chia/simulator/__init__.py +0 -0
  640. chia/simulator/block_tools.py +2053 -0
  641. chia/simulator/full_node_simulator.py +802 -0
  642. chia/simulator/keyring.py +128 -0
  643. chia/simulator/setup_services.py +505 -0
  644. chia/simulator/simulator_constants.py +13 -0
  645. chia/simulator/simulator_full_node_rpc_api.py +101 -0
  646. chia/simulator/simulator_full_node_rpc_client.py +62 -0
  647. chia/simulator/simulator_protocol.py +29 -0
  648. chia/simulator/simulator_test_tools.py +163 -0
  649. chia/simulator/socket.py +27 -0
  650. chia/simulator/ssl_certs.py +114 -0
  651. chia/simulator/ssl_certs_1.py +699 -0
  652. chia/simulator/ssl_certs_10.py +699 -0
  653. chia/simulator/ssl_certs_2.py +699 -0
  654. chia/simulator/ssl_certs_3.py +699 -0
  655. chia/simulator/ssl_certs_4.py +699 -0
  656. chia/simulator/ssl_certs_5.py +699 -0
  657. chia/simulator/ssl_certs_6.py +699 -0
  658. chia/simulator/ssl_certs_7.py +699 -0
  659. chia/simulator/ssl_certs_8.py +699 -0
  660. chia/simulator/ssl_certs_9.py +699 -0
  661. chia/simulator/start_simulator.py +135 -0
  662. chia/simulator/wallet_tools.py +245 -0
  663. chia/ssl/__init__.py +0 -0
  664. chia/ssl/chia_ca.crt +19 -0
  665. chia/ssl/chia_ca.key +28 -0
  666. chia/ssl/create_ssl.py +249 -0
  667. chia/ssl/dst_root_ca.pem +20 -0
  668. chia/timelord/__init__.py +0 -0
  669. chia/timelord/iters_from_block.py +50 -0
  670. chia/timelord/timelord.py +1202 -0
  671. chia/timelord/timelord_api.py +132 -0
  672. chia/timelord/timelord_launcher.py +188 -0
  673. chia/timelord/timelord_state.py +244 -0
  674. chia/timelord/types.py +22 -0
  675. chia/types/__init__.py +0 -0
  676. chia/types/aliases.py +35 -0
  677. chia/types/block_protocol.py +20 -0
  678. chia/types/blockchain_format/__init__.py +0 -0
  679. chia/types/blockchain_format/classgroup.py +5 -0
  680. chia/types/blockchain_format/coin.py +28 -0
  681. chia/types/blockchain_format/foliage.py +8 -0
  682. chia/types/blockchain_format/pool_target.py +5 -0
  683. chia/types/blockchain_format/program.py +270 -0
  684. chia/types/blockchain_format/proof_of_space.py +135 -0
  685. chia/types/blockchain_format/reward_chain_block.py +6 -0
  686. chia/types/blockchain_format/serialized_program.py +5 -0
  687. chia/types/blockchain_format/sized_bytes.py +11 -0
  688. chia/types/blockchain_format/slots.py +9 -0
  689. chia/types/blockchain_format/sub_epoch_summary.py +5 -0
  690. chia/types/blockchain_format/tree_hash.py +72 -0
  691. chia/types/blockchain_format/vdf.py +86 -0
  692. chia/types/clvm_cost.py +13 -0
  693. chia/types/coin_record.py +43 -0
  694. chia/types/coin_spend.py +115 -0
  695. chia/types/condition_opcodes.py +73 -0
  696. chia/types/condition_with_args.py +17 -0
  697. chia/types/eligible_coin_spends.py +364 -0
  698. chia/types/end_of_slot_bundle.py +5 -0
  699. chia/types/fee_rate.py +38 -0
  700. chia/types/full_block.py +5 -0
  701. chia/types/generator_types.py +14 -0
  702. chia/types/header_block.py +5 -0
  703. chia/types/internal_mempool_item.py +19 -0
  704. chia/types/mempool_inclusion_status.py +9 -0
  705. chia/types/mempool_item.py +85 -0
  706. chia/types/mempool_submission_status.py +30 -0
  707. chia/types/mojos.py +7 -0
  708. chia/types/peer_info.py +64 -0
  709. chia/types/signing_mode.py +29 -0
  710. chia/types/spend_bundle.py +31 -0
  711. chia/types/spend_bundle_conditions.py +7 -0
  712. chia/types/transaction_queue_entry.py +55 -0
  713. chia/types/unfinished_block.py +5 -0
  714. chia/types/unfinished_header_block.py +37 -0
  715. chia/types/weight_proof.py +50 -0
  716. chia/util/__init__.py +0 -0
  717. chia/util/action_scope.py +168 -0
  718. chia/util/api_decorators.py +89 -0
  719. chia/util/async_pool.py +224 -0
  720. chia/util/augmented_chain.py +130 -0
  721. chia/util/batches.py +39 -0
  722. chia/util/bech32m.py +123 -0
  723. chia/util/beta_metrics.py +118 -0
  724. chia/util/block_cache.py +56 -0
  725. chia/util/byte_types.py +10 -0
  726. chia/util/check_fork_next_block.py +32 -0
  727. chia/util/chia_logging.py +124 -0
  728. chia/util/chia_version.py +33 -0
  729. chia/util/collection.py +17 -0
  730. chia/util/condition_tools.py +201 -0
  731. chia/util/config.py +366 -0
  732. chia/util/cpu.py +20 -0
  733. chia/util/db_synchronous.py +21 -0
  734. chia/util/db_version.py +30 -0
  735. chia/util/db_wrapper.py +427 -0
  736. chia/util/default_root.py +10 -0
  737. chia/util/dump_keyring.py +93 -0
  738. chia/util/english.txt +2048 -0
  739. chia/util/errors.py +351 -0
  740. chia/util/file_keyring.py +480 -0
  741. chia/util/files.py +95 -0
  742. chia/util/full_block_utils.py +321 -0
  743. chia/util/generator_tools.py +62 -0
  744. chia/util/hash.py +29 -0
  745. chia/util/initial-config.yaml +675 -0
  746. chia/util/inline_executor.py +24 -0
  747. chia/util/ints.py +19 -0
  748. chia/util/json_util.py +41 -0
  749. chia/util/keychain.py +673 -0
  750. chia/util/keyring_wrapper.py +266 -0
  751. chia/util/limited_semaphore.py +39 -0
  752. chia/util/lock.py +47 -0
  753. chia/util/log_exceptions.py +29 -0
  754. chia/util/logging.py +34 -0
  755. chia/util/lru_cache.py +29 -0
  756. chia/util/math.py +20 -0
  757. chia/util/network.py +240 -0
  758. chia/util/paginator.py +46 -0
  759. chia/util/path.py +29 -0
  760. chia/util/permissions.py +19 -0
  761. chia/util/pprint.py +40 -0
  762. chia/util/prev_transaction_block.py +23 -0
  763. chia/util/priority_mutex.py +92 -0
  764. chia/util/profiler.py +194 -0
  765. chia/util/recursive_replace.py +22 -0
  766. chia/util/safe_cancel_task.py +14 -0
  767. chia/util/service_groups.py +47 -0
  768. chia/util/setproctitle.py +20 -0
  769. chia/util/significant_bits.py +30 -0
  770. chia/util/ssl_check.py +213 -0
  771. chia/util/streamable.py +654 -0
  772. chia/util/task_timing.py +378 -0
  773. chia/util/timing.py +64 -0
  774. chia/util/vdf_prover.py +31 -0
  775. chia/util/ws_message.py +66 -0
  776. chia/wallet/__init__.py +0 -0
  777. chia/wallet/cat_wallet/__init__.py +0 -0
  778. chia/wallet/cat_wallet/cat_constants.py +75 -0
  779. chia/wallet/cat_wallet/cat_info.py +47 -0
  780. chia/wallet/cat_wallet/cat_outer_puzzle.py +120 -0
  781. chia/wallet/cat_wallet/cat_utils.py +163 -0
  782. chia/wallet/cat_wallet/cat_wallet.py +869 -0
  783. chia/wallet/cat_wallet/dao_cat_info.py +28 -0
  784. chia/wallet/cat_wallet/dao_cat_wallet.py +669 -0
  785. chia/wallet/cat_wallet/lineage_store.py +74 -0
  786. chia/wallet/cat_wallet/puzzles/__init__.py +0 -0
  787. chia/wallet/cat_wallet/puzzles/cat_truths.clib +31 -0
  788. chia/wallet/cat_wallet/puzzles/cat_v2.clsp +397 -0
  789. chia/wallet/cat_wallet/puzzles/cat_v2.clsp.hex +1 -0
  790. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp +25 -0
  791. chia/wallet/cat_wallet/puzzles/delegated_tail.clsp.hex +1 -0
  792. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp +15 -0
  793. chia/wallet/cat_wallet/puzzles/everything_with_signature.clsp.hex +1 -0
  794. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp +26 -0
  795. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id.clsp.hex +1 -0
  796. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp +42 -0
  797. chia/wallet/cat_wallet/puzzles/genesis_by_coin_id_or_singleton.clsp.hex +1 -0
  798. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp +24 -0
  799. chia/wallet/cat_wallet/puzzles/genesis_by_puzzle_hash.clsp.hex +1 -0
  800. chia/wallet/coin_selection.py +188 -0
  801. chia/wallet/conditions.py +1326 -0
  802. chia/wallet/dao_wallet/__init__.py +0 -0
  803. chia/wallet/dao_wallet/dao_info.py +61 -0
  804. chia/wallet/dao_wallet/dao_utils.py +810 -0
  805. chia/wallet/dao_wallet/dao_wallet.py +2121 -0
  806. chia/wallet/db_wallet/__init__.py +0 -0
  807. chia/wallet/db_wallet/db_wallet_puzzles.py +107 -0
  808. chia/wallet/derivation_record.py +30 -0
  809. chia/wallet/derive_keys.py +146 -0
  810. chia/wallet/did_wallet/__init__.py +0 -0
  811. chia/wallet/did_wallet/did_info.py +39 -0
  812. chia/wallet/did_wallet/did_wallet.py +1485 -0
  813. chia/wallet/did_wallet/did_wallet_puzzles.py +220 -0
  814. chia/wallet/did_wallet/puzzles/__init__.py +0 -0
  815. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp +135 -0
  816. chia/wallet/did_wallet/puzzles/did_innerpuz.clsp.hex +1 -0
  817. chia/wallet/driver_protocol.py +26 -0
  818. chia/wallet/key_val_store.py +55 -0
  819. chia/wallet/lineage_proof.py +58 -0
  820. chia/wallet/nft_wallet/__init__.py +0 -0
  821. chia/wallet/nft_wallet/metadata_outer_puzzle.py +92 -0
  822. chia/wallet/nft_wallet/nft_info.py +120 -0
  823. chia/wallet/nft_wallet/nft_puzzles.py +305 -0
  824. chia/wallet/nft_wallet/nft_wallet.py +1686 -0
  825. chia/wallet/nft_wallet/ownership_outer_puzzle.py +101 -0
  826. chia/wallet/nft_wallet/puzzles/__init__.py +0 -0
  827. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp +6 -0
  828. chia/wallet/nft_wallet/puzzles/create_nft_launcher_from_did.clsp.hex +1 -0
  829. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp +6 -0
  830. chia/wallet/nft_wallet/puzzles/nft_intermediate_launcher.clsp.hex +1 -0
  831. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp +30 -0
  832. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_default.clsp.hex +1 -0
  833. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp +28 -0
  834. chia/wallet/nft_wallet/puzzles/nft_metadata_updater_updateable.clsp.hex +1 -0
  835. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp +100 -0
  836. chia/wallet/nft_wallet/puzzles/nft_ownership_layer.clsp.hex +1 -0
  837. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp +78 -0
  838. chia/wallet/nft_wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clsp.hex +1 -0
  839. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp +74 -0
  840. chia/wallet/nft_wallet/puzzles/nft_state_layer.clsp.hex +1 -0
  841. chia/wallet/nft_wallet/singleton_outer_puzzle.py +101 -0
  842. chia/wallet/nft_wallet/transfer_program_puzzle.py +82 -0
  843. chia/wallet/nft_wallet/uncurry_nft.py +217 -0
  844. chia/wallet/notification_manager.py +117 -0
  845. chia/wallet/notification_store.py +178 -0
  846. chia/wallet/outer_puzzles.py +84 -0
  847. chia/wallet/payment.py +34 -0
  848. chia/wallet/puzzle_drivers.py +118 -0
  849. chia/wallet/puzzles/__init__.py +0 -0
  850. chia/wallet/puzzles/augmented_condition.clsp +13 -0
  851. chia/wallet/puzzles/augmented_condition.clsp.hex +1 -0
  852. chia/wallet/puzzles/clawback/__init__.py +0 -0
  853. chia/wallet/puzzles/clawback/drivers.py +188 -0
  854. chia/wallet/puzzles/clawback/metadata.py +38 -0
  855. chia/wallet/puzzles/clawback/puzzle_decorator.py +67 -0
  856. chia/wallet/puzzles/condition_codes.clib +77 -0
  857. chia/wallet/puzzles/curry-and-treehash.clib +102 -0
  858. chia/wallet/puzzles/curry.clib +135 -0
  859. chia/wallet/puzzles/curry_by_index.clib +16 -0
  860. chia/wallet/puzzles/dao_cat_eve.clsp +17 -0
  861. chia/wallet/puzzles/dao_cat_eve.clsp.hex +1 -0
  862. chia/wallet/puzzles/dao_cat_launcher.clsp +36 -0
  863. chia/wallet/puzzles/dao_cat_launcher.clsp.hex +1 -0
  864. chia/wallet/puzzles/dao_finished_state.clsp +35 -0
  865. chia/wallet/puzzles/dao_finished_state.clsp.hex +1 -0
  866. chia/wallet/puzzles/dao_finished_state.clsp.hex.sha256tree +1 -0
  867. chia/wallet/puzzles/dao_lockup.clsp +288 -0
  868. chia/wallet/puzzles/dao_lockup.clsp.hex +1 -0
  869. chia/wallet/puzzles/dao_lockup.clsp.hex.sha256tree +1 -0
  870. chia/wallet/puzzles/dao_proposal.clsp +377 -0
  871. chia/wallet/puzzles/dao_proposal.clsp.hex +1 -0
  872. chia/wallet/puzzles/dao_proposal.clsp.hex.sha256tree +1 -0
  873. chia/wallet/puzzles/dao_proposal_timer.clsp +78 -0
  874. chia/wallet/puzzles/dao_proposal_timer.clsp.hex +1 -0
  875. chia/wallet/puzzles/dao_proposal_timer.clsp.hex.sha256tree +1 -0
  876. chia/wallet/puzzles/dao_proposal_validator.clsp +87 -0
  877. chia/wallet/puzzles/dao_proposal_validator.clsp.hex +1 -0
  878. chia/wallet/puzzles/dao_proposal_validator.clsp.hex.sha256tree +1 -0
  879. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp +240 -0
  880. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex +1 -0
  881. chia/wallet/puzzles/dao_spend_p2_singleton_v2.clsp.hex.sha256tree +1 -0
  882. chia/wallet/puzzles/dao_treasury.clsp +115 -0
  883. chia/wallet/puzzles/dao_treasury.clsp.hex +1 -0
  884. chia/wallet/puzzles/dao_update_proposal.clsp +44 -0
  885. chia/wallet/puzzles/dao_update_proposal.clsp.hex +1 -0
  886. chia/wallet/puzzles/deployed_puzzle_hashes.json +67 -0
  887. chia/wallet/puzzles/json.clib +25 -0
  888. chia/wallet/puzzles/load_clvm.py +162 -0
  889. chia/wallet/puzzles/merkle_utils.clib +18 -0
  890. chia/wallet/puzzles/notification.clsp +7 -0
  891. chia/wallet/puzzles/notification.clsp.hex +1 -0
  892. chia/wallet/puzzles/p2_1_of_n.clsp +22 -0
  893. chia/wallet/puzzles/p2_1_of_n.clsp.hex +1 -0
  894. chia/wallet/puzzles/p2_conditions.clsp +3 -0
  895. chia/wallet/puzzles/p2_conditions.clsp.hex +1 -0
  896. chia/wallet/puzzles/p2_conditions.py +27 -0
  897. chia/wallet/puzzles/p2_delegated_conditions.clsp +18 -0
  898. chia/wallet/puzzles/p2_delegated_conditions.clsp.hex +1 -0
  899. chia/wallet/puzzles/p2_delegated_conditions.py +22 -0
  900. chia/wallet/puzzles/p2_delegated_puzzle.clsp +19 -0
  901. chia/wallet/puzzles/p2_delegated_puzzle.clsp.hex +1 -0
  902. chia/wallet/puzzles/p2_delegated_puzzle.py +35 -0
  903. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp +91 -0
  904. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.clsp.hex +1 -0
  905. chia/wallet/puzzles/p2_delegated_puzzle_or_hidden_puzzle.py +161 -0
  906. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp +108 -0
  907. chia/wallet/puzzles/p2_m_of_n_delegate_direct.clsp.hex +1 -0
  908. chia/wallet/puzzles/p2_m_of_n_delegate_direct.py +22 -0
  909. chia/wallet/puzzles/p2_parent.clsp +19 -0
  910. chia/wallet/puzzles/p2_parent.clsp.hex +1 -0
  911. chia/wallet/puzzles/p2_puzzle_hash.clsp +18 -0
  912. chia/wallet/puzzles/p2_puzzle_hash.clsp.hex +1 -0
  913. chia/wallet/puzzles/p2_puzzle_hash.py +28 -0
  914. chia/wallet/puzzles/p2_singleton.clsp +30 -0
  915. chia/wallet/puzzles/p2_singleton.clsp.hex +1 -0
  916. chia/wallet/puzzles/p2_singleton_aggregator.clsp +81 -0
  917. chia/wallet/puzzles/p2_singleton_aggregator.clsp.hex +1 -0
  918. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp +50 -0
  919. chia/wallet/puzzles/p2_singleton_or_delayed_puzhash.clsp.hex +1 -0
  920. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp +47 -0
  921. chia/wallet/puzzles/p2_singleton_via_delegated_puzzle.clsp.hex +1 -0
  922. chia/wallet/puzzles/puzzle_utils.py +34 -0
  923. chia/wallet/puzzles/settlement_payments.clsp +49 -0
  924. chia/wallet/puzzles/settlement_payments.clsp.hex +1 -0
  925. chia/wallet/puzzles/sha256tree.clib +11 -0
  926. chia/wallet/puzzles/singleton_launcher.clsp +16 -0
  927. chia/wallet/puzzles/singleton_launcher.clsp.hex +1 -0
  928. chia/wallet/puzzles/singleton_top_layer.clsp +177 -0
  929. chia/wallet/puzzles/singleton_top_layer.clsp.hex +1 -0
  930. chia/wallet/puzzles/singleton_top_layer.py +295 -0
  931. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp +107 -0
  932. chia/wallet/puzzles/singleton_top_layer_v1_1.clsp.hex +1 -0
  933. chia/wallet/puzzles/singleton_top_layer_v1_1.py +344 -0
  934. chia/wallet/puzzles/singleton_truths.clib +21 -0
  935. chia/wallet/puzzles/tails.py +344 -0
  936. chia/wallet/puzzles/utility_macros.clib +48 -0
  937. chia/wallet/signer_protocol.py +126 -0
  938. chia/wallet/singleton.py +106 -0
  939. chia/wallet/singleton_record.py +30 -0
  940. chia/wallet/trade_manager.py +1088 -0
  941. chia/wallet/trade_record.py +67 -0
  942. chia/wallet/trading/__init__.py +0 -0
  943. chia/wallet/trading/offer.py +703 -0
  944. chia/wallet/trading/trade_status.py +13 -0
  945. chia/wallet/trading/trade_store.py +526 -0
  946. chia/wallet/transaction_record.py +143 -0
  947. chia/wallet/transaction_sorting.py +14 -0
  948. chia/wallet/uncurried_puzzle.py +17 -0
  949. chia/wallet/util/__init__.py +0 -0
  950. chia/wallet/util/address_type.py +55 -0
  951. chia/wallet/util/blind_signer_tl.py +168 -0
  952. chia/wallet/util/clvm_streamable.py +203 -0
  953. chia/wallet/util/compute_hints.py +66 -0
  954. chia/wallet/util/compute_memos.py +45 -0
  955. chia/wallet/util/curry_and_treehash.py +90 -0
  956. chia/wallet/util/debug_spend_bundle.py +234 -0
  957. chia/wallet/util/merkle_tree.py +100 -0
  958. chia/wallet/util/merkle_utils.py +102 -0
  959. chia/wallet/util/new_peak_queue.py +82 -0
  960. chia/wallet/util/notifications.py +12 -0
  961. chia/wallet/util/peer_request_cache.py +174 -0
  962. chia/wallet/util/puzzle_compression.py +96 -0
  963. chia/wallet/util/puzzle_decorator.py +100 -0
  964. chia/wallet/util/puzzle_decorator_type.py +7 -0
  965. chia/wallet/util/query_filter.py +60 -0
  966. chia/wallet/util/transaction_type.py +23 -0
  967. chia/wallet/util/tx_config.py +158 -0
  968. chia/wallet/util/wallet_sync_utils.py +348 -0
  969. chia/wallet/util/wallet_types.py +65 -0
  970. chia/wallet/vc_wallet/__init__.py +0 -0
  971. chia/wallet/vc_wallet/cr_cat_drivers.py +663 -0
  972. chia/wallet/vc_wallet/cr_cat_wallet.py +875 -0
  973. chia/wallet/vc_wallet/cr_outer_puzzle.py +102 -0
  974. chia/wallet/vc_wallet/cr_puzzles/__init__.py +0 -0
  975. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp +3 -0
  976. chia/wallet/vc_wallet/cr_puzzles/conditions_w_fee_announce.clsp.hex +1 -0
  977. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp +304 -0
  978. chia/wallet/vc_wallet/cr_puzzles/credential_restriction.clsp.hex +1 -0
  979. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp +45 -0
  980. chia/wallet/vc_wallet/cr_puzzles/flag_proofs_checker.clsp.hex +1 -0
  981. chia/wallet/vc_wallet/vc_drivers.py +838 -0
  982. chia/wallet/vc_wallet/vc_puzzles/__init__.py +0 -0
  983. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp +30 -0
  984. chia/wallet/vc_wallet/vc_puzzles/covenant_layer.clsp.hex +1 -0
  985. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp +75 -0
  986. chia/wallet/vc_wallet/vc_puzzles/eml_covenant_morpher.clsp.hex +1 -0
  987. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp +32 -0
  988. chia/wallet/vc_wallet/vc_puzzles/eml_transfer_program_covenant_adapter.clsp.hex +1 -0
  989. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp +80 -0
  990. chia/wallet/vc_wallet/vc_puzzles/eml_update_metadata_with_DID.clsp.hex +1 -0
  991. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp +163 -0
  992. chia/wallet/vc_wallet/vc_puzzles/exigent_metadata_layer.clsp.hex +1 -0
  993. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp +16 -0
  994. chia/wallet/vc_wallet/vc_puzzles/p2_announced_delegated_puzzle.clsp.hex +1 -0
  995. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp +74 -0
  996. chia/wallet/vc_wallet/vc_puzzles/standard_vc_backdoor_puzzle.clsp.hex +1 -0
  997. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp +23 -0
  998. chia/wallet/vc_wallet/vc_puzzles/std_parent_morpher.clsp.hex +1 -0
  999. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp +64 -0
  1000. chia/wallet/vc_wallet/vc_puzzles/viral_backdoor.clsp.hex +1 -0
  1001. chia/wallet/vc_wallet/vc_store.py +263 -0
  1002. chia/wallet/vc_wallet/vc_wallet.py +638 -0
  1003. chia/wallet/wallet.py +698 -0
  1004. chia/wallet/wallet_action_scope.py +95 -0
  1005. chia/wallet/wallet_blockchain.py +244 -0
  1006. chia/wallet/wallet_coin_record.py +72 -0
  1007. chia/wallet/wallet_coin_store.py +351 -0
  1008. chia/wallet/wallet_info.py +36 -0
  1009. chia/wallet/wallet_interested_store.py +188 -0
  1010. chia/wallet/wallet_nft_store.py +279 -0
  1011. chia/wallet/wallet_node.py +1769 -0
  1012. chia/wallet/wallet_node_api.py +201 -0
  1013. chia/wallet/wallet_pool_store.py +120 -0
  1014. chia/wallet/wallet_protocol.py +90 -0
  1015. chia/wallet/wallet_puzzle_store.py +365 -0
  1016. chia/wallet/wallet_retry_store.py +70 -0
  1017. chia/wallet/wallet_singleton_store.py +258 -0
  1018. chia/wallet/wallet_spend_bundle.py +41 -0
  1019. chia/wallet/wallet_state_manager.py +2820 -0
  1020. chia/wallet/wallet_transaction_store.py +470 -0
  1021. chia/wallet/wallet_user_store.py +110 -0
  1022. chia/wallet/wallet_weight_proof_handler.py +126 -0
  1023. chia_blockchain-2.4.4.dist-info/LICENSE +201 -0
  1024. chia_blockchain-2.4.4.dist-info/METADATA +161 -0
  1025. chia_blockchain-2.4.4.dist-info/RECORD +1028 -0
  1026. chia_blockchain-2.4.4.dist-info/WHEEL +4 -0
  1027. chia_blockchain-2.4.4.dist-info/entry_points.txt +17 -0
  1028. mozilla-ca/cacert.pem +3666 -0
@@ -0,0 +1,2121 @@
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, Dict, List, Optional, Set, Tuple, 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 CAT_MOD, SpendableCAT, construct_cat_puzzle
25
+ from chia.wallet.cat_wallet.cat_utils import get_innerpuzzle_from_puzzle as get_innerpuzzle_from_cat_puzzle
26
+ from chia.wallet.cat_wallet.cat_utils import unsigned_spend_bundle_for_spendable_cats
27
+ from chia.wallet.cat_wallet.cat_wallet import CATWallet
28
+ from chia.wallet.cat_wallet.dao_cat_wallet import DAOCATWallet
29
+ from chia.wallet.coin_selection import select_coins
30
+ from chia.wallet.conditions import AssertCoinAnnouncement, Condition, parse_timelock_info
31
+ from chia.wallet.dao_wallet.dao_info import DAOInfo, DAORules, ProposalInfo, ProposalType
32
+ from chia.wallet.dao_wallet.dao_utils import (
33
+ DAO_FINISHED_STATE,
34
+ DAO_PROPOSAL_MOD,
35
+ DAO_TREASURY_MOD,
36
+ SINGLETON_LAUNCHER,
37
+ create_cat_launcher_for_singleton_id,
38
+ curry_cat_eve,
39
+ curry_singleton,
40
+ generate_cat_tail,
41
+ get_active_votes_from_lockup_puzzle,
42
+ get_asset_id_from_puzzle,
43
+ get_dao_rules_from_update_proposal,
44
+ get_finished_state_inner_puzzle,
45
+ get_finished_state_puzzle,
46
+ get_innerpuz_from_lockup_puzzle,
47
+ get_new_puzzle_from_proposal_solution,
48
+ get_new_puzzle_from_treasury_solution,
49
+ get_p2_singleton_puzhash,
50
+ get_p2_singleton_puzzle,
51
+ get_proposal_args,
52
+ get_proposal_puzzle,
53
+ get_proposal_timer_puzzle,
54
+ get_proposed_puzzle_reveal_from_solution,
55
+ get_treasury_puzzle,
56
+ get_treasury_rules_from_puzzle,
57
+ match_funding_puzzle,
58
+ uncurry_proposal,
59
+ uncurry_treasury,
60
+ )
61
+ from chia.wallet.lineage_proof import LineageProof
62
+ from chia.wallet.singleton import (
63
+ get_inner_puzzle_from_singleton,
64
+ get_most_recent_singleton_coin_from_coin_spend,
65
+ get_singleton_id_from_puzzle,
66
+ get_singleton_struct_for_id,
67
+ )
68
+ from chia.wallet.transaction_record import TransactionRecord
69
+ from chia.wallet.uncurried_puzzle import uncurry_puzzle
70
+ from chia.wallet.util.transaction_type import TransactionType
71
+ from chia.wallet.util.wallet_sync_utils import fetch_coin_spend
72
+ from chia.wallet.util.wallet_types import WalletType
73
+ from chia.wallet.wallet import Wallet
74
+ from chia.wallet.wallet_action_scope import WalletActionScope
75
+ from chia.wallet.wallet_coin_record import WalletCoinRecord
76
+ from chia.wallet.wallet_info import WalletInfo
77
+ from chia.wallet.wallet_spend_bundle import WalletSpendBundle
78
+
79
+
80
+ class DAOWallet:
81
+ """
82
+ This is a wallet in the sense that it conforms to the interface needed by WalletStateManager.
83
+ It is not a user-facing wallet. A user cannot spend or receive XCH though a wallet of this type.
84
+
85
+ Wallets of type CAT and DAO_CAT are the user-facing wallets which hold the voting tokens a user
86
+ owns. The DAO Wallet is used for state-tracking of the Treasury Singleton and its associated
87
+ Proposals.
88
+
89
+ State change Spends (spends this user creates, either from DAOWallet or DAOCATWallet:
90
+ * Create a proposal
91
+ * Add more votes to a proposal
92
+ * Lock / Unlock voting tokens
93
+ * Collect finished state of a Proposal - spend to read the oracle result and Get our CAT coins back
94
+ * Anyone can send money to the Treasury, whether in possession of a voting CAT or not
95
+
96
+ Incoming spends we listen for:
97
+ * Update Treasury state if treasury is spent
98
+ * Hear about a finished proposal
99
+ * Hear about a new proposal -- check interest threshold (how many votes)
100
+ * Get Updated Proposal Data
101
+ """
102
+
103
+ if TYPE_CHECKING:
104
+ from chia.wallet.wallet_protocol import WalletProtocol
105
+
106
+ _protocol_check: ClassVar[WalletProtocol[DAOInfo]] = cast("DAOWallet", None)
107
+
108
+ wallet_state_manager: Any
109
+ log: logging.Logger
110
+ wallet_info: WalletInfo
111
+ dao_info: DAOInfo
112
+ dao_rules: DAORules
113
+ standard_wallet: Wallet
114
+ wallet_id: uint32
115
+
116
+ @staticmethod
117
+ async def create_new_dao_and_wallet(
118
+ wallet_state_manager: Any,
119
+ wallet: Wallet,
120
+ amount_of_cats: uint64,
121
+ dao_rules: DAORules,
122
+ action_scope: WalletActionScope,
123
+ filter_amount: uint64 = uint64(1),
124
+ name: Optional[str] = None,
125
+ fee: uint64 = uint64(0),
126
+ fee_for_cat: uint64 = uint64(0),
127
+ ) -> DAOWallet:
128
+ """
129
+ Create a brand new DAO wallet
130
+ This must be called under the wallet state manager lock
131
+ :param wallet_state_manager: Wallet state manager
132
+ :param wallet: Standard wallet
133
+ :param amount_of_cats: Initial amount of voting CATs
134
+ :param dao_rules: The rules which govern the DAO
135
+ :param filter_amount: Min votes to see proposal (user defined)
136
+ :param name: Wallet name
137
+ :param fee: transaction fee
138
+ :param fee_for_cat: transaction fee for creating the CATs
139
+ :return: DAO wallet
140
+ """
141
+
142
+ self = DAOWallet()
143
+ self.wallet_state_manager = wallet_state_manager
144
+ if name is None:
145
+ name = self.generate_wallet_name()
146
+
147
+ self.standard_wallet = wallet
148
+ self.log = logging.getLogger(name if name else __name__)
149
+ std_wallet_id = self.standard_wallet.wallet_id
150
+ bal = await wallet_state_manager.get_confirmed_balance_for_wallet(std_wallet_id)
151
+ if amount_of_cats > bal:
152
+ raise ValueError(f"Your balance of {bal} mojos is not enough to create {amount_of_cats} CATs")
153
+
154
+ self.dao_info = DAOInfo(
155
+ treasury_id=bytes32([0] * 32),
156
+ cat_wallet_id=uint32(0),
157
+ dao_cat_wallet_id=uint32(0),
158
+ proposals_list=[],
159
+ parent_info=[],
160
+ current_treasury_coin=None,
161
+ current_treasury_innerpuz=None,
162
+ singleton_block_height=uint32(0),
163
+ filter_below_vote_amount=filter_amount,
164
+ assets=[],
165
+ current_height=uint64(0),
166
+ )
167
+ self.dao_rules = dao_rules
168
+ info_as_string = json.dumps(self.dao_info.to_json_dict())
169
+ self.wallet_info = await wallet_state_manager.user_store.create_wallet(
170
+ name, WalletType.DAO.value, info_as_string
171
+ )
172
+ self.wallet_id = self.wallet_info.id
173
+ std_wallet_id = self.standard_wallet.wallet_id
174
+
175
+ try:
176
+ await self.generate_new_dao(
177
+ amount_of_cats,
178
+ action_scope,
179
+ fee=fee,
180
+ fee_for_cat=fee_for_cat,
181
+ )
182
+ except Exception as e_info: # pragma: no cover
183
+ await wallet_state_manager.user_store.delete_wallet(self.id())
184
+ self.log.exception(f"Failed to create dao wallet: {e_info}")
185
+ raise
186
+
187
+ await self.wallet_state_manager.add_new_wallet(self)
188
+
189
+ # Now the dao wallet is created we can create the dao_cat wallet
190
+ cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
191
+ cat_tail = cat_wallet.cat_info.limitations_program_hash
192
+ new_dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
193
+ self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
194
+ )
195
+ dao_cat_wallet_id = new_dao_cat_wallet.wallet_info.id
196
+ dao_info = dataclasses.replace(
197
+ self.dao_info, cat_wallet_id=cat_wallet.id(), dao_cat_wallet_id=dao_cat_wallet_id
198
+ )
199
+ await self.save_info(dao_info)
200
+
201
+ return self
202
+
203
+ @staticmethod
204
+ async def create_new_dao_wallet_for_existing_dao(
205
+ wallet_state_manager: Any,
206
+ main_wallet: Wallet,
207
+ treasury_id: bytes32,
208
+ filter_amount: uint64 = uint64(1),
209
+ name: Optional[str] = None,
210
+ ) -> DAOWallet:
211
+ """
212
+ Create a DAO wallet for existing DAO
213
+ :param wallet_state_manager: Wallet state manager
214
+ :param main_wallet: Standard wallet
215
+ :param treasury_id: The singleton ID of the DAO treasury coin
216
+ :param filter_amount: Min votes to see proposal (user defined)
217
+ :param name: Wallet name
218
+ :return: DAO wallet
219
+ """
220
+ self = DAOWallet()
221
+ self.wallet_state_manager = wallet_state_manager
222
+ if name is None:
223
+ name = self.generate_wallet_name()
224
+
225
+ self.standard_wallet = main_wallet
226
+ self.log = logging.getLogger(name if name else __name__)
227
+ self.log.info("Creating DAO wallet for existent DAO ...")
228
+ self.dao_info = DAOInfo(
229
+ treasury_id=treasury_id,
230
+ cat_wallet_id=uint32(0),
231
+ dao_cat_wallet_id=uint32(0),
232
+ proposals_list=[],
233
+ parent_info=[],
234
+ current_treasury_coin=None,
235
+ current_treasury_innerpuz=None,
236
+ singleton_block_height=uint32(0),
237
+ filter_below_vote_amount=filter_amount,
238
+ assets=[],
239
+ current_height=uint64(0),
240
+ )
241
+ info_as_string = json.dumps(self.dao_info.to_json_dict())
242
+ self.wallet_info = await wallet_state_manager.user_store.create_wallet(
243
+ name, WalletType.DAO.value, info_as_string
244
+ )
245
+ await self.wallet_state_manager.add_new_wallet(self)
246
+ await self.resync_treasury_state()
247
+ await self.save_info(self.dao_info)
248
+ self.wallet_id = self.wallet_info.id
249
+
250
+ # Now the dao wallet is created we can create the dao_cat wallet
251
+ cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
252
+ cat_tail = cat_wallet.cat_info.limitations_program_hash
253
+ new_dao_cat_wallet = await DAOCATWallet.get_or_create_wallet_for_cat(
254
+ self.wallet_state_manager, self.standard_wallet, cat_tail.hex()
255
+ )
256
+ dao_cat_wallet_id = new_dao_cat_wallet.wallet_info.id
257
+ dao_info = dataclasses.replace(
258
+ self.dao_info, cat_wallet_id=cat_wallet.id(), dao_cat_wallet_id=dao_cat_wallet_id
259
+ )
260
+ await self.save_info(dao_info)
261
+
262
+ # add treasury id to interested puzzle hashes. This is hinted in funding coins so we can track them
263
+ funding_inner_hash = get_p2_singleton_puzhash(self.dao_info.treasury_id)
264
+ await self.wallet_state_manager.add_interested_puzzle_hashes(
265
+ [self.dao_info.treasury_id, funding_inner_hash], [self.id(), self.id()]
266
+ )
267
+ return self
268
+
269
+ @staticmethod
270
+ async def create(
271
+ wallet_state_manager: Any,
272
+ wallet: Wallet,
273
+ wallet_info: WalletInfo,
274
+ name: Optional[str] = None,
275
+ ) -> DAOWallet:
276
+ """
277
+ Create a DID wallet based on the local database
278
+ :param wallet_state_manager: Wallet state manager
279
+ :param wallet: Standard wallet
280
+ :param wallet_info: Serialized WalletInfo
281
+ :param name: Wallet name
282
+ :return:
283
+ """
284
+ self = DAOWallet()
285
+ self.log = logging.getLogger(name if name else __name__)
286
+ self.wallet_state_manager = wallet_state_manager
287
+ self.wallet_info = wallet_info
288
+ self.wallet_id = wallet_info.id
289
+ self.standard_wallet = wallet
290
+ self.dao_info = DAOInfo.from_json_dict(json.loads(wallet_info.data))
291
+ self.dao_rules = get_treasury_rules_from_puzzle(self.dao_info.current_treasury_innerpuz)
292
+ return self
293
+
294
+ @classmethod
295
+ def type(cls) -> WalletType:
296
+ return WalletType.DAO
297
+
298
+ def id(self) -> uint32:
299
+ return self.wallet_info.id
300
+
301
+ async def set_name(self, new_name: str) -> None:
302
+ new_info = dataclasses.replace(self.wallet_info, name=new_name)
303
+ self.wallet_info = new_info
304
+ await self.wallet_state_manager.user_store.update_wallet(self.wallet_info)
305
+
306
+ def get_name(self) -> str:
307
+ return self.wallet_info.name
308
+
309
+ async def match_hinted_coin(self, coin: Coin, hint: bytes32) -> bool:
310
+ raise NotImplementedError("Method not implemented for DAO Wallet") # pragma: no cover
311
+
312
+ def puzzle_hash_for_pk(self, pubkey: G1Element) -> bytes32:
313
+ raise NotImplementedError("puzzle_hash_for_pk is not available in DAO wallets") # pragma: no cover
314
+
315
+ async def get_new_p2_inner_hash(self) -> bytes32:
316
+ puzzle = await self.get_new_p2_inner_puzzle()
317
+ return puzzle.get_tree_hash()
318
+
319
+ async def get_new_p2_inner_puzzle(self) -> Program:
320
+ return await self.standard_wallet.get_new_puzzle()
321
+
322
+ def get_parent_for_coin(self, coin: Coin) -> Optional[LineageProof]:
323
+ parent_info = None
324
+ for name, ccparent in self.dao_info.parent_info:
325
+ if name == coin.parent_coin_info:
326
+ parent_info = ccparent
327
+ return parent_info
328
+
329
+ async def get_max_send_amount(self, records: Optional[Set[WalletCoinRecord]] = None) -> uint128:
330
+ return uint128(0) # pragma: no cover
331
+
332
+ async def get_spendable_balance(self, unspent_records: Optional[Set[WalletCoinRecord]] = None) -> uint128:
333
+ # No spendable or receivable value
334
+ return uint128(1)
335
+
336
+ async def get_confirmed_balance(self, record_list: Optional[Set[WalletCoinRecord]] = None) -> uint128:
337
+ # No spendable or receivable value
338
+ return uint128(1)
339
+
340
+ async def select_coins(
341
+ self,
342
+ amount: uint64,
343
+ action_scope: WalletActionScope,
344
+ ) -> Set[Coin]:
345
+ """
346
+ Returns a set of coins that can be used for generating a new transaction.
347
+ Note: Must be called under wallet state manager lock
348
+ There is no need for max/min coin amount or excluded amount because the dao treasury should
349
+ always be a single coin with amount 1
350
+ """
351
+ spendable_amount: uint128 = await self.get_spendable_balance()
352
+ if amount > spendable_amount:
353
+ self.log.warning(f"Can't select {amount}, from spendable {spendable_amount} for wallet id {self.id()}")
354
+ return set()
355
+
356
+ spendable_coins: List[WalletCoinRecord] = list(
357
+ await self.wallet_state_manager.get_spendable_coins_for_wallet(self.wallet_info.id)
358
+ )
359
+
360
+ # Try to use coins from the store, if there isn't enough of "unused"
361
+ # coins use change coins that are not confirmed yet
362
+ unconfirmed_removals: Dict[bytes32, Coin] = await self.wallet_state_manager.unconfirmed_removals_for_wallet(
363
+ self.wallet_info.id
364
+ )
365
+ async with action_scope.use() as interface:
366
+ coins = await select_coins(
367
+ spendable_amount,
368
+ action_scope.config.adjust_for_side_effects(interface.side_effects).tx_config.coin_selection_config,
369
+ spendable_coins,
370
+ unconfirmed_removals,
371
+ self.log,
372
+ uint128(amount),
373
+ )
374
+ interface.side_effects.selected_coins.extend([*coins])
375
+ assert sum(c.amount for c in coins) >= amount
376
+ return coins
377
+
378
+ async def get_pending_change_balance(self) -> uint64:
379
+ # No spendable or receivable value
380
+ return uint64(0)
381
+
382
+ async def get_unconfirmed_balance(self, record_list: Optional[Set[WalletCoinRecord]] = None) -> uint128:
383
+ # No spendable or receivable value
384
+ return uint128(1)
385
+
386
+ # if asset_id == None: then we get normal XCH
387
+ async def get_balance_by_asset_type(self, asset_id: Optional[bytes32] = None) -> uint128:
388
+ puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
389
+ records = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(puzhash)
390
+ return uint128(sum(cr.coin.amount for cr in records if not cr.spent))
391
+
392
+ # if asset_id == None: then we get normal XCH
393
+ async def select_coins_for_asset_type(
394
+ self, amount: uint64, action_scope: WalletActionScope, asset_id: Optional[bytes32] = None
395
+ ) -> List[Coin]:
396
+ puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
397
+ records = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(puzhash)
398
+ unspent_records = [r for r in records if not r.spent]
399
+ spendable_amount = uint128(sum(r.coin.amount for r in unspent_records))
400
+ async with action_scope.use() as interface:
401
+ return list(
402
+ await select_coins(
403
+ spendable_amount,
404
+ action_scope.config.adjust_for_side_effects(interface.side_effects).tx_config.coin_selection_config,
405
+ unspent_records,
406
+ {},
407
+ self.log,
408
+ uint128(amount),
409
+ )
410
+ )
411
+
412
+ async def coin_added(self, coin: Coin, height: uint32, peer: WSChiaConnection, coin_data: Optional[Any]) -> None:
413
+ """
414
+ Notification from wallet state manager that a coin has been received.
415
+ This can be either a treasury coin update or funds added to the treasury
416
+ """
417
+ self.log.info(f"DAOWallet.coin_added() called with the coin: {coin.name().hex()}:{coin}.")
418
+ wallet_node: Any = self.wallet_state_manager.wallet_node
419
+ peer = wallet_node.get_full_node_peer()
420
+ if peer is None: # pragma: no cover
421
+ raise ValueError("Could not find any peers to request puzzle and solution from")
422
+ try:
423
+ # Get the parent coin spend
424
+ cs = (await wallet_node.get_coin_state([coin.parent_coin_info], peer, height))[0]
425
+ parent_spend = await fetch_coin_spend(cs.spent_height, cs.coin, peer)
426
+
427
+ uncurried = uncurry_puzzle(parent_spend.puzzle_reveal)
428
+ matched_funding_puz = match_funding_puzzle(
429
+ uncurried, parent_spend.solution.to_program(), coin, [self.dao_info.treasury_id]
430
+ )
431
+ if matched_funding_puz:
432
+ # funding coin
433
+ xch_funds_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
434
+ if coin.puzzle_hash == xch_funds_puzhash:
435
+ asset_id = None
436
+ else:
437
+ asset_id = get_asset_id_from_puzzle(parent_spend.puzzle_reveal.to_program())
438
+ # to prevent fake p2_singletons being added
439
+ assert coin.puzzle_hash == get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=asset_id)
440
+ if asset_id not in self.dao_info.assets:
441
+ new_asset_list = self.dao_info.assets.copy()
442
+ new_asset_list.append(asset_id)
443
+ dao_info = dataclasses.replace(self.dao_info, assets=new_asset_list)
444
+ await self.save_info(dao_info)
445
+ await self.wallet_state_manager.add_interested_puzzle_hashes([coin.puzzle_hash], [self.id()])
446
+ self.log.info(f"DAO funding coin added: {coin.name().hex()}:{coin}. Asset ID: {asset_id}")
447
+ except Exception as e: # pragma: no cover
448
+ self.log.exception(f"Error occurred during dao wallet coin addition: {e}")
449
+ return
450
+
451
+ def get_cat_tail_hash(self) -> bytes32:
452
+ cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id]
453
+ return cat_wallet.cat_info.limitations_program_hash
454
+
455
+ async def adjust_filter_level(self, new_filter_level: uint64) -> None:
456
+ dao_info = dataclasses.replace(self.dao_info, filter_below_vote_amount=new_filter_level)
457
+ await self.save_info(dao_info)
458
+
459
+ async def clear_finished_proposals_from_memory(self) -> None:
460
+ dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
461
+ new_list = [
462
+ prop_info
463
+ for prop_info in self.dao_info.proposals_list
464
+ if not prop_info.closed
465
+ or prop_info.closed is None
466
+ or any(prop_info.proposal_id in lci.active_votes for lci in dao_cat_wallet.dao_cat_info.locked_coins)
467
+ ]
468
+ dao_info = dataclasses.replace(self.dao_info, proposals_list=new_list)
469
+ await self.save_info(dao_info)
470
+ return
471
+
472
+ async def resync_treasury_state(self) -> None:
473
+ """
474
+ This is called during create_new_dao_wallet_for_existing_dao.
475
+ When we want to sync to an existing DAO, we provide the treasury coins singleton ID, and then trace all
476
+ the child coins until we reach the current DAO treasury coin. We use the puzzle reveal and solution to
477
+ get the current state of the DAO, and to work out what the tail of the DAO CAT token is.
478
+ This also captures all the proposals that have been created and their state.
479
+ """
480
+ parent_coin_id: bytes32 = self.dao_info.treasury_id
481
+ wallet_node: Any = self.wallet_state_manager.wallet_node
482
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
483
+ if peer is None: # pragma: no cover
484
+ raise ValueError("Could not find any peers to request puzzle and solution from")
485
+
486
+ parent_coin = None
487
+ parent_parent_coin = None
488
+ while True:
489
+ children = await wallet_node.fetch_children(parent_coin_id, peer)
490
+ if len(children) == 0:
491
+ break
492
+
493
+ children_state_list: List[CoinState] = [child for child in children if child.coin.amount % 2 == 1]
494
+ # ensure children_state_list has only one odd amount coin (the treasury)
495
+ if (len(children_state_list) == 0) or (len(children_state_list) > 1): # pragma: no cover
496
+ raise RuntimeError("Could not retrieve child_state")
497
+ children_state = children_state_list[0]
498
+ assert children_state is not None
499
+ child_coin = children_state.coin
500
+ if parent_coin is not None:
501
+ parent_parent_coin = parent_coin
502
+ parent_coin = child_coin
503
+ parent_coin_id = child_coin.name()
504
+
505
+ if parent_parent_coin is None: # pragma: no cover
506
+ raise RuntimeError("could not get parent_parent_coin of %s", children)
507
+
508
+ # ensure the child coin is unspent to prevent untrusted nodes sending false coin states
509
+ assert children_state.spent_height is None
510
+
511
+ # get lineage proof of parent spend, and also current innerpuz
512
+ assert children_state.created_height
513
+ parent_spend = await fetch_coin_spend(children_state.created_height, parent_parent_coin, peer)
514
+ assert parent_spend is not None
515
+ parent_inner_puz = get_inner_puzzle_from_singleton(parent_spend.puzzle_reveal)
516
+ if parent_inner_puz is None: # pragma: no cover
517
+ raise ValueError("get_innerpuzzle_from_puzzle failed")
518
+
519
+ if parent_spend.puzzle_reveal.get_tree_hash() == child_coin.puzzle_hash:
520
+ current_inner_puz = parent_inner_puz
521
+ else: # pragma: no cover
522
+ # extract the treasury solution from the full singleton solution
523
+ inner_solution = parent_spend.solution.to_program().rest().rest().first()
524
+ # reconstruct the treasury puzzle
525
+ current_inner_puz = get_new_puzzle_from_treasury_solution(parent_inner_puz, inner_solution)
526
+ # set the treasury rules
527
+ self.dao_rules = get_treasury_rules_from_puzzle(current_inner_puz)
528
+
529
+ current_lineage_proof = LineageProof(
530
+ parent_parent_coin.parent_coin_info, parent_inner_puz.get_tree_hash(), parent_parent_coin.amount
531
+ )
532
+ await self.add_parent(parent_parent_coin.name(), current_lineage_proof)
533
+
534
+ # Hack to find the cat tail hash from the memo of the genesis spend
535
+ launcher_state = await wallet_node.get_coin_state([self.dao_info.treasury_id], peer)
536
+ genesis_coin_id = launcher_state[0].coin.parent_coin_info
537
+ genesis_state = await wallet_node.get_coin_state([genesis_coin_id], peer)
538
+ genesis_spend = await fetch_coin_spend(genesis_state[0].spent_height, genesis_state[0].coin, peer)
539
+ cat_tail_hash = None
540
+ conds = genesis_spend.solution.to_program().at("rfr").as_iter()
541
+ for cond in conds:
542
+ if (cond.first().as_atom() == ConditionOpcode.CREATE_COIN) and (
543
+ int_from_bytes(cond.at("rrf").as_atom()) == 1
544
+ ):
545
+ cat_tail_hash = bytes32(cond.at("rrrff").as_atom())
546
+ cat_origin_id = bytes32(cond.at("rrrfrf").as_atom())
547
+ # Calculate the CAT tail from the memo data. If someone tries to use a fake tail hash in
548
+ # the memo field, it won't match with the DAO's actual treasury ID.
549
+ cat_tail = generate_cat_tail(cat_origin_id, self.dao_info.treasury_id)
550
+ break
551
+ assert cat_tail_hash
552
+ assert cat_tail.get_tree_hash() == cat_tail_hash
553
+
554
+ cat_wallet: Optional[CATWallet] = None
555
+
556
+ # Get or create a cat wallet
557
+ for wallet_id in self.wallet_state_manager.wallets:
558
+ wallet = self.wallet_state_manager.wallets[wallet_id]
559
+ if wallet.type() == WalletType.CAT: # pragma: no cover
560
+ assert isinstance(wallet, CATWallet)
561
+ if wallet.cat_info.limitations_program_hash == cat_tail_hash:
562
+ cat_wallet = wallet
563
+ break
564
+ else:
565
+ # Didn't find a cat wallet, so create one
566
+ cat_wallet = await CATWallet.get_or_create_wallet_for_cat(
567
+ self.wallet_state_manager, self.standard_wallet, cat_tail_hash.hex()
568
+ )
569
+
570
+ assert cat_wallet is not None
571
+ await cat_wallet.set_tail_program(bytes(cat_tail).hex())
572
+ cat_wallet_id = cat_wallet.wallet_info.id
573
+ dao_info = dataclasses.replace(
574
+ self.dao_info,
575
+ cat_wallet_id=uint32(cat_wallet_id),
576
+ dao_cat_wallet_id=uint32(0),
577
+ current_treasury_coin=child_coin,
578
+ current_treasury_innerpuz=current_inner_puz,
579
+ )
580
+ await self.save_info(dao_info)
581
+
582
+ future_parent = LineageProof(
583
+ child_coin.parent_coin_info,
584
+ dao_info.current_treasury_innerpuz.get_tree_hash(),
585
+ uint64(child_coin.amount),
586
+ )
587
+ await self.add_parent(child_coin.name(), future_parent)
588
+ assert self.dao_info.parent_info is not None
589
+
590
+ # get existing xch funds for treasury
591
+ xch_funds_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
592
+ await self.wallet_state_manager.add_interested_puzzle_hashes([xch_funds_puzhash], [self.id()])
593
+ await self.wallet_state_manager.add_interested_puzzle_hashes([self.dao_info.treasury_id], [self.id()])
594
+ await self.wallet_state_manager.add_interested_puzzle_hashes(
595
+ [self.dao_info.current_treasury_coin.puzzle_hash], [self.id()]
596
+ )
597
+
598
+ # Resync the wallet from when the treasury was created to get the existing funds
599
+ # TODO: Maybe split this out as an option for users since it may be slow?
600
+ if not wallet_node.is_trusted(peer):
601
+ # Untrusted nodes won't automatically send us the history of all the treasury and proposal coins,
602
+ # so we have to request them via sync_from_untrusted_close_to_peak
603
+ request = RequestBlockHeader(children_state.created_height)
604
+ response: Optional[RespondBlockHeader] = await peer.call_api(FullNodeAPI.request_block_header, request)
605
+ await wallet_node.sync_from_untrusted_close_to_peak(response.header_block, peer)
606
+
607
+ return
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
+ # pylint: disable-next=E0606
1477
+ spend_bundle = WalletSpendBundle([proposal_cs, timer_cs, treasury_cs], AugSchemeMPL.aggregate([]))
1478
+ if fee > 0:
1479
+ await self.standard_wallet.create_tandem_xch_tx(fee, action_scope)
1480
+ full_spend = spend_bundle
1481
+ if cat_spend_bundle is not None:
1482
+ full_spend = full_spend.aggregate([full_spend, cat_spend_bundle])
1483
+ if delegated_puzzle_sb is not None:
1484
+ full_spend = full_spend.aggregate([full_spend, delegated_puzzle_sb])
1485
+
1486
+ record = TransactionRecord(
1487
+ confirmed_at_height=uint32(0),
1488
+ created_at_time=uint64(int(time.time())),
1489
+ to_puzzle_hash=get_finished_state_puzzle(proposal_info.proposal_id).get_tree_hash(),
1490
+ amount=uint64(1),
1491
+ fee_amount=fee,
1492
+ confirmed=False,
1493
+ sent=uint32(10),
1494
+ spend_bundle=full_spend,
1495
+ additions=full_spend.additions(),
1496
+ removals=full_spend.removals(),
1497
+ wallet_id=self.id(),
1498
+ sent_to=[],
1499
+ trade_id=None,
1500
+ type=uint32(TransactionType.INCOMING_TX.value),
1501
+ name=full_spend.name(),
1502
+ memos=[],
1503
+ valid_times=parse_timelock_info(extra_conditions),
1504
+ )
1505
+ async with action_scope.use() as interface:
1506
+ interface.side_effects.transactions.append(record)
1507
+
1508
+ async def fetch_proposed_puzzle_reveal(self, proposal_id: bytes32) -> Program:
1509
+ wallet_node: Any = self.wallet_state_manager.wallet_node
1510
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
1511
+ if peer is None: # pragma: no cover
1512
+ raise ValueError("Could not find any peers to request puzzle and solution from")
1513
+ # The proposal_id is launcher coin, so proposal_id's child is eve and the eve spend contains the reveal
1514
+ children = await wallet_node.fetch_children(proposal_id, peer)
1515
+ eve_state = children[0]
1516
+
1517
+ eve_spend = await fetch_coin_spend(eve_state.created_height, eve_state.coin, peer)
1518
+ puzzle_reveal = get_proposed_puzzle_reveal_from_solution(eve_spend.solution.to_program())
1519
+ return puzzle_reveal
1520
+
1521
+ async def fetch_cat_lineage_proof(self, cat_coin: Coin) -> LineageProof:
1522
+ wallet_node: Any = self.wallet_state_manager.wallet_node
1523
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
1524
+ if peer is None: # pragma: no cover
1525
+ raise ValueError("Could not find any peers to request puzzle and solution from")
1526
+ state = await wallet_node.get_coin_state([cat_coin.parent_coin_info], peer)
1527
+ assert state is not None
1528
+ # CoinState contains Coin, spent_height, and created_height,
1529
+ parent_spend = await fetch_coin_spend(state[0].spent_height, state[0].coin, peer)
1530
+ parent_inner_puz = get_innerpuzzle_from_cat_puzzle(parent_spend.puzzle_reveal.to_program())
1531
+ return LineageProof(state[0].coin.parent_coin_info, parent_inner_puz.get_tree_hash(), state[0].coin.amount)
1532
+
1533
+ async def _create_treasury_fund_transaction(
1534
+ self,
1535
+ funding_wallet: WalletProtocol[Any],
1536
+ amount: uint64,
1537
+ action_scope: WalletActionScope,
1538
+ fee: uint64 = uint64(0),
1539
+ extra_conditions: Tuple[Condition, ...] = tuple(),
1540
+ ) -> None:
1541
+ if funding_wallet.type() == WalletType.STANDARD_WALLET.value:
1542
+ p2_singleton_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id, asset_id=None)
1543
+ wallet: Wallet = funding_wallet # type: ignore[assignment]
1544
+ await wallet.generate_signed_transaction(
1545
+ amount,
1546
+ p2_singleton_puzhash,
1547
+ action_scope,
1548
+ fee=fee,
1549
+ memos=[p2_singleton_puzhash],
1550
+ )
1551
+ elif funding_wallet.type() == WalletType.CAT.value:
1552
+ cat_wallet: CATWallet = funding_wallet # type: ignore[assignment]
1553
+ # generate_signed_transaction has a different type signature in Wallet and CATWallet
1554
+ # CATWallet uses a List of amounts and a List of puzhashes as the first two arguments
1555
+ p2_singleton_puzhash = get_p2_singleton_puzhash(self.dao_info.treasury_id)
1556
+ await cat_wallet.generate_signed_transaction(
1557
+ [amount],
1558
+ [p2_singleton_puzhash],
1559
+ action_scope,
1560
+ fee=fee,
1561
+ extra_conditions=extra_conditions,
1562
+ )
1563
+ else: # pragma: no cover
1564
+ raise ValueError(f"Assets of type {funding_wallet.type()} are not currently supported.")
1565
+
1566
+ async def create_add_funds_to_treasury_spend(
1567
+ self,
1568
+ amount: uint64,
1569
+ action_scope: WalletActionScope,
1570
+ fee: uint64 = uint64(0),
1571
+ funding_wallet_id: uint32 = uint32(1),
1572
+ extra_conditions: Tuple[Condition, ...] = tuple(),
1573
+ ) -> None:
1574
+ # set up the p2_singleton
1575
+ funding_wallet = self.wallet_state_manager.wallets[funding_wallet_id]
1576
+ await self._create_treasury_fund_transaction(
1577
+ funding_wallet, amount, action_scope, fee, extra_conditions=extra_conditions
1578
+ )
1579
+
1580
+ async def fetch_singleton_lineage_proof(self, coin: Coin) -> LineageProof:
1581
+ wallet_node: Any = self.wallet_state_manager.wallet_node
1582
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
1583
+ if peer is None: # pragma: no cover
1584
+ raise ValueError("Could not find any peers to request puzzle and solution from")
1585
+ state = await wallet_node.get_coin_state([coin.parent_coin_info], peer)
1586
+ assert state is not None
1587
+ # CoinState contains Coin, spent_height, and created_height,
1588
+ parent_spend = await fetch_coin_spend(state[0].spent_height, state[0].coin, peer)
1589
+ parent_inner_puz = get_inner_puzzle_from_singleton(parent_spend.puzzle_reveal)
1590
+ assert isinstance(parent_inner_puz, Program)
1591
+ return LineageProof(state[0].coin.parent_coin_info, parent_inner_puz.get_tree_hash(), state[0].coin.amount)
1592
+
1593
+ async def free_coins_from_finished_proposals(
1594
+ self,
1595
+ action_scope: WalletActionScope,
1596
+ fee: uint64 = uint64(0),
1597
+ extra_conditions: Tuple[Condition, ...] = tuple(),
1598
+ ) -> None:
1599
+ dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
1600
+ spends = []
1601
+ closed_list = []
1602
+ finished_puz = None
1603
+ for proposal_info in self.dao_info.proposals_list:
1604
+ if proposal_info.closed:
1605
+ closed_list.append(proposal_info.proposal_id)
1606
+ inner_solution = Program.to(
1607
+ [
1608
+ proposal_info.current_coin.amount,
1609
+ ]
1610
+ )
1611
+ lineage_proof: LineageProof = await self.fetch_singleton_lineage_proof(proposal_info.current_coin)
1612
+ solution = Program.to([lineage_proof.to_program(), proposal_info.current_coin.amount, inner_solution])
1613
+ finished_puz = get_finished_state_puzzle(proposal_info.proposal_id)
1614
+ cs = make_spend(proposal_info.current_coin, finished_puz, solution)
1615
+ prop_sb = WalletSpendBundle([cs], AugSchemeMPL.aggregate([]))
1616
+ spends.append(prop_sb)
1617
+
1618
+ sb = await dao_cat_wallet.remove_active_proposal(closed_list, action_scope=action_scope)
1619
+ spends.append(sb)
1620
+
1621
+ if not spends: # pragma: no cover
1622
+ raise ValueError("No proposals are available for release")
1623
+
1624
+ full_spend = WalletSpendBundle.aggregate(spends)
1625
+ if fee > 0:
1626
+ await self.standard_wallet.create_tandem_xch_tx(fee, action_scope)
1627
+
1628
+ assert isinstance(finished_puz, Program)
1629
+ record = TransactionRecord(
1630
+ confirmed_at_height=uint32(0),
1631
+ created_at_time=uint64(int(time.time())),
1632
+ to_puzzle_hash=finished_puz.get_tree_hash(),
1633
+ amount=uint64(1),
1634
+ fee_amount=fee,
1635
+ confirmed=False,
1636
+ sent=uint32(10),
1637
+ spend_bundle=full_spend,
1638
+ additions=full_spend.additions(),
1639
+ removals=full_spend.removals(),
1640
+ wallet_id=self.id(),
1641
+ sent_to=[],
1642
+ trade_id=None,
1643
+ type=uint32(TransactionType.INCOMING_TX.value),
1644
+ name=full_spend.name(),
1645
+ memos=[],
1646
+ valid_times=parse_timelock_info(extra_conditions),
1647
+ )
1648
+ async with action_scope.use() as interface:
1649
+ interface.side_effects.transactions.append(record)
1650
+
1651
+ async def parse_proposal(self, proposal_id: bytes32) -> Dict[str, Any]:
1652
+ for prop_info in self.dao_info.proposals_list:
1653
+ if prop_info.proposal_id == proposal_id:
1654
+ state = await self.get_proposal_state(proposal_id)
1655
+ proposed_puzzle_reveal = await self.fetch_proposed_puzzle_reveal(proposal_id)
1656
+ proposal_type, curried_args = get_proposal_args(proposed_puzzle_reveal)
1657
+ if proposal_type == ProposalType.SPEND:
1658
+ cat_launcher = create_cat_launcher_for_singleton_id(self.dao_info.treasury_id)
1659
+ (
1660
+ TREASURY_SINGLETON_STRUCT,
1661
+ CAT_MOD_HASH,
1662
+ CONDITIONS,
1663
+ LIST_OF_TAILHASH_CONDITIONS,
1664
+ P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH,
1665
+ ) = curried_args.as_iter()
1666
+ mint_amount = None
1667
+ new_cat_puzhash = None
1668
+ xch_created_coins = []
1669
+ for cond in CONDITIONS.as_iter():
1670
+ if cond.first().as_int() == 51:
1671
+ if cond.rest().first().as_atom() == cat_launcher.get_tree_hash():
1672
+ mint_amount = cond.rest().rest().first().as_int()
1673
+ new_cat_puzhash = cond.rest().rest().rest().first().first().as_atom()
1674
+ else:
1675
+ cc = {"puzzle_hash": cond.at("rf").as_atom(), "amount": cond.at("rrf").as_int()}
1676
+ xch_created_coins.append(cc)
1677
+
1678
+ asset_create_coins: List[Dict[Any, Any]] = []
1679
+ for asset in LIST_OF_TAILHASH_CONDITIONS.as_iter():
1680
+ if asset == Program.to(0): # pragma: no cover
1681
+ asset_dict: Optional[Dict[str, Any]] = None
1682
+ else:
1683
+ asset_id = asset.first().as_atom()
1684
+ cc_list = []
1685
+ for cond in asset.rest().first().as_iter():
1686
+ if cond.first().as_int() == 51:
1687
+ asset_dict = {
1688
+ "puzzle_hash": cond.at("rf").as_atom(),
1689
+ "amount": cond.at("rrf").as_int(),
1690
+ }
1691
+ # cc_list.append([asset_id, asset_dict])
1692
+ cc_list.append(asset_dict)
1693
+ asset_create_coins.append({"asset_id": asset_id, "conditions": cc_list})
1694
+ dictionary: Dict[str, Any] = {
1695
+ "state": state,
1696
+ "proposal_type": proposal_type.value,
1697
+ "proposed_puzzle_reveal": proposed_puzzle_reveal,
1698
+ "xch_conditions": xch_created_coins,
1699
+ "asset_conditions": asset_create_coins,
1700
+ }
1701
+ if mint_amount is not None and new_cat_puzhash is not None:
1702
+ dictionary["mint_amount"] = mint_amount
1703
+ dictionary["new_cat_puzhash"] = new_cat_puzhash
1704
+ elif proposal_type == ProposalType.UPDATE:
1705
+ dao_rules = get_dao_rules_from_update_proposal(proposed_puzzle_reveal)
1706
+ dictionary = {
1707
+ "state": state,
1708
+ "proposal_type": proposal_type.value,
1709
+ "dao_rules": dao_rules,
1710
+ }
1711
+ return dictionary
1712
+ raise ValueError(f"Unable to find proposal with id: {proposal_id.hex()}") # pragma: no cover
1713
+
1714
+ async def add_parent(self, name: bytes32, parent: Optional[LineageProof]) -> None:
1715
+ self.log.info(f"Adding parent {name}: {parent}")
1716
+ current_list = self.dao_info.parent_info.copy()
1717
+ current_list.append((name, parent))
1718
+ dao_info: DAOInfo = DAOInfo(
1719
+ self.dao_info.treasury_id,
1720
+ self.dao_info.cat_wallet_id,
1721
+ self.dao_info.dao_cat_wallet_id,
1722
+ self.dao_info.proposals_list,
1723
+ current_list,
1724
+ self.dao_info.current_treasury_coin,
1725
+ self.dao_info.current_treasury_innerpuz,
1726
+ self.dao_info.singleton_block_height,
1727
+ self.dao_info.filter_below_vote_amount,
1728
+ self.dao_info.assets,
1729
+ self.dao_info.current_height,
1730
+ )
1731
+ await self.save_info(dao_info)
1732
+
1733
+ async def save_info(self, dao_info: DAOInfo) -> None:
1734
+ self.dao_info = dao_info
1735
+ current_info = self.wallet_info
1736
+ data_str = json.dumps(dao_info.to_json_dict())
1737
+ wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
1738
+ self.wallet_info = wallet_info
1739
+ await self.wallet_state_manager.user_store.update_wallet(wallet_info)
1740
+
1741
+ def generate_wallet_name(self) -> str:
1742
+ """
1743
+ Generate a new DAO wallet name
1744
+ :return: wallet name
1745
+ """
1746
+ max_num = 0
1747
+ for wallet in self.wallet_state_manager.wallets.values():
1748
+ if wallet.type() == WalletType.DAO: # pragma: no cover
1749
+ matched = re.search(r"^Profile (\d+)$", wallet.wallet_info.name) # TODO: bug: wallet.wallet_info
1750
+ if matched and int(matched.group(1)) > max_num:
1751
+ max_num = int(matched.group(1))
1752
+ return f"Profile {max_num + 1}"
1753
+
1754
+ def require_derivation_paths(self) -> bool:
1755
+ return False
1756
+
1757
+ def get_cat_wallet_id(self) -> uint32:
1758
+ return self.dao_info.cat_wallet_id
1759
+
1760
+ async def enter_dao_cat_voting_mode(
1761
+ self,
1762
+ amount: uint64,
1763
+ action_scope: WalletActionScope,
1764
+ ) -> List[TransactionRecord]:
1765
+ dao_cat_wallet: DAOCATWallet = self.wallet_state_manager.wallets[self.dao_info.dao_cat_wallet_id]
1766
+ return await dao_cat_wallet.enter_dao_cat_voting_mode(amount, action_scope)
1767
+
1768
+ @staticmethod
1769
+ def get_next_interesting_coin(spend: CoinSpend) -> Optional[Coin]: # pragma: no cover
1770
+ # CoinSpend of one of the coins that we cared about. This coin was spent in a block, but might be in a reorg
1771
+ # If we return a value, it is a coin that we are also interested in (to support two transitions per block)
1772
+ return get_most_recent_singleton_coin_from_coin_spend(spend)
1773
+
1774
+ async def add_or_update_proposal_info(
1775
+ self,
1776
+ new_state: CoinSpend,
1777
+ block_height: uint32,
1778
+ ) -> None:
1779
+ new_dao_info = copy.copy(self.dao_info)
1780
+ puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
1781
+ if puzzle is None: # pragma: no cover
1782
+ raise ValueError("get_innerpuzzle_from_puzzle failed")
1783
+ solution = (
1784
+ Program.from_bytes(bytes(new_state.solution)).rest().rest().first()
1785
+ ) # get proposal solution from full singleton solution
1786
+ singleton_id = singleton.get_singleton_id_from_puzzle(new_state.puzzle_reveal)
1787
+ if singleton_id is None: # pragma: no cover
1788
+ raise ValueError("get_singleton_id_from_puzzle failed")
1789
+ ended = False
1790
+ dao_rules = get_treasury_rules_from_puzzle(self.dao_info.current_treasury_innerpuz)
1791
+ current_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
1792
+ if current_coin is None: # pragma: no cover
1793
+ raise ValueError("get_most_recent_singleton_coin_from_coin_spend failed")
1794
+
1795
+ current_innerpuz = get_new_puzzle_from_proposal_solution(puzzle, solution)
1796
+ assert isinstance(current_innerpuz, Program)
1797
+ assert current_coin.puzzle_hash == curry_singleton(singleton_id, current_innerpuz).get_tree_hash()
1798
+ # check if our parent puzzle was the finished state
1799
+ if puzzle.uncurry()[0] == DAO_FINISHED_STATE:
1800
+ ended = True
1801
+ index = 0
1802
+ for current_info in new_dao_info.proposals_list:
1803
+ # Search for current proposal_info
1804
+ if current_info.proposal_id == singleton_id:
1805
+ new_proposal_info = ProposalInfo(
1806
+ singleton_id,
1807
+ puzzle,
1808
+ current_info.amount_voted,
1809
+ current_info.yes_votes,
1810
+ current_coin,
1811
+ current_innerpuz,
1812
+ current_info.timer_coin,
1813
+ block_height,
1814
+ current_info.passed,
1815
+ ended,
1816
+ )
1817
+ new_dao_info.proposals_list[index] = new_proposal_info
1818
+ await self.save_info(new_dao_info)
1819
+ future_parent = LineageProof(
1820
+ new_state.coin.parent_coin_info,
1821
+ puzzle.get_tree_hash(),
1822
+ uint64(new_state.coin.amount),
1823
+ )
1824
+ await self.add_parent(new_state.coin.name(), future_parent)
1825
+ return
1826
+ index = index + 1
1827
+
1828
+ # check if we are the finished state
1829
+ if current_innerpuz == get_finished_state_inner_puzzle(singleton_id):
1830
+ ended = True
1831
+
1832
+ c_a, curried_args = uncurry_proposal(puzzle)
1833
+ (
1834
+ DAO_PROPOSAL_TIMER_MOD_HASH,
1835
+ SINGLETON_MOD_HASH,
1836
+ SINGLETON_LAUNCHER_PUZHASH,
1837
+ CAT_MOD_HASH,
1838
+ DAO_FINISHED_STATE_HASH,
1839
+ _DAO_TREASURY_MOD_HASH,
1840
+ lockup_self_hash,
1841
+ cat_tail_hash,
1842
+ treasury_id,
1843
+ ) = curried_args.as_iter()
1844
+ (
1845
+ curry_one,
1846
+ proposal_id,
1847
+ proposed_puzzle_hash,
1848
+ yes_votes,
1849
+ total_votes,
1850
+ ) = c_a.as_iter()
1851
+
1852
+ if current_coin is None: # pragma: no cover
1853
+ raise RuntimeError("get_most_recent_singleton_coin_from_coin_spend({new_state}) failed")
1854
+
1855
+ timer_coin = None
1856
+ if solution.at("rrrrrrf").as_int() == 0:
1857
+ # we need to add the vote amounts from the solution to get accurate totals
1858
+ is_yes_vote = solution.at("rf").as_int()
1859
+ votes_added = 0
1860
+ for vote_amount in solution.first().as_iter():
1861
+ votes_added += vote_amount.as_int()
1862
+ else:
1863
+ # If we have entered the finished state
1864
+ # TODO: we need to alert the user that they can free up their coins
1865
+ is_yes_vote = 0
1866
+ votes_added = 0
1867
+
1868
+ if current_coin.amount < dao_rules.proposal_minimum_amount and not ended: # pragma: no cover
1869
+ raise ValueError("this coin does not meet the minimum requirements and can be ignored")
1870
+ new_total_votes = total_votes.as_int() + votes_added
1871
+ if new_total_votes < self.dao_info.filter_below_vote_amount: # pragma: no cover
1872
+ return # ignore all proposals below the filter amount
1873
+
1874
+ if is_yes_vote == 1:
1875
+ new_yes_votes = yes_votes.as_int() + votes_added
1876
+ else:
1877
+ new_yes_votes = yes_votes.as_int()
1878
+
1879
+ required_yes_votes = (self.dao_rules.attendance_required * self.dao_rules.pass_percentage) // 10000
1880
+ yes_votes_needed = max(0, required_yes_votes - new_yes_votes)
1881
+
1882
+ passed = True if yes_votes_needed == 0 else False
1883
+
1884
+ index = 0
1885
+ for current_info in new_dao_info.proposals_list:
1886
+ # Search for current proposal_info
1887
+ if current_info.proposal_id == singleton_id:
1888
+ # If we are receiving a voting spend update
1889
+ new_proposal_info = ProposalInfo(
1890
+ singleton_id,
1891
+ puzzle,
1892
+ uint64(new_total_votes),
1893
+ uint64(new_yes_votes),
1894
+ current_coin,
1895
+ current_innerpuz,
1896
+ current_info.timer_coin,
1897
+ block_height,
1898
+ passed,
1899
+ ended,
1900
+ )
1901
+ new_dao_info.proposals_list[index] = new_proposal_info
1902
+ await self.save_info(new_dao_info)
1903
+ future_parent = LineageProof(
1904
+ new_state.coin.parent_coin_info,
1905
+ puzzle.get_tree_hash(),
1906
+ uint64(new_state.coin.amount),
1907
+ )
1908
+ await self.add_parent(new_state.coin.name(), future_parent)
1909
+ return
1910
+ index = index + 1
1911
+
1912
+ # Search for the timer coin
1913
+ if not ended:
1914
+ wallet_node: Any = self.wallet_state_manager.wallet_node
1915
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
1916
+ if peer is None: # pragma: no cover
1917
+ raise ValueError("Could not find any peers to request puzzle and solution from")
1918
+ children = await wallet_node.fetch_children(singleton_id, peer)
1919
+ assert len(children) > 0
1920
+ found = False
1921
+ parent_coin_id = singleton_id
1922
+
1923
+ if self.dao_info.current_treasury_innerpuz is None: # pragma: no cover
1924
+ raise ValueError("self.dao_info.current_treasury_innerpuz is None")
1925
+
1926
+ timer_coin_puzhash = get_proposal_timer_puzzle(
1927
+ bytes32(cat_tail_hash.as_atom()),
1928
+ singleton_id,
1929
+ self.dao_info.treasury_id,
1930
+ ).get_tree_hash()
1931
+
1932
+ while not found and len(children) > 0:
1933
+ children = await wallet_node.fetch_children(parent_coin_id, peer)
1934
+ if len(children) == 0: # pragma: no cover
1935
+ break
1936
+ children_state = [child for child in children if child.coin.amount % 2 == 1]
1937
+ assert children_state is not None
1938
+ assert len(children_state) > 0
1939
+ child_state = children_state[0]
1940
+ for child in children:
1941
+ if child.coin.puzzle_hash == timer_coin_puzhash:
1942
+ found = True
1943
+ timer_coin = child.coin
1944
+ break
1945
+ child_coin = child_state.coin
1946
+ parent_coin_id = child_coin.name()
1947
+
1948
+ # If we reach here then we don't currently know about this coin
1949
+ # We only want to add this coin if it has a timer coin since fake proposals without a timer can
1950
+ # be created.
1951
+ if found:
1952
+ new_proposal_info = ProposalInfo(
1953
+ singleton_id,
1954
+ puzzle,
1955
+ uint64(new_total_votes),
1956
+ uint64(new_yes_votes),
1957
+ current_coin,
1958
+ current_innerpuz,
1959
+ timer_coin, # if this is None then the proposal has finished
1960
+ block_height, # block height that current proposal singleton coin was created
1961
+ passed,
1962
+ ended,
1963
+ )
1964
+ new_dao_info.proposals_list.append(new_proposal_info)
1965
+ await self.save_info(new_dao_info)
1966
+ future_parent = LineageProof(
1967
+ new_state.coin.parent_coin_info,
1968
+ puzzle.get_tree_hash(),
1969
+ uint64(new_state.coin.amount),
1970
+ )
1971
+ await self.add_parent(new_state.coin.name(), future_parent)
1972
+ return
1973
+
1974
+ async def update_closed_proposal_coin(self, new_state: CoinSpend, block_height: uint32) -> None:
1975
+ new_dao_info = copy.copy(self.dao_info)
1976
+ puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
1977
+ proposal_id = singleton.get_singleton_id_from_puzzle(new_state.puzzle_reveal)
1978
+ current_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
1979
+ index = 0
1980
+ for pi in self.dao_info.proposals_list:
1981
+ if pi.proposal_id == proposal_id:
1982
+ assert isinstance(current_coin, Coin)
1983
+ new_info = ProposalInfo(
1984
+ proposal_id,
1985
+ pi.inner_puzzle,
1986
+ pi.amount_voted,
1987
+ pi.yes_votes,
1988
+ current_coin,
1989
+ pi.current_innerpuz,
1990
+ pi.timer_coin,
1991
+ pi.singleton_block_height,
1992
+ pi.passed,
1993
+ pi.closed,
1994
+ )
1995
+ new_dao_info.proposals_list[index] = new_info
1996
+ await self.save_info(new_dao_info)
1997
+ assert isinstance(puzzle, Program)
1998
+ future_parent = LineageProof(
1999
+ new_state.coin.parent_coin_info,
2000
+ puzzle.get_tree_hash(),
2001
+ uint64(new_state.coin.amount),
2002
+ )
2003
+ await self.add_parent(new_state.coin.name(), future_parent)
2004
+ return
2005
+ index = index + 1
2006
+
2007
+ async def get_proposal_state(self, proposal_id: bytes32) -> Dict[str, Union[int, bool]]:
2008
+ """
2009
+ Use this to figure out whether a proposal has passed or failed and whether it can be closed
2010
+ Given a proposal_id:
2011
+ - if required yes votes are recorded then proposal passed.
2012
+ - if timelock and attendance are met then proposal can close
2013
+ Returns a dict of passed and closable bools, and the remaining votes/blocks needed
2014
+
2015
+ Note that a proposal can be in a passed and closable state now, but become failed if a large number of
2016
+ 'no' votes are recieved before the soft close is reached.
2017
+ """
2018
+ for prop in self.dao_info.proposals_list:
2019
+ if prop.proposal_id == proposal_id:
2020
+ is_closed = prop.closed
2021
+ break
2022
+ else: # pragma: no cover
2023
+ raise ValueError(f"Proposal not found for id {proposal_id}")
2024
+
2025
+ wallet_node = self.wallet_state_manager.wallet_node
2026
+ peer: WSChiaConnection = wallet_node.get_full_node_peer()
2027
+ if peer is None: # pragma: no cover
2028
+ raise ValueError("Could not find any peers to request puzzle and solution from")
2029
+ assert isinstance(prop.timer_coin, Coin)
2030
+ timer_cs = (await wallet_node.get_coin_state([prop.timer_coin.name()], peer))[0]
2031
+ peak = await self.wallet_state_manager.blockchain.get_peak_block()
2032
+ blocks_elapsed = peak.height - timer_cs.created_height
2033
+
2034
+ required_yes_votes = (self.dao_rules.attendance_required * self.dao_rules.pass_percentage) // 10000
2035
+ total_votes_needed = max(0, self.dao_rules.attendance_required - prop.amount_voted)
2036
+ yes_votes_needed = max(0, required_yes_votes - prop.yes_votes)
2037
+ blocks_needed = max(0, self.dao_rules.proposal_timelock - blocks_elapsed)
2038
+
2039
+ passed = True if yes_votes_needed == 0 else False
2040
+ closable = True if total_votes_needed == blocks_needed == 0 else False
2041
+ proposal_state = {
2042
+ "total_votes_needed": total_votes_needed,
2043
+ "yes_votes_needed": yes_votes_needed,
2044
+ "blocks_needed": blocks_needed,
2045
+ "passed": passed,
2046
+ "closable": closable,
2047
+ "closed": is_closed,
2048
+ }
2049
+ return proposal_state
2050
+
2051
+ async def update_treasury_info(
2052
+ self,
2053
+ new_state: CoinSpend,
2054
+ block_height: uint32,
2055
+ ) -> None:
2056
+ if self.dao_info.singleton_block_height <= block_height:
2057
+ # TODO: what do we do here?
2058
+ # return
2059
+ pass
2060
+ puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
2061
+ if puzzle is None: # pragma: no cover
2062
+ raise ValueError("get_innerpuzzle_from_puzzle failed")
2063
+ solution = (
2064
+ Program.from_bytes(bytes(new_state.solution)).rest().rest().first()
2065
+ ) # get proposal solution from full singleton solution
2066
+ new_innerpuz = get_new_puzzle_from_treasury_solution(puzzle, solution)
2067
+ child_coin = get_most_recent_singleton_coin_from_coin_spend(new_state)
2068
+ assert isinstance(child_coin, Coin)
2069
+ assert isinstance(self.dao_info.current_treasury_coin, Coin)
2070
+ if child_coin.puzzle_hash != self.dao_info.current_treasury_coin.puzzle_hash:
2071
+ # update dao rules
2072
+ assert isinstance(new_innerpuz, Program)
2073
+ self.dao_rules = get_treasury_rules_from_puzzle(new_innerpuz)
2074
+ dao_info = dataclasses.replace(
2075
+ self.dao_info,
2076
+ current_treasury_coin=child_coin,
2077
+ current_treasury_innerpuz=new_innerpuz,
2078
+ singleton_block_height=block_height,
2079
+ )
2080
+ await self.save_info(dao_info)
2081
+ future_parent = LineageProof(
2082
+ new_state.coin.parent_coin_info,
2083
+ puzzle.get_tree_hash(),
2084
+ uint64(new_state.coin.amount),
2085
+ )
2086
+ await self.add_parent(new_state.coin.name(), future_parent)
2087
+ return
2088
+
2089
+ async def apply_state_transition(self, new_state: CoinSpend, block_height: uint32) -> bool:
2090
+ """
2091
+ We are being notified of a singleton state transition. A Singleton has been spent.
2092
+ Returns True iff the spend is a valid transition spend for the singleton, False otherwise.
2093
+ """
2094
+
2095
+ self.log.info(
2096
+ f"DAOWallet.apply_state_transition called with the height: {block_height} "
2097
+ f"and CoinSpend of {new_state.coin.name()}."
2098
+ )
2099
+ singleton_id = get_singleton_id_from_puzzle(new_state.puzzle_reveal)
2100
+ if not singleton_id: # pragma: no cover
2101
+ raise ValueError("Received a non singleton coin for dao wallet")
2102
+
2103
+ # Consume new DAOBlockchainInfo
2104
+ # Determine if this is a treasury spend or a proposal spend
2105
+ puzzle = get_inner_puzzle_from_singleton(new_state.puzzle_reveal)
2106
+ assert puzzle
2107
+ try:
2108
+ mod, curried_args = puzzle.uncurry()
2109
+ except ValueError as e: # pragma: no cover
2110
+ self.log.warning("Cannot uncurry puzzle in DAO Wallet: error: %s", e)
2111
+ raise e
2112
+ if mod == DAO_TREASURY_MOD:
2113
+ await self.update_treasury_info(new_state, block_height)
2114
+ elif (mod == DAO_PROPOSAL_MOD) or (mod.uncurry()[0] == DAO_PROPOSAL_MOD):
2115
+ await self.add_or_update_proposal_info(new_state, block_height)
2116
+ elif mod == DAO_FINISHED_STATE:
2117
+ await self.update_closed_proposal_coin(new_state, block_height)
2118
+ else: # pragma: no cover
2119
+ raise ValueError(f"Unsupported spend in DAO Wallet: {self.id()}")
2120
+
2121
+ return True