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,3106 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import dataclasses
5
+ import json
6
+ import logging
7
+ import random
8
+ from operator import attrgetter
9
+ from typing import Any, Dict, List, Optional, Tuple, cast
10
+
11
+ import aiosqlite
12
+ import pytest
13
+ from chia_rs import G1Element, G2Element
14
+
15
+ from chia._tests.conftest import ConsensusMode
16
+ from chia._tests.environments.wallet import WalletStateTransition, WalletTestFramework
17
+ from chia._tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
18
+ from chia._tests.wallet.test_wallet_coin_store import (
19
+ get_coin_records_amount_filter_tests,
20
+ get_coin_records_amount_range_tests,
21
+ get_coin_records_coin_id_filter_tests,
22
+ get_coin_records_coin_type_tests,
23
+ get_coin_records_confirmed_range_tests,
24
+ get_coin_records_include_total_count_tests,
25
+ get_coin_records_mixed_tests,
26
+ get_coin_records_offset_limit_tests,
27
+ get_coin_records_order_tests,
28
+ get_coin_records_parent_coin_id_filter_tests,
29
+ get_coin_records_puzzle_hash_filter_tests,
30
+ get_coin_records_reverse_tests,
31
+ get_coin_records_spent_range_tests,
32
+ get_coin_records_wallet_id_tests,
33
+ get_coin_records_wallet_type_tests,
34
+ record_1,
35
+ record_2,
36
+ record_3,
37
+ record_4,
38
+ record_5,
39
+ record_6,
40
+ record_7,
41
+ record_8,
42
+ record_9,
43
+ )
44
+ from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
45
+ from chia.consensus.coinbase import create_puzzlehash_for_pk
46
+ from chia.rpc.full_node_rpc_client import FullNodeRpcClient
47
+ from chia.rpc.rpc_client import ResponseFailureError
48
+ from chia.rpc.rpc_server import RpcServer
49
+ from chia.rpc.wallet_request_types import (
50
+ AddKey,
51
+ CheckDeleteKey,
52
+ CombineCoins,
53
+ DefaultCAT,
54
+ DeleteKey,
55
+ DIDGetPubkey,
56
+ GetNotifications,
57
+ GetPrivateKey,
58
+ LogIn,
59
+ SplitCoins,
60
+ SplitCoinsResponse,
61
+ VerifySignature,
62
+ VerifySignatureResponse,
63
+ )
64
+ from chia.rpc.wallet_rpc_api import WalletRpcApi
65
+ from chia.rpc.wallet_rpc_client import WalletRpcClient
66
+ from chia.server.server import ChiaServer
67
+ from chia.server.start_service import Service
68
+ from chia.simulator.full_node_simulator import FullNodeSimulator
69
+ from chia.simulator.simulator_protocol import FarmNewBlockProtocol
70
+ from chia.types.blockchain_format.coin import Coin, coin_as_list
71
+ from chia.types.blockchain_format.program import Program
72
+ from chia.types.blockchain_format.sized_bytes import bytes32
73
+ from chia.types.coin_record import CoinRecord
74
+ from chia.types.coin_spend import CoinSpend, make_spend
75
+ from chia.types.peer_info import PeerInfo
76
+ from chia.types.signing_mode import SigningMode
77
+ from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
78
+ from chia.util.config import load_config, lock_and_load_config, save_config
79
+ from chia.util.db_wrapper import DBWrapper2
80
+ from chia.util.hash import std_hash
81
+ from chia.util.ints import uint16, uint32, uint64
82
+ from chia.util.streamable import ConversionError, InvalidTypeError
83
+ from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
84
+ from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
85
+ from chia.wallet.cat_wallet.cat_wallet import CATWallet
86
+ from chia.wallet.conditions import (
87
+ ConditionValidTimes,
88
+ CreateCoinAnnouncement,
89
+ CreatePuzzleAnnouncement,
90
+ Remark,
91
+ conditions_to_json_dicts,
92
+ )
93
+ from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
94
+ from chia.wallet.did_wallet.did_wallet import DIDWallet
95
+ from chia.wallet.nft_wallet.nft_wallet import NFTWallet
96
+ from chia.wallet.puzzles.clawback.metadata import AutoClaimSettings
97
+ from chia.wallet.signer_protocol import UnsignedTransaction
98
+ from chia.wallet.trading.trade_status import TradeStatus
99
+ from chia.wallet.transaction_record import TransactionRecord
100
+ from chia.wallet.transaction_sorting import SortKey
101
+ from chia.wallet.uncurried_puzzle import uncurry_puzzle
102
+ from chia.wallet.util.address_type import AddressType
103
+ from chia.wallet.util.blind_signer_tl import BLIND_SIGNER_TRANSLATION
104
+ from chia.wallet.util.clvm_streamable import byte_deserialize_clvm_streamable
105
+ from chia.wallet.util.compute_memos import compute_memos
106
+ from chia.wallet.util.query_filter import AmountFilter, HashFilter, TransactionTypeFilter
107
+ from chia.wallet.util.transaction_type import TransactionType
108
+ from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
109
+ from chia.wallet.util.wallet_types import CoinType, WalletType
110
+ from chia.wallet.wallet import Wallet
111
+ from chia.wallet.wallet_coin_record import WalletCoinRecord
112
+ from chia.wallet.wallet_coin_store import GetCoinRecords
113
+ from chia.wallet.wallet_node import WalletNode
114
+ from chia.wallet.wallet_protocol import WalletProtocol
115
+ from chia.wallet.wallet_spend_bundle import WalletSpendBundle
116
+
117
+ log = logging.getLogger(__name__)
118
+
119
+
120
+ @dataclasses.dataclass
121
+ class WalletBundle:
122
+ service: Service
123
+ node: WalletNode
124
+ rpc_client: WalletRpcClient
125
+ wallet: Wallet
126
+
127
+
128
+ @dataclasses.dataclass
129
+ class FullNodeBundle:
130
+ server: ChiaServer
131
+ api: FullNodeSimulator
132
+ rpc_client: FullNodeRpcClient
133
+
134
+
135
+ @dataclasses.dataclass
136
+ class WalletRpcTestEnvironment:
137
+ wallet_1: WalletBundle
138
+ wallet_2: WalletBundle
139
+ full_node: FullNodeBundle
140
+
141
+
142
+ async def farm_transaction_block(full_node_api: FullNodeSimulator, wallet_node: WalletNode):
143
+ await full_node_api.farm_blocks_to_puzzlehash(count=1)
144
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
145
+
146
+
147
+ def check_mempool_spend_count(full_node_api: FullNodeSimulator, num_of_spends):
148
+ return full_node_api.full_node.mempool_manager.mempool.size() == num_of_spends
149
+
150
+
151
+ async def farm_transaction(full_node_api: FullNodeSimulator, wallet_node: WalletNode, spend_bundle: WalletSpendBundle):
152
+ await time_out_assert(
153
+ 20, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle, spend_bundle.name()
154
+ )
155
+ await farm_transaction_block(full_node_api, wallet_node)
156
+ assert full_node_api.full_node.mempool_manager.get_spendbundle(spend_bundle.name()) is None
157
+
158
+
159
+ async def generate_funds(full_node_api: FullNodeSimulator, wallet_bundle: WalletBundle, num_blocks: int = 1):
160
+ wallet_id = 1
161
+ initial_balances = await wallet_bundle.rpc_client.get_wallet_balance(wallet_id)
162
+ ph: bytes32 = decode_puzzle_hash(await wallet_bundle.rpc_client.get_next_address(wallet_id, True))
163
+ generated_funds = 0
164
+ for i in range(0, num_blocks):
165
+ await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
166
+ peak_height = full_node_api.full_node.blockchain.get_peak_height()
167
+ assert peak_height is not None
168
+ generated_funds += calculate_pool_reward(peak_height) + calculate_base_farmer_reward(peak_height)
169
+
170
+ # Farm a dummy block to confirm the created funds
171
+ await farm_transaction_block(full_node_api, wallet_bundle.node)
172
+
173
+ expected_confirmed = initial_balances["confirmed_wallet_balance"] + generated_funds
174
+ expected_unconfirmed = initial_balances["unconfirmed_wallet_balance"] + generated_funds
175
+ await time_out_assert(20, get_confirmed_balance, expected_confirmed, wallet_bundle.rpc_client, wallet_id)
176
+ await time_out_assert(20, get_unconfirmed_balance, expected_unconfirmed, wallet_bundle.rpc_client, wallet_id)
177
+ await time_out_assert(20, wallet_bundle.rpc_client.get_synced)
178
+
179
+ return generated_funds
180
+
181
+
182
+ @pytest.fixture(scope="function", params=[True, False])
183
+ async def wallet_rpc_environment(two_wallet_nodes_services, request, self_hostname):
184
+ full_node, wallets, bt = two_wallet_nodes_services
185
+ full_node_service = full_node[0]
186
+ full_node_api = full_node_service._api
187
+ full_node_server = full_node_api.full_node.server
188
+ wallet_service = wallets[0]
189
+ wallet_service_2 = wallets[1]
190
+ wallet_node = wallet_service._node
191
+ wallet_node_2 = wallet_service_2._node
192
+ wallet = wallet_node.wallet_state_manager.main_wallet
193
+ wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
194
+
195
+ config = bt.config
196
+ hostname = config["self_hostname"]
197
+
198
+ if request.param:
199
+ wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
200
+ wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
201
+ else:
202
+ wallet_node.config["trusted_peers"] = {}
203
+ wallet_node_2.config["trusted_peers"] = {}
204
+
205
+ await wallet_node.server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
206
+ await wallet_node_2.server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
207
+
208
+ async with WalletRpcClient.create_as_context(
209
+ hostname, wallet_service.rpc_server.listen_port, wallet_service.root_path, wallet_service.config
210
+ ) as client:
211
+ async with WalletRpcClient.create_as_context(
212
+ hostname, wallet_service_2.rpc_server.listen_port, wallet_service_2.root_path, wallet_service_2.config
213
+ ) as client_2:
214
+ async with FullNodeRpcClient.create_as_context(
215
+ hostname,
216
+ full_node_service.rpc_server.listen_port,
217
+ full_node_service.root_path,
218
+ full_node_service.config,
219
+ ) as client_node:
220
+ wallet_bundle_1: WalletBundle = WalletBundle(wallet_service, wallet_node, client, wallet)
221
+ wallet_bundle_2: WalletBundle = WalletBundle(wallet_service_2, wallet_node_2, client_2, wallet_2)
222
+ node_bundle: FullNodeBundle = FullNodeBundle(full_node_server, full_node_api, client_node)
223
+
224
+ yield WalletRpcTestEnvironment(wallet_bundle_1, wallet_bundle_2, node_bundle)
225
+
226
+
227
+ async def create_tx_outputs(wallet: Wallet, output_args: List[Tuple[int, Optional[List[str]]]]) -> List[Dict[str, Any]]:
228
+ outputs = []
229
+ for args in output_args:
230
+ output = {"amount": uint64(args[0]), "puzzle_hash": await wallet.get_new_puzzlehash()}
231
+ if args[1] is not None:
232
+ assert len(args[1]) > 0
233
+ output["memos"] = args[1]
234
+ outputs.append(output)
235
+ return outputs
236
+
237
+
238
+ async def assert_wallet_types(client: WalletRpcClient, expected: Dict[WalletType, int]) -> None:
239
+ for wallet_type in WalletType:
240
+ wallets = await client.get_wallets(wallet_type)
241
+ wallet_count = len(wallets)
242
+ if wallet_type in expected:
243
+ assert wallet_count == expected.get(wallet_type, 0)
244
+ for wallet in wallets:
245
+ assert wallet["type"] == wallet_type.value
246
+
247
+
248
+ def assert_tx_amounts(
249
+ tx: TransactionRecord,
250
+ outputs: List[Dict[str, Any]],
251
+ *,
252
+ amount_fee: uint64,
253
+ change_expected: bool,
254
+ is_cat: bool = False,
255
+ ) -> None:
256
+ assert tx.fee_amount == amount_fee
257
+ assert tx.amount == sum(output["amount"] for output in outputs)
258
+ expected_additions = len(outputs) + 1 if change_expected else len(outputs)
259
+ assert len(tx.additions) == expected_additions
260
+ addition_amounts = [addition.amount for addition in tx.additions]
261
+ removal_amounts = [removal.amount for removal in tx.removals]
262
+ for output in outputs:
263
+ assert output["amount"] in addition_amounts
264
+ if is_cat:
265
+ assert (sum(removal_amounts) - sum(addition_amounts)) == 0
266
+ else:
267
+ assert (sum(removal_amounts) - sum(addition_amounts)) == amount_fee
268
+
269
+
270
+ async def assert_push_tx_error(node_rpc: FullNodeRpcClient, tx: TransactionRecord):
271
+ spend_bundle = tx.spend_bundle
272
+ assert spend_bundle is not None
273
+ # check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error
274
+ try:
275
+ await node_rpc.push_tx(spend_bundle)
276
+ except ValueError as error:
277
+ error_string = error.args[0]["error"] # noqa: # pylint: disable=E1126
278
+ if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1:
279
+ raise ValueError from error
280
+
281
+
282
+ async def assert_get_balance(rpc_client: WalletRpcClient, wallet_node: WalletNode, wallet: WalletProtocol) -> None:
283
+ expected_balance = await wallet_node.get_balance(wallet.id())
284
+ expected_balance_dict = expected_balance.to_json_dict()
285
+ expected_balance_dict["wallet_id"] = wallet.id()
286
+ expected_balance_dict["wallet_type"] = wallet.type()
287
+ expected_balance_dict["fingerprint"] = wallet_node.logged_in_fingerprint
288
+ if wallet.type() in {WalletType.CAT, WalletType.CRCAT}:
289
+ assert isinstance(wallet, CATWallet)
290
+ expected_balance_dict["asset_id"] = wallet.get_asset_id()
291
+ assert await rpc_client.get_wallet_balance(wallet.id()) == expected_balance_dict
292
+
293
+
294
+ async def tx_in_mempool(client: WalletRpcClient, transaction_id: bytes32):
295
+ tx = await client.get_transaction(transaction_id)
296
+ return tx.is_in_mempool()
297
+
298
+
299
+ async def get_confirmed_balance(client: WalletRpcClient, wallet_id: int):
300
+ return (await client.get_wallet_balance(wallet_id))["confirmed_wallet_balance"]
301
+
302
+
303
+ async def get_unconfirmed_balance(client: WalletRpcClient, wallet_id: int):
304
+ return (await client.get_wallet_balance(wallet_id))["unconfirmed_wallet_balance"]
305
+
306
+
307
+ @pytest.mark.anyio
308
+ async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment):
309
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
310
+
311
+ wallet_2: Wallet = env.wallet_2.wallet
312
+ wallet_node: WalletNode = env.wallet_1.node
313
+ full_node_api: FullNodeSimulator = env.full_node.api
314
+ client: WalletRpcClient = env.wallet_1.rpc_client
315
+
316
+ generated_funds = await generate_funds(full_node_api, env.wallet_1)
317
+
318
+ addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
319
+ tx_amount = uint64(15600000)
320
+ with pytest.raises(ValueError):
321
+ await client.send_transaction(1, uint64(100000000000000001), addr, DEFAULT_TX_CONFIG)
322
+
323
+ # Tests sending a basic transaction
324
+ extra_conditions = (Remark(Program.to(("test", None))),)
325
+ non_existent_coin = Coin(bytes32([0] * 32), bytes32([0] * 32), uint64(0))
326
+ tx_no_push = (
327
+ await client.send_transaction(
328
+ 1,
329
+ tx_amount,
330
+ addr,
331
+ memos=["this is a basic tx"],
332
+ tx_config=DEFAULT_TX_CONFIG.override(
333
+ excluded_coin_amounts=[uint64(250000000000)],
334
+ excluded_coin_ids=[non_existent_coin.name()],
335
+ reuse_puzhash=True,
336
+ ),
337
+ extra_conditions=extra_conditions,
338
+ push=False,
339
+ )
340
+ ).transaction
341
+ response = await client.fetch(
342
+ "send_transaction",
343
+ {
344
+ "wallet_id": 1,
345
+ "amount": tx_amount,
346
+ "address": addr,
347
+ "fee": 0,
348
+ "memos": ["this is a basic tx"],
349
+ "puzzle_decorator": None,
350
+ "extra_conditions": conditions_to_json_dicts(extra_conditions),
351
+ "exclude_coin_amounts": [250000000000],
352
+ "exclude_coins": [non_existent_coin.to_json_dict()],
353
+ "reuse_puzhash": True,
354
+ "CHIP-0029": True,
355
+ "translation": "CHIP-0028",
356
+ "push": True,
357
+ },
358
+ )
359
+ assert response["success"]
360
+ tx = TransactionRecord.from_json_dict_convenience(response["transactions"][0])
361
+ [
362
+ byte_deserialize_clvm_streamable(
363
+ bytes.fromhex(utx), UnsignedTransaction, translation_layer=BLIND_SIGNER_TRANSLATION
364
+ )
365
+ for utx in response["unsigned_transactions"]
366
+ ]
367
+ assert tx == dataclasses.replace(tx_no_push, created_at_time=tx.created_at_time)
368
+ transaction_id = tx.name
369
+ spend_bundle = tx.spend_bundle
370
+ assert spend_bundle is not None
371
+
372
+ await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
373
+ await time_out_assert(20, get_unconfirmed_balance, generated_funds - tx_amount, client, 1)
374
+
375
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
376
+
377
+ # Checks that the memo can be retrieved
378
+ tx_confirmed = await client.get_transaction(transaction_id)
379
+ assert tx_confirmed.confirmed
380
+ assert len(tx_confirmed.get_memos()) == 1
381
+ assert [b"this is a basic tx"] in tx_confirmed.get_memos().values()
382
+ assert list(tx_confirmed.get_memos().keys())[0] in [a.name() for a in spend_bundle.additions()]
383
+
384
+ await time_out_assert(20, get_confirmed_balance, generated_funds - tx_amount, client, 1)
385
+
386
+
387
+ @pytest.mark.anyio
388
+ async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironment):
389
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
390
+
391
+ wallet: Wallet = env.wallet_1.wallet
392
+ wallet_node: WalletNode = env.wallet_1.node
393
+ full_node_api: FullNodeSimulator = env.full_node.api
394
+ client: WalletRpcClient = env.wallet_1.rpc_client
395
+
396
+ await generate_funds(full_node_api, env.wallet_1, num_blocks=2)
397
+
398
+ outputs = await create_tx_outputs(wallet, [(1234321, None)])
399
+
400
+ tx = (
401
+ await client.create_signed_transactions(
402
+ outputs,
403
+ tx_config=DEFAULT_TX_CONFIG,
404
+ fee=uint64(100),
405
+ )
406
+ ).signed_tx
407
+
408
+ resp_client = await client.push_transactions([tx], fee=uint64(10))
409
+ resp = await client.fetch(
410
+ "push_transactions", {"transactions": [tx.to_json_dict_convenience(wallet_node.config)], "fee": 10}
411
+ )
412
+ assert resp["success"]
413
+ resp = await client.fetch("push_transactions", {"transactions": [tx.to_json_dict()], "fee": 10})
414
+ assert resp["success"]
415
+
416
+ spend_bundle = WalletSpendBundle.aggregate(
417
+ [
418
+ # We ARE checking that the spend bundle is not None but mypy can't recognize this
419
+ TransactionRecord.from_json_dict_convenience(tx).spend_bundle # type: ignore[type-var]
420
+ for tx in resp_client["transactions"]
421
+ if tx["spend_bundle"] is not None
422
+ ]
423
+ )
424
+ assert spend_bundle is not None
425
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
426
+
427
+ for tx_json in resp_client["transactions"]:
428
+ tx = TransactionRecord.from_json_dict_convenience(tx_json)
429
+ tx = await client.get_transaction(transaction_id=tx.name)
430
+ assert tx.confirmed
431
+
432
+
433
+ @pytest.mark.anyio
434
+ async def test_get_balance(wallet_rpc_environment: WalletRpcTestEnvironment):
435
+ env = wallet_rpc_environment
436
+ wallet: Wallet = env.wallet_1.wallet
437
+ wallet_node: WalletNode = env.wallet_1.node
438
+ full_node_api: FullNodeSimulator = env.full_node.api
439
+ wallet_rpc_client = env.wallet_1.rpc_client
440
+ await full_node_api.farm_blocks_to_wallet(2, wallet)
441
+ async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
442
+ cat_wallet = await CATWallet.create_new_cat_wallet(
443
+ wallet_node.wallet_state_manager,
444
+ wallet,
445
+ {"identifier": "genesis_by_id"},
446
+ uint64(100),
447
+ action_scope,
448
+ )
449
+ await full_node_api.wait_transaction_records_entered_mempool(action_scope.side_effects.transactions)
450
+ await full_node_api.wait_for_wallet_synced(wallet_node)
451
+ await assert_get_balance(wallet_rpc_client, wallet_node, wallet)
452
+ await assert_get_balance(wallet_rpc_client, wallet_node, cat_wallet)
453
+
454
+
455
+ @pytest.mark.anyio
456
+ async def test_get_farmed_amount(wallet_rpc_environment: WalletRpcTestEnvironment):
457
+ env = wallet_rpc_environment
458
+ wallet: Wallet = env.wallet_1.wallet
459
+ full_node_api: FullNodeSimulator = env.full_node.api
460
+ wallet_rpc_client = env.wallet_1.rpc_client
461
+ await full_node_api.farm_blocks_to_wallet(2, wallet)
462
+
463
+ get_farmed_amount_result = await wallet_rpc_client.get_farmed_amount()
464
+ get_timestamp_for_height_result = await wallet_rpc_client.get_timestamp_for_height(uint32(3)) # genesis + 2
465
+
466
+ expected_result = {
467
+ "blocks_won": 2,
468
+ "farmed_amount": 4_000_000_000_000,
469
+ "farmer_reward_amount": 500_000_000_000,
470
+ "fee_amount": 0,
471
+ "last_height_farmed": 3,
472
+ "last_time_farmed": get_timestamp_for_height_result,
473
+ "pool_reward_amount": 3_500_000_000_000,
474
+ "success": True,
475
+ }
476
+ assert get_farmed_amount_result == expected_result
477
+
478
+
479
+ @pytest.mark.anyio
480
+ async def test_get_farmed_amount_with_fee(wallet_rpc_environment: WalletRpcTestEnvironment):
481
+ env = wallet_rpc_environment
482
+ wallet: Wallet = env.wallet_1.wallet
483
+ full_node_api: FullNodeSimulator = env.full_node.api
484
+ wallet_rpc_client = env.wallet_1.rpc_client
485
+ wallet_node: WalletNode = env.wallet_1.node
486
+
487
+ await generate_funds(full_node_api, env.wallet_1)
488
+
489
+ fee_amount = 100
490
+ async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
491
+ await wallet.generate_signed_transaction(
492
+ amount=uint64(5),
493
+ puzzle_hash=bytes32([0] * 32),
494
+ action_scope=action_scope,
495
+ fee=uint64(fee_amount),
496
+ )
497
+
498
+ our_ph = await wallet.get_new_puzzlehash()
499
+ await full_node_api.wait_transaction_records_entered_mempool(records=action_scope.side_effects.transactions)
500
+ await full_node_api.farm_blocks_to_puzzlehash(count=2, farm_to=our_ph, guarantee_transaction_blocks=True)
501
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
502
+
503
+ result = await wallet_rpc_client.get_farmed_amount()
504
+ assert result["fee_amount"] == fee_amount
505
+
506
+
507
+ @pytest.mark.anyio
508
+ async def test_get_timestamp_for_height(wallet_rpc_environment: WalletRpcTestEnvironment):
509
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
510
+
511
+ full_node_api: FullNodeSimulator = env.full_node.api
512
+ client: WalletRpcClient = env.wallet_1.rpc_client
513
+
514
+ await generate_funds(full_node_api, env.wallet_1)
515
+
516
+ # This tests that the client returns a uint64, rather than raising or returning something unexpected
517
+ uint64(await client.get_timestamp_for_height(uint32(1)))
518
+
519
+
520
+ @pytest.mark.parametrize(
521
+ "output_args, fee, select_coin, is_cat",
522
+ [
523
+ ([(348026, None)], 0, False, False),
524
+ ([(1270495230, ["memo_1"]), (902347, ["memo_2"])], 1, True, False),
525
+ ([(84920, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"])], 0, False, False),
526
+ (
527
+ [(32058710, ["memo_1_0", "memo_1_1"]), (1, ["memo_2_0"]), (923, ["memo_3_0", "memo_3_1"])],
528
+ 32804,
529
+ True,
530
+ False,
531
+ ),
532
+ ([(1337, ["LEET"]), (81000, ["pingwei"])], 817, False, True),
533
+ ([(120000000000, None), (120000000000, None)], 10000000000, True, False),
534
+ ],
535
+ )
536
+ @pytest.mark.anyio
537
+ async def test_create_signed_transaction(
538
+ wallet_rpc_environment: WalletRpcTestEnvironment,
539
+ output_args: List[Tuple[int, Optional[List[str]]]],
540
+ fee: int,
541
+ select_coin: bool,
542
+ is_cat: bool,
543
+ ):
544
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
545
+
546
+ wallet_2: Wallet = env.wallet_2.wallet
547
+ wallet_1_node: WalletNode = env.wallet_1.node
548
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
549
+ full_node_api: FullNodeSimulator = env.full_node.api
550
+ full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
551
+
552
+ generated_funds = await generate_funds(full_node_api, env.wallet_1)
553
+
554
+ wallet_id = 1
555
+ if is_cat:
556
+ generated_funds = 10**9
557
+
558
+ res = await wallet_1_rpc.create_new_cat_and_wallet(uint64(generated_funds), test=True)
559
+ assert res["success"]
560
+ wallet_id = res["wallet_id"]
561
+
562
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
563
+ for i in range(5):
564
+ if check_mempool_spend_count(full_node_api, 0):
565
+ break
566
+ await farm_transaction_block(full_node_api, wallet_1_node)
567
+
568
+ outputs = await create_tx_outputs(wallet_2, output_args)
569
+ amount_outputs = sum(output["amount"] for output in outputs)
570
+ amount_fee = uint64(fee)
571
+
572
+ if is_cat:
573
+ amount_total = amount_outputs
574
+ else:
575
+ amount_total = amount_outputs + amount_fee
576
+
577
+ selected_coin = None
578
+ if select_coin:
579
+ selected_coin = await wallet_1_rpc.select_coins(
580
+ amount=amount_total, wallet_id=wallet_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
581
+ )
582
+ assert len(selected_coin) == 1
583
+
584
+ txs = (
585
+ await wallet_1_rpc.create_signed_transactions(
586
+ outputs,
587
+ coins=selected_coin,
588
+ fee=amount_fee,
589
+ wallet_id=wallet_id,
590
+ # shouldn't actually block it
591
+ tx_config=DEFAULT_TX_CONFIG.override(
592
+ excluded_coin_amounts=[uint64(selected_coin[0].amount)] if selected_coin is not None else [],
593
+ ),
594
+ push=True,
595
+ )
596
+ ).transactions
597
+ change_expected = not selected_coin or selected_coin[0].amount - amount_total > 0
598
+ assert_tx_amounts(txs[-1], outputs, amount_fee=amount_fee, change_expected=change_expected, is_cat=is_cat)
599
+
600
+ # Farm the transaction and make sure the wallet balance reflects it correct
601
+ spend_bundle = txs[0].spend_bundle
602
+ assert spend_bundle is not None
603
+ await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
604
+ await time_out_assert(20, get_confirmed_balance, generated_funds - amount_total, wallet_1_rpc, wallet_id)
605
+
606
+ # Assert every coin comes from the same parent
607
+ additions: List[Coin] = spend_bundle.additions()
608
+ assert len({c.parent_coin_info for c in additions}) == 2 if is_cat else 1
609
+
610
+ # Assert you can get the spend for each addition
611
+ for addition in additions:
612
+ cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
613
+ assert cr is not None
614
+ spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
615
+ addition.parent_coin_info, cr.confirmed_block_index
616
+ )
617
+ assert spend is not None
618
+
619
+ # Assert the memos are all correct
620
+ addition_dict: Dict[bytes32, Coin] = {addition.name(): addition for addition in additions}
621
+ memo_dictionary: Dict[bytes32, List[bytes]] = compute_memos(spend_bundle)
622
+ for output in outputs:
623
+ if "memos" in output:
624
+ found: bool = False
625
+ for addition_id, addition in addition_dict.items():
626
+ if (
627
+ is_cat
628
+ and addition.amount == output["amount"]
629
+ and memo_dictionary[addition_id][0] == output["puzzle_hash"]
630
+ and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output["memos"]]
631
+ ) or (
632
+ addition.amount == output["amount"]
633
+ and addition.puzzle_hash == output["puzzle_hash"]
634
+ and memo_dictionary[addition_id] == [memo.encode() for memo in output["memos"]]
635
+ ):
636
+ found = True
637
+ assert found
638
+
639
+
640
+ @pytest.mark.anyio
641
+ async def test_create_signed_transaction_with_coin_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
642
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
643
+
644
+ wallet_2: Wallet = env.wallet_2.wallet
645
+ full_node_api: FullNodeSimulator = env.full_node.api
646
+ client: WalletRpcClient = env.wallet_1.rpc_client
647
+ client_node: FullNodeRpcClient = env.full_node.rpc_client
648
+
649
+ await generate_funds(full_node_api, env.wallet_1)
650
+
651
+ signed_tx_amount = uint64(888000)
652
+ tx_coin_announcements = [
653
+ CreateCoinAnnouncement(
654
+ std_hash(b"\xca" + std_hash(b"message")),
655
+ std_hash(b"coin_id_1"),
656
+ ),
657
+ CreateCoinAnnouncement(
658
+ bytes(Program.to("a string")),
659
+ std_hash(b"coin_id_2"),
660
+ ),
661
+ ]
662
+ outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
663
+ tx_res: TransactionRecord = (
664
+ await client.create_signed_transactions(
665
+ outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_coin_announcements,)
666
+ )
667
+ ).signed_tx
668
+ assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
669
+ await assert_push_tx_error(client_node, tx_res)
670
+
671
+
672
+ @pytest.mark.anyio
673
+ async def test_create_signed_transaction_with_puzzle_announcement(wallet_rpc_environment: WalletRpcTestEnvironment):
674
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
675
+
676
+ wallet_2: Wallet = env.wallet_2.wallet
677
+ full_node_api: FullNodeSimulator = env.full_node.api
678
+ client: WalletRpcClient = env.wallet_1.rpc_client
679
+ client_node: FullNodeRpcClient = env.full_node.rpc_client
680
+
681
+ await generate_funds(full_node_api, env.wallet_1)
682
+
683
+ signed_tx_amount = uint64(888000)
684
+ tx_puzzle_announcements = [
685
+ CreatePuzzleAnnouncement(
686
+ std_hash(b"\xca" + std_hash(b"message")),
687
+ std_hash(b"puzzle_hash_1"),
688
+ ),
689
+ CreatePuzzleAnnouncement(
690
+ bytes(Program.to("a string")),
691
+ std_hash(b"puzzle_hash_2"),
692
+ ),
693
+ ]
694
+ outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
695
+ tx_res = (
696
+ await client.create_signed_transactions(
697
+ outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_puzzle_announcements,)
698
+ )
699
+ ).signed_tx
700
+ assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
701
+ await assert_push_tx_error(client_node, tx_res)
702
+
703
+
704
+ @pytest.mark.anyio
705
+ async def test_create_signed_transaction_with_excluded_coins(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
706
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
707
+ wallet_1: Wallet = env.wallet_1.wallet
708
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
709
+ full_node_api: FullNodeSimulator = env.full_node.api
710
+ full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
711
+ await generate_funds(full_node_api, env.wallet_1)
712
+
713
+ async def it_does_not_include_the_excluded_coins() -> None:
714
+ selected_coins = await wallet_1_rpc.select_coins(
715
+ amount=250000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
716
+ )
717
+ assert len(selected_coins) == 1
718
+ outputs = await create_tx_outputs(wallet_1, [(uint64(250000000000), None)])
719
+
720
+ tx = (
721
+ await wallet_1_rpc.create_signed_transactions(
722
+ outputs,
723
+ DEFAULT_TX_CONFIG.override(
724
+ excluded_coin_ids=[c.name() for c in selected_coins],
725
+ ),
726
+ )
727
+ ).signed_tx
728
+
729
+ assert len(tx.removals) == 1
730
+ assert tx.removals[0] != selected_coins[0]
731
+ assert tx.removals[0].amount == uint64(1750000000000)
732
+ await assert_push_tx_error(full_node_rpc, tx)
733
+
734
+ async def it_throws_an_error_when_all_spendable_coins_are_excluded() -> None:
735
+ selected_coins = await wallet_1_rpc.select_coins(
736
+ amount=1750000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
737
+ )
738
+ assert len(selected_coins) == 1
739
+ outputs = await create_tx_outputs(wallet_1, [(uint64(1750000000000), None)])
740
+
741
+ with pytest.raises(ValueError):
742
+ await wallet_1_rpc.create_signed_transactions(
743
+ outputs,
744
+ DEFAULT_TX_CONFIG.override(
745
+ excluded_coin_ids=[c.name() for c in selected_coins],
746
+ ),
747
+ )
748
+
749
+ await it_does_not_include_the_excluded_coins()
750
+ await it_throws_an_error_when_all_spendable_coins_are_excluded()
751
+
752
+
753
+ @pytest.mark.anyio
754
+ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnvironment):
755
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
756
+
757
+ wallet_1_node: WalletNode = env.wallet_1.node
758
+ wallet_2_node: WalletNode = env.wallet_2.node
759
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
760
+ wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
761
+ wallet_1 = wallet_1_node.wallet_state_manager.main_wallet
762
+ wallet_2 = wallet_2_node.wallet_state_manager.main_wallet
763
+ full_node_api: FullNodeSimulator = env.full_node.api
764
+ wallet_2_api = WalletRpcApi(wallet_2_node)
765
+
766
+ generated_funds = await generate_funds(full_node_api, env.wallet_1, 1)
767
+ await generate_funds(full_node_api, env.wallet_2, 1)
768
+ wallet_1_puzhash = await wallet_1.get_new_puzzlehash()
769
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=20)
770
+ wallet_2_puzhash = await wallet_2.get_new_puzzlehash()
771
+ tx = (
772
+ await wallet_1_rpc.send_transaction(
773
+ wallet_id=1,
774
+ amount=uint64(500),
775
+ address=encode_puzzle_hash(wallet_2_puzhash, "txch"),
776
+ tx_config=DEFAULT_TX_CONFIG,
777
+ fee=uint64(0),
778
+ puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
779
+ )
780
+ ).transaction
781
+ clawback_coin_id_1 = tx.additions[0].name()
782
+ assert tx.spend_bundle is not None
783
+ await farm_transaction(full_node_api, wallet_1_node, tx.spend_bundle)
784
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=20)
785
+ tx = (
786
+ await wallet_2_rpc.send_transaction(
787
+ wallet_id=1,
788
+ amount=uint64(500),
789
+ address=encode_puzzle_hash(wallet_1_puzhash, "txch"),
790
+ tx_config=DEFAULT_TX_CONFIG,
791
+ fee=uint64(0),
792
+ puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
793
+ )
794
+ ).transaction
795
+ assert tx.spend_bundle is not None
796
+ clawback_coin_id_2 = tx.additions[0].name()
797
+ await farm_transaction(full_node_api, wallet_2_node, tx.spend_bundle)
798
+ await time_out_assert(20, get_confirmed_balance, generated_funds - 500, wallet_1_rpc, 1)
799
+ await time_out_assert(20, get_confirmed_balance, generated_funds - 500, wallet_2_rpc, 1)
800
+ await asyncio.sleep(10)
801
+ # Test missing coin_ids
802
+ has_exception = False
803
+ try:
804
+ await wallet_2_api.spend_clawback_coins({})
805
+ except ValueError:
806
+ has_exception = True
807
+ assert has_exception
808
+ # Test coin ID is not a Clawback coin
809
+ invalid_coin_id = tx.removals[0].name()
810
+ resp = await wallet_2_rpc.spend_clawback_coins([invalid_coin_id], 500)
811
+ assert resp["success"]
812
+ assert resp["transaction_ids"] == []
813
+ # Test unsupported wallet
814
+ coin_record = await wallet_1_node.wallet_state_manager.coin_store.get_coin_record(clawback_coin_id_1)
815
+ assert coin_record is not None
816
+ await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
817
+ dataclasses.replace(coin_record, wallet_type=WalletType.CAT)
818
+ )
819
+ resp = await wallet_1_rpc.spend_clawback_coins([clawback_coin_id_1], 100)
820
+ assert resp["success"]
821
+ assert len(resp["transaction_ids"]) == 0
822
+ # Test missing metadata
823
+ await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(dataclasses.replace(coin_record, metadata=None))
824
+ resp = await wallet_1_rpc.spend_clawback_coins([clawback_coin_id_1], 100)
825
+ assert resp["success"]
826
+ assert len(resp["transaction_ids"]) == 0
827
+ # Test missing incoming tx
828
+ coin_record = await wallet_1_node.wallet_state_manager.coin_store.get_coin_record(clawback_coin_id_2)
829
+ assert coin_record is not None
830
+ fake_coin = Coin(coin_record.coin.parent_coin_info, wallet_2_puzhash, coin_record.coin.amount)
831
+ await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
832
+ dataclasses.replace(coin_record, coin=fake_coin)
833
+ )
834
+ resp = await wallet_1_rpc.spend_clawback_coins([fake_coin.name()], 100)
835
+ assert resp["transaction_ids"] == []
836
+ # Test coin puzzle hash doesn't match the puzzle
837
+ farmed_tx = (await wallet_1.wallet_state_manager.tx_store.get_farming_rewards())[0]
838
+ await wallet_1.wallet_state_manager.tx_store.add_transaction_record(
839
+ dataclasses.replace(farmed_tx, name=fake_coin.name())
840
+ )
841
+ await wallet_1_node.wallet_state_manager.coin_store.add_coin_record(
842
+ dataclasses.replace(coin_record, coin=fake_coin)
843
+ )
844
+ resp = await wallet_1_rpc.spend_clawback_coins([fake_coin.name()], 100)
845
+ assert resp["transaction_ids"] == []
846
+ # Test claim spend
847
+ await wallet_2_rpc.set_auto_claim(
848
+ AutoClaimSettings(
849
+ enabled=False,
850
+ tx_fee=uint64(100),
851
+ min_amount=uint64(0),
852
+ batch_size=uint16(1),
853
+ )
854
+ )
855
+ resp = await wallet_2_rpc.spend_clawback_coins([clawback_coin_id_1, clawback_coin_id_2], 100)
856
+ assert resp["success"]
857
+ assert len(resp["transaction_ids"]) == 2
858
+ for _tx in resp["transactions"]:
859
+ clawback_tx = TransactionRecord.from_json_dict_convenience(_tx)
860
+ if clawback_tx.spend_bundle is not None:
861
+ await time_out_assert_not_none(
862
+ 10, full_node_api.full_node.mempool_manager.get_spendbundle, clawback_tx.spend_bundle.name()
863
+ )
864
+ await farm_transaction_block(full_node_api, wallet_2_node)
865
+ await time_out_assert(20, get_confirmed_balance, generated_funds + 300, wallet_2_rpc, 1)
866
+ # Test spent coin
867
+ resp = await wallet_2_rpc.spend_clawback_coins([clawback_coin_id_1], 500)
868
+ assert resp["success"]
869
+ assert resp["transaction_ids"] == []
870
+
871
+
872
+ @pytest.mark.anyio
873
+ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvironment):
874
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
875
+
876
+ wallet_2: Wallet = env.wallet_2.wallet
877
+ wallet_node: WalletNode = env.wallet_1.node
878
+ full_node_api: FullNodeSimulator = env.full_node.api
879
+ client: WalletRpcClient = env.wallet_1.rpc_client
880
+
881
+ generated_funds = await generate_funds(full_node_api, env.wallet_1)
882
+
883
+ removals = await client.select_coins(
884
+ 1750000000000, wallet_id=1, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
885
+ ) # we want a coin that won't be selected by default
886
+ outputs = await create_tx_outputs(wallet_2, [(uint64(1), ["memo_1"]), (uint64(2), ["memo_2"])])
887
+ amount_outputs = sum(output["amount"] for output in outputs)
888
+ amount_fee = uint64(amount_outputs + 1)
889
+
890
+ send_tx_res: TransactionRecord = (
891
+ await client.send_transaction_multi(
892
+ 1,
893
+ outputs,
894
+ DEFAULT_TX_CONFIG,
895
+ coins=removals,
896
+ fee=amount_fee,
897
+ )
898
+ ).transaction
899
+ spend_bundle = send_tx_res.spend_bundle
900
+ assert spend_bundle is not None
901
+ assert send_tx_res is not None
902
+
903
+ assert_tx_amounts(send_tx_res, outputs, amount_fee=amount_fee, change_expected=True)
904
+ assert send_tx_res.removals == removals
905
+
906
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
907
+
908
+ await time_out_assert(20, get_confirmed_balance, generated_funds - amount_outputs - amount_fee, client, 1)
909
+
910
+ # Checks that the memo can be retrieved
911
+ tx_confirmed = await client.get_transaction(send_tx_res.name)
912
+ assert tx_confirmed.confirmed
913
+ memos = tx_confirmed.get_memos()
914
+ assert len(memos) == len(outputs)
915
+ for output in outputs:
916
+ assert [output["memos"][0].encode()] in memos.values()
917
+ spend_bundle = send_tx_res.spend_bundle
918
+ assert spend_bundle is not None
919
+ for key in memos.keys():
920
+ assert key in [a.name() for a in spend_bundle.additions()]
921
+
922
+
923
+ @pytest.mark.anyio
924
+ async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment):
925
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
926
+
927
+ wallet: Wallet = env.wallet_1.wallet
928
+ wallet_node: WalletNode = env.wallet_1.node
929
+ full_node_api: FullNodeSimulator = env.full_node.api
930
+ client: WalletRpcClient = env.wallet_1.rpc_client
931
+
932
+ await generate_funds(full_node_api, env.wallet_1, 5)
933
+
934
+ all_transactions = await client.get_transactions(1)
935
+ assert len(all_transactions) >= 10
936
+ # Test transaction pagination
937
+ some_transactions = await client.get_transactions(1, 0, 5)
938
+ some_transactions_2 = await client.get_transactions(1, 5, 10)
939
+ assert some_transactions == all_transactions[0:5]
940
+ assert some_transactions_2 == all_transactions[5:10]
941
+
942
+ # Testing sorts
943
+ # Test the default sort (CONFIRMED_AT_HEIGHT)
944
+ assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"))
945
+ all_transactions = await client.get_transactions(1, reverse=True)
946
+ assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"), reverse=True)
947
+
948
+ # Test RELEVANCE
949
+ puzhash = await wallet.get_new_puzzlehash()
950
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
951
+ await client.send_transaction(
952
+ 1, uint64(1), encode_puzzle_hash(puzhash, "txch"), DEFAULT_TX_CONFIG
953
+ ) # Create a pending tx
954
+
955
+ all_transactions = await client.get_transactions(1, sort_key=SortKey.RELEVANCE)
956
+ sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"), reverse=True)
957
+ sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"), reverse=True)
958
+ sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"))
959
+ assert all_transactions == sorted_transactions
960
+
961
+ all_transactions = await client.get_transactions(1, sort_key=SortKey.RELEVANCE, reverse=True)
962
+ sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"))
963
+ sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"))
964
+ sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"), reverse=True)
965
+ assert all_transactions == sorted_transactions
966
+
967
+ # Test get_transactions to address
968
+ ph_by_addr = await wallet.get_new_puzzlehash()
969
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
970
+ await client.send_transaction(1, uint64(1), encode_puzzle_hash(ph_by_addr, "txch"), DEFAULT_TX_CONFIG)
971
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
972
+ tx_for_address = await client.get_transactions(1, to_address=encode_puzzle_hash(ph_by_addr, "txch"))
973
+ assert len(tx_for_address) == 1
974
+ assert tx_for_address[0].to_puzzle_hash == ph_by_addr
975
+
976
+ # Test type filter
977
+ all_transactions = await client.get_transactions(
978
+ 1, type_filter=TransactionTypeFilter.include([TransactionType.COINBASE_REWARD])
979
+ )
980
+ assert len(all_transactions) == 5
981
+ assert all(transaction.type == TransactionType.COINBASE_REWARD for transaction in all_transactions)
982
+ # Test confirmed filter
983
+ all_transactions = await client.get_transactions(1, confirmed=True)
984
+ assert len(all_transactions) == 10
985
+ assert all(transaction.confirmed for transaction in all_transactions)
986
+ all_transactions = await client.get_transactions(1, confirmed=False)
987
+ assert len(all_transactions) == 2
988
+ assert all(not transaction.confirmed for transaction in all_transactions)
989
+
990
+ # Test bypass broken txs
991
+ await wallet.wallet_state_manager.tx_store.add_transaction_record(
992
+ dataclasses.replace(all_transactions[0], type=uint32(TransactionType.INCOMING_CLAWBACK_SEND))
993
+ )
994
+ all_transactions = await client.get_transactions(
995
+ 1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND]), confirmed=False
996
+ )
997
+ assert len(all_transactions) == 1
998
+
999
+
1000
+ @pytest.mark.anyio
1001
+ async def test_get_transaction_count(wallet_rpc_environment: WalletRpcTestEnvironment):
1002
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1003
+
1004
+ full_node_api: FullNodeSimulator = env.full_node.api
1005
+ client: WalletRpcClient = env.wallet_1.rpc_client
1006
+
1007
+ await generate_funds(full_node_api, env.wallet_1)
1008
+
1009
+ all_transactions = await client.get_transactions(1)
1010
+ assert len(all_transactions) > 0
1011
+ transaction_count = await client.get_transaction_count(1)
1012
+ assert transaction_count == len(all_transactions)
1013
+ transaction_count = await client.get_transaction_count(1, confirmed=False)
1014
+ assert transaction_count == 0
1015
+ transaction_count = await client.get_transaction_count(
1016
+ 1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND])
1017
+ )
1018
+ assert transaction_count == 0
1019
+
1020
+
1021
+ @pytest.mark.anyio
1022
+ async def test_cat_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
1023
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1024
+
1025
+ wallet_node: WalletNode = env.wallet_1.node
1026
+
1027
+ client: WalletRpcClient = env.wallet_1.rpc_client
1028
+ client_2: WalletRpcClient = env.wallet_2.rpc_client
1029
+
1030
+ full_node_api: FullNodeSimulator = env.full_node.api
1031
+
1032
+ await generate_funds(full_node_api, env.wallet_1, 1)
1033
+ await generate_funds(full_node_api, env.wallet_2, 1)
1034
+
1035
+ # Test a deprecated path
1036
+ with pytest.raises(ValueError, match="dropped"):
1037
+ await client.fetch(
1038
+ "create_new_wallet",
1039
+ {
1040
+ "wallet_type": "cat_wallet",
1041
+ "mode": "new",
1042
+ },
1043
+ )
1044
+
1045
+ # Creates a CAT wallet with 100 mojos and a CAT with 20 mojos and fee=10
1046
+ await client.create_new_cat_and_wallet(uint64(100), fee=uint64(10), test=True)
1047
+ await time_out_assert(20, client.get_synced)
1048
+
1049
+ res = await client.create_new_cat_and_wallet(uint64(20), test=True)
1050
+ assert res["success"]
1051
+ cat_0_id = res["wallet_id"]
1052
+ asset_id = bytes32.fromhex(res["asset_id"])
1053
+ assert len(asset_id) > 0
1054
+
1055
+ await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
1056
+ await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1})
1057
+
1058
+ bal_0 = await client.get_wallet_balance(cat_0_id)
1059
+ assert bal_0["confirmed_wallet_balance"] == 0
1060
+ assert bal_0["pending_coin_removal_count"] == 1
1061
+ col = await client.get_cat_asset_id(cat_0_id)
1062
+ assert col == asset_id
1063
+ assert (await client.get_cat_name(cat_0_id)) == CATWallet.default_wallet_name_for_unknown_cat(asset_id.hex())
1064
+ await client.set_cat_name(cat_0_id, "My cat")
1065
+ assert (await client.get_cat_name(cat_0_id)) == "My cat"
1066
+ result = await client.cat_asset_id_to_name(col)
1067
+ assert result is not None
1068
+ wid, name = result
1069
+ assert wid == cat_0_id
1070
+ assert name == "My cat"
1071
+ result = await client.cat_asset_id_to_name(bytes32([0] * 32))
1072
+ assert result is None
1073
+ verified_asset_id = next(iter(DEFAULT_CATS.items()))[1]["asset_id"]
1074
+ result = await client.cat_asset_id_to_name(bytes32.from_hexstr(verified_asset_id))
1075
+ assert result is not None
1076
+ should_be_none, name = result
1077
+ assert should_be_none is None
1078
+ assert name == next(iter(DEFAULT_CATS.items()))[1]["name"]
1079
+
1080
+ # make sure spend is in mempool before farming tx block
1081
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
1082
+ for i in range(5):
1083
+ if check_mempool_spend_count(full_node_api, 0):
1084
+ break
1085
+ await farm_transaction_block(full_node_api, wallet_node)
1086
+
1087
+ # check that we farmed the transaction
1088
+ assert check_mempool_spend_count(full_node_api, 0)
1089
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
1090
+
1091
+ await time_out_assert(5, get_confirmed_balance, 20, client, cat_0_id)
1092
+ bal_0 = await client.get_wallet_balance(cat_0_id)
1093
+ assert bal_0["pending_coin_removal_count"] == 0
1094
+ assert bal_0["unspent_coin_count"] == 1
1095
+
1096
+ # Creates a second wallet with the same CAT
1097
+ res = await client_2.create_wallet_for_existing_cat(asset_id)
1098
+ assert res["success"]
1099
+ cat_1_id = res["wallet_id"]
1100
+ cat_1_asset_id = bytes.fromhex(res["asset_id"])
1101
+ assert cat_1_asset_id == asset_id
1102
+
1103
+ await assert_wallet_types(client, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 2})
1104
+ await assert_wallet_types(client_2, {WalletType.STANDARD_WALLET: 1, WalletType.CAT: 1})
1105
+
1106
+ await farm_transaction_block(full_node_api, wallet_node)
1107
+
1108
+ bal_1 = await client_2.get_wallet_balance(cat_1_id)
1109
+ assert bal_1["confirmed_wallet_balance"] == 0
1110
+
1111
+ addr_0 = await client.get_next_address(cat_0_id, False)
1112
+ addr_1 = await client_2.get_next_address(cat_1_id, False)
1113
+
1114
+ assert addr_0 != addr_1
1115
+
1116
+ # Test CAT spend without a fee
1117
+ with pytest.raises(ValueError):
1118
+ await client.cat_spend(
1119
+ cat_0_id,
1120
+ DEFAULT_TX_CONFIG.override(
1121
+ excluded_coin_amounts=[uint64(20)],
1122
+ excluded_coin_ids=[bytes32([0] * 32)],
1123
+ ),
1124
+ uint64(4),
1125
+ addr_1,
1126
+ uint64(0),
1127
+ ["the cat memo"],
1128
+ )
1129
+ tx_res = await client.cat_spend(cat_0_id, DEFAULT_TX_CONFIG, uint64(4), addr_1, uint64(0), ["the cat memo"])
1130
+
1131
+ spend_bundle = tx_res.transaction.spend_bundle
1132
+ assert spend_bundle is not None
1133
+ assert uncurry_puzzle(spend_bundle.coin_spends[0].puzzle_reveal).mod == CAT_MOD
1134
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
1135
+
1136
+ await farm_transaction_block(full_node_api, wallet_node)
1137
+
1138
+ # Test CAT spend with a fee
1139
+ tx_res = await client.cat_spend(cat_0_id, DEFAULT_TX_CONFIG, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"])
1140
+
1141
+ spend_bundle = tx_res.transaction.spend_bundle
1142
+ assert spend_bundle is not None
1143
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
1144
+
1145
+ # Test CAT spend with a fee and pre-specified removals / coins
1146
+ removals = await client.select_coins(
1147
+ amount=uint64(2), wallet_id=cat_0_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
1148
+ )
1149
+ tx_res = await client.cat_spend(
1150
+ cat_0_id, DEFAULT_TX_CONFIG, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"], removals=removals
1151
+ )
1152
+
1153
+ spend_bundle = tx_res.transaction.spend_bundle
1154
+ assert spend_bundle is not None
1155
+ assert removals[0] in {removal for tx in tx_res.transactions for removal in tx.removals}
1156
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
1157
+
1158
+ # Test unacknowledged CAT
1159
+ await wallet_node.wallet_state_manager.interested_store.add_unacknowledged_token(
1160
+ asset_id, "Unknown", uint32(10000), bytes32(b"\00" * 32)
1161
+ )
1162
+ cats = await client.get_stray_cats()
1163
+ assert len(cats) == 1
1164
+
1165
+ await time_out_assert(20, get_confirmed_balance, 14, client, cat_0_id)
1166
+ await time_out_assert(20, get_confirmed_balance, 6, client_2, cat_1_id)
1167
+
1168
+ # Test CAT coin selection
1169
+ selected_coins = await client.select_coins(
1170
+ amount=1, wallet_id=cat_0_id, coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
1171
+ )
1172
+ assert len(selected_coins) > 0
1173
+
1174
+ # Test get_cat_list
1175
+ cat_list = (await client.get_cat_list()).cat_list
1176
+ assert len(DEFAULT_CATS) == len(cat_list)
1177
+ default_cats_set = {
1178
+ DefaultCAT(asset_id=bytes32.from_hexstr(cat["asset_id"]), name=cat["name"], symbol=cat["symbol"])
1179
+ for cat in DEFAULT_CATS.values()
1180
+ }
1181
+ assert default_cats_set == set(cat_list)
1182
+
1183
+
1184
+ @pytest.mark.anyio
1185
+ async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
1186
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1187
+
1188
+ wallet_node: WalletNode = env.wallet_1.node
1189
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
1190
+ wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
1191
+ full_node_api: FullNodeSimulator = env.full_node.api
1192
+
1193
+ await generate_funds(full_node_api, env.wallet_1, 1)
1194
+ await generate_funds(full_node_api, env.wallet_2, 1)
1195
+
1196
+ # Creates a CAT wallet with 20 mojos
1197
+ res = await wallet_1_rpc.create_new_cat_and_wallet(uint64(20), test=True)
1198
+ assert res["success"]
1199
+ cat_wallet_id = res["wallet_id"]
1200
+ cat_asset_id = bytes32.fromhex(res["asset_id"])
1201
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1202
+ await farm_transaction_block(full_node_api, wallet_node)
1203
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
1204
+
1205
+ await time_out_assert(5, get_confirmed_balance, 20, wallet_1_rpc, cat_wallet_id)
1206
+
1207
+ # Creates a wallet for the same CAT on wallet_2 and send 4 CAT from wallet_1 to it
1208
+ await wallet_2_rpc.create_wallet_for_existing_cat(cat_asset_id)
1209
+ wallet_2_address = await wallet_2_rpc.get_next_address(cat_wallet_id, False)
1210
+ adds = [{"puzzle_hash": decode_puzzle_hash(wallet_2_address), "amount": uint64(4), "memos": ["the cat memo"]}]
1211
+ tx_res = (
1212
+ await wallet_1_rpc.send_transaction_multi(
1213
+ cat_wallet_id, additions=adds, tx_config=DEFAULT_TX_CONFIG, fee=uint64(0)
1214
+ )
1215
+ ).transaction
1216
+ spend_bundle = tx_res.spend_bundle
1217
+ assert spend_bundle is not None
1218
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
1219
+ await time_out_assert(5, get_confirmed_balance, 4, wallet_2_rpc, cat_wallet_id)
1220
+ test_crs: List[CoinRecord] = await wallet_1_rpc.get_coin_records_by_names(
1221
+ [a.name() for a in spend_bundle.additions() if a.amount != 4]
1222
+ )
1223
+ for cr in test_crs:
1224
+ assert cr.coin in spend_bundle.additions()
1225
+ with pytest.raises(ValueError):
1226
+ await wallet_1_rpc.get_coin_records_by_names([a.name() for a in spend_bundle.additions() if a.amount == 4])
1227
+ # Create an offer of 5 chia for one CAT
1228
+ await wallet_1_rpc.create_offer_for_ids(
1229
+ {uint32(1): -5, cat_asset_id.hex(): 1}, DEFAULT_TX_CONFIG, validate_only=True
1230
+ )
1231
+ all_offers = await wallet_1_rpc.get_all_offers()
1232
+ assert len(all_offers) == 0
1233
+
1234
+ driver_dict: Dict[str, Any] = {cat_asset_id.hex(): {"type": "CAT", "tail": "0x" + cat_asset_id.hex()}}
1235
+
1236
+ create_res = await wallet_1_rpc.create_offer_for_ids(
1237
+ {uint32(1): -5, cat_asset_id.hex(): 1},
1238
+ DEFAULT_TX_CONFIG,
1239
+ driver_dict=driver_dict,
1240
+ fee=uint64(1),
1241
+ )
1242
+ offer = create_res.offer
1243
+
1244
+ id, summary = await wallet_1_rpc.get_offer_summary(offer)
1245
+ assert id == offer.name()
1246
+ id, advanced_summary = await wallet_1_rpc.get_offer_summary(offer, advanced=True)
1247
+ assert id == offer.name()
1248
+ assert summary == {
1249
+ "offered": {"xch": 5},
1250
+ "requested": {cat_asset_id.hex(): 1},
1251
+ "infos": driver_dict,
1252
+ "fees": 1,
1253
+ "additions": [c.name().hex() for c in offer.additions()],
1254
+ "removals": [c.name().hex() for c in offer.removals()],
1255
+ "valid_times": {
1256
+ "max_height": None,
1257
+ "max_time": None,
1258
+ "min_height": None,
1259
+ "min_time": None,
1260
+ },
1261
+ }
1262
+ assert advanced_summary == summary
1263
+
1264
+ id, valid = await wallet_1_rpc.check_offer_validity(offer)
1265
+ assert id == offer.name()
1266
+
1267
+ all_offers = await wallet_1_rpc.get_all_offers(file_contents=True)
1268
+ assert len(all_offers) == 1
1269
+ assert TradeStatus(all_offers[0].status) == TradeStatus.PENDING_ACCEPT
1270
+ assert all_offers[0].offer == bytes(offer)
1271
+
1272
+ offer_count = await wallet_1_rpc.get_offers_count()
1273
+ assert offer_count.total == 1
1274
+ assert offer_count.my_offers_count == 1
1275
+ assert offer_count.taken_offers_count == 0
1276
+
1277
+ trade_record = (await wallet_2_rpc.take_offer(offer, DEFAULT_TX_CONFIG, fee=uint64(1))).trade_record
1278
+ assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM
1279
+
1280
+ await wallet_1_rpc.cancel_offer(offer.name(), DEFAULT_TX_CONFIG, secure=False)
1281
+
1282
+ trade_record = await wallet_1_rpc.get_offer(offer.name(), file_contents=True)
1283
+ assert trade_record.offer == bytes(offer)
1284
+ assert TradeStatus(trade_record.status) == TradeStatus.CANCELLED
1285
+
1286
+ await wallet_1_rpc.cancel_offer(offer.name(), DEFAULT_TX_CONFIG, fee=uint64(1), secure=True)
1287
+
1288
+ trade_record = await wallet_1_rpc.get_offer(offer.name())
1289
+ assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CANCEL
1290
+
1291
+ create_res = await wallet_1_rpc.create_offer_for_ids(
1292
+ {uint32(1): -5, cat_wallet_id: 1}, DEFAULT_TX_CONFIG, fee=uint64(1)
1293
+ )
1294
+ all_offers = await wallet_1_rpc.get_all_offers()
1295
+ assert len(all_offers) == 2
1296
+ offer_count = await wallet_1_rpc.get_offers_count()
1297
+ assert offer_count.total == 2
1298
+ assert offer_count.my_offers_count == 2
1299
+ assert offer_count.taken_offers_count == 0
1300
+ new_trade_record = create_res.trade_record
1301
+
1302
+ await farm_transaction_block(full_node_api, wallet_node)
1303
+
1304
+ async def is_trade_confirmed(client, trade) -> bool:
1305
+ trade_record = await client.get_offer(trade.name())
1306
+ return TradeStatus(trade_record.status) == TradeStatus.CONFIRMED
1307
+
1308
+ await time_out_assert(15, is_trade_confirmed, True, wallet_1_rpc, offer)
1309
+
1310
+ # Test trade sorting
1311
+ def only_ids(trades):
1312
+ return [t.trade_id for t in trades]
1313
+
1314
+ trade_record = await wallet_1_rpc.get_offer(offer.name())
1315
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True) # confirmed at index descending
1316
+ assert len(all_offers) == 2
1317
+ assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
1318
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, reverse=True) # confirmed at index ascending
1319
+ assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
1320
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, sort_key="RELEVANCE") # most relevant
1321
+ assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
1322
+ all_offers = await wallet_1_rpc.get_all_offers(
1323
+ include_completed=True, sort_key="RELEVANCE", reverse=True
1324
+ ) # least relevant
1325
+ assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
1326
+ # Test pagination
1327
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=1)
1328
+ assert len(all_offers) == 1
1329
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=50)
1330
+ assert len(all_offers) == 0
1331
+ all_offers = await wallet_1_rpc.get_all_offers(include_completed=True, start=0, end=50)
1332
+ assert len(all_offers) == 2
1333
+
1334
+ ###
1335
+ # This is temporary code, delete it when we no longer care about incorrectly parsing old offers
1336
+ # There's also temp code in wallet_rpc_api.py
1337
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1338
+ await wallet_1_rpc.fetch(
1339
+ "get_offer_summary",
1340
+ {
1341
+ "offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
1342
+ },
1343
+ )
1344
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1345
+ await wallet_1_rpc.fetch(
1346
+ "check_offer_validity",
1347
+ {
1348
+ "offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
1349
+ },
1350
+ )
1351
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1352
+ await wallet_1_rpc.fetch(
1353
+ "take_offer",
1354
+ {
1355
+ "offer": "offer1qqr83wcuu2rykcmqvpsxygqq0qthwpmsrsutadthxda7ppk8wemm74e88h2dh265k7fv7kdk2etmg99xpm0yu8ddl4rkhl73mflmfasl4h75w6lllup4t7urp0mstehm8cnmc6mdxn0rvdejdfpway2ccm6srenn6urvdmgvand96gat25z42f4u2sh7ad7upvhfty77v6ak76dmrdxrzdgn89mlr58ya6j77yax8095r2u2g826yg9jq4q6xzt4g6yveqnhg6r77lrt3weksdwcwz5ava4t5mtdem5syjejwg53q6t5lfd2t5p6dds9dylj7jxw2fmquugdnkvke5jckmsr8ne4akwha9hv9y24x6shezhaxqjegegprjp6h8hua2a24lntev22zkxdkhya5u7k6uhenv2jwwf8nddcs99707290u0yw7jv8w500yarnlew75keam9mwejmanardkdpk6awxj7ndl9ka35zfvydvfj646fuq7j6zv5uzl3czrw76psrnaudu2d65mtez7m9sz9xat52s87v6vmlpmknvju0u4wq5kklflwuamnaczl8vkne284ehyamaaxuzkcdvpjg3tlt7cxhy0r6fammc0arha65nxcv7kpxmra5zck7emrn7v4teuk6473eh7l39mqp5zafdmygm0tf0hp2ug20fhk7mlkmve4atg76xv9mw0xe2ped72mvkqall4hstp0a9jsxyyasz6dl7zmka2kwfx94w0knut43r4x447w3shmw5alldevrdu2gthelcjt8h6775p92ktlmlg846varj62rghj67e2hyhlauwkkv6nhnvt7wm6tt2kh2xw6kze0ttxsqplfct7mk4jvnykm0arw2juvnmkudgyerj59dn4ja8r5avud67zd7mr2cl54skynhs4twu2qgwrcdzcchhfee008e30yazzwu96h2uhaprejkrgns5w8nds244k3uyfhtmmzne29hmmdsvvlrtr02w0nc0thtkpywwmmxuqfc0tsssdflned" # noqa: E501
1356
+ },
1357
+ )
1358
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1359
+ await wallet_1_rpc.fetch(
1360
+ "get_offer_summary",
1361
+ {
1362
+ "offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
1363
+ },
1364
+ )
1365
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1366
+ await wallet_1_rpc.fetch(
1367
+ "check_offer_validity",
1368
+ {
1369
+ "offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
1370
+ },
1371
+ )
1372
+ with pytest.raises(ValueError, match="Old offer format is no longer supported"):
1373
+ await wallet_1_rpc.fetch(
1374
+ "take_offer",
1375
+ {
1376
+ "offer": "offer1qqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqx68z6sprqv0dvdkvr4rv8r8mwlw7mzuyht6gn74y8yx8ta952h2qqqqqqqqqqqqrls9lllq8ls9lllq8ls9l67lllsflczlllsflllqnlstlllqnll7zllxnlstq8lluz07zllszqgpq8lluz0llczlutl7tuqlllsfl6llllsflllqtljalllqnls9lllqnl30luqszqgplllqnll7qhl9tll7p8lqtll7p8lsgp8llllqnlcyptllllsfluzpdlllqyqszqgpq8lluz0lqdllllsfluzq9llllcyl7pq9lllluz0lqs9llll7p8lsg9llluqszqgpqyqszqgpqyqszq0llcylllsrlllllln63hlqtlnx08lluzqrlcpl7qukqhlllljplczllls8lc9lllsrlczlue0llcylup0llcyluxlllcylllshlmulllshle5lujgplllp0lhelllp0lhelllp0lnflevsrlsnq8llu9l7l8lp0ll7zllxnlcpqyqszq0lqyqszqgplllqy9cplcpsrll7qhlluplllezlllsnlllphlstq8ly2q0llcflllsmlctsrlj9q8llu2l79llluqcrluqsrll7q0lp0lstlctlutcplllq8ls3qyqluqcplllqtll7qllp0ll7q0lqtll7qllluylllczluh0llcylup0llcylufllqyqszq0lqstn7q0llcplup074hlluz07qhlluz0llczluflllcyla0lllcylutlllcyluhlllcyl7qmllllqnlcyqtllllsflcml7qgpqyqszqgpq8lluz0lqsp0llcpqyqszq0llcpluygpq8lqxq0llcplup0llcrlutlllcplup0llcrllljpluph7q0llcpsgqhllllq8ls3qyqluqcplllq8ls3qyqluqcpq8lqxq07p8lluz07p0ly7q0llcylll3plctlatcplmhszq0llllqtll7qllqhll7q0lqtll7qllluylllczllls8lllp8l3rl6csrll7q2el7qgplcpsrll7qvp37q0llcplup07fhlluz07qhlluz07r0lluz07zllluz0llcyl7qmnluzq9ucpluqszqgpqyqlllsrlczlaa0llcylup0llcyllls9lllq0ll7z0lz8l43q8lluql7p8ltrll7p8llup07ahlluz07qhlluz07yllluz0720lluz0llctlu607kuqlllsfletl7qgpqyqszqgpleeszq0llcplup0llcrlllsnlc3laugplllq8ls9lllq0ll7g8llup0llcrlllsnlllqyslllcdlu5cpq8lluql7qhlluplllcflllselefl7q07dyqlawgplllq8lszq0lszq07qvql7qgplcpszq0llcpp8ll7q0lpzqgplcpsrll7qgfsrlsrqyqluqcplllqnll7qhlluplllcflugl7kyqlllszk0lszq07qvqlllsflllqtljdlllqnls9lllqnlsmlllqnlshlllqnl30luqszqgpqyql7qgpqyqszqgplcpsrll7q0lqnlcplllqnlcplchszqgplcpsrll7qhllupl7p0lluql7p8lp8ll7qhl2mll7p8lqtll7p8lphll7p8lp0lcpqyqszqgplllqy9cplcpsrlshlmulllshle5lu5gplllp0lhelllp0lhelllp0lnflevsrlstq8llu9l7l8llup07vhlluz07qhlluz07pllluz0llctlu607dyql7qgpqyqsrll7zllxnlcpqyqszq0llczllls8lllqllstq8lluql7zllluqs9lllqtljalllqnls9lllqnlsnluqszqgplllqtljalllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqlllsflllqnlnplllqnl4lluqszq0llczlal0llcylup0llcylllsflllqnljllc9srll7p8ltllcyqtlszq0llcyllls9lexlllsflczlllsflctlllsflc9lllsrluqszqgpqyqlllsflchlllsfluphlll7p8lsgqhllllqnll7qhl9tll7p8lqtll7p8lsgz0llllqnll7qhlwmll7p8lqtll7p8lp8ll7p8lsg90llllqnll7zllxnljmq8lluz0790lszqgpqyqszq0llcyl7ppdlllszqgpqyqsrll7p8lsgzlllllqnlcyzlll7qgpqyqszqgpqyqszqgplczlad0llcylup0llcyla0lllcylualllcyllls9lllq0l30lllq8lsnledllls9le2lllsflczlllsfle8lllsflllqtlhdlllqnls9lllqnljnlllqnl40lllqnll7zllxnlcrwvqlllsfl6el7qgpqyqszqgplllqnlcrdllszqgpqyqszq0lqyqluqcplllqnl30lllqnlstlllqnlcyqhllllsflllqnll7p8l0rll7p8llu807h8llup07thlluz07qhlluz0llcyluhlllcyl7pqzlllszqgpluqszqgpq8lszqgplllqnll7p8lyrll7p8llu9llqdllaw0llczluh0llcylup0llcylllsflc4lllsflllzrlcyqtllll3rluzqt0l72uql7pq9luql7qgpq8lszqgpqyql7qgpq8lzwqgpluqszqgpqyqszqgpq8lqxqgplllqnll7qdqx7l0xc8wskqn8d5at9dfqmwyt5q675phnkk4zh4e2x9tklqa9fa0llcylllsrgqn55h9a7c7aq9zfj2tx6aa5268xz2r2ds5emgu9yut5hhkp96rrmll7p8lluql7qhlluql7qhlptll7p8lqtll7p8lq0lcpqyqsrll7p8lluqlllen8mll7qhllupl7p0lluql7p8lluz07r8lluz0llczlu00llcylup0llcyluyllqyqszq0lqyqsrll7qhlzmll7p8lqtll7p8lr8ll7p8llup07zhlluz07qhlluz07r0lszqgpq8lszqgpqyqsrlcpq8lqxq0llczllls8lc9lllsrlcylllsflcgluycplllqtl3dlllqnls9lllqnlsmlllqnlshluqszqgpqyqlllszzuqluqcplczllls8lllqllstq8lluql7zllluqs9lllqtl3alllqnls9lllqnlsnluqszqgplllqtl3alllqnls9lllqnlsmluqszqgpq8lluql7zllluqsrlc9szq07qvqluqcpq8lqxqgpqyqlll6pm3jc0w0dpj78zznpvxj057m22f2nk94gc3kus29jvxnefjjd4nklll6qehe6qve5g6r23z4txtrxjqhdg8npntzhw2w8yrkg7y50ksplt32lup0llaqvmuaqxv6ydp4g324n93nfqtk5rese43th98rjpmy0z28mgql4c4gpqyqsq00wsa2002kemp6v5g4avmmdc4edymhaj5vjzvnxuupgu00ufh83e8mt9q2autw93k0a55w5wf5mtdfdaxw9d3fk97dfqhtx8m2d32eqqqqqw3499peelczlllsrlczlllsrlczllls8lctlllsrlczllls8lllp8lstlllrhlshlllrmll7zllp0ll7qhlqmll7p8lqtll7p8lzllcpqyqszqgpqyqlllsrlczlutl7tuqlllsrlcgszq07qvqlllsrlcylllsflcylllsflc9lllsflllqtlsdlllqnls9lllqnl30luqszqgpluqszqgplllqtl30le0szqgplcpsrll7p8lluql7vhlqtll7qlllurl7pvqlllsrlctlllszqhllup07phlluz07qhlluz07z0lszqgpq8llup07phlluz07qhlluz07r0lszqgpqyqlllsrlctlllszq0lqkqgplcpsrlsrqyqlllsflllqxcgqwvaass7c74kdmgtgwq3v5kzcf7pdchnqjuw9yqd0agsgjr8tmua30nshgv7ddn8t3xehd0htnk5luqcpq8lsrll7q0lluellg96ufqk9maa268cn0r6xsre3fs33hcp384eu0uxj77w5fa0n8u008lsrq8lluellgyyddvdkw7jgeu9ugpwah0mk34v4un87qgnqaphe48qss0nmf637mlc2w3499pe4q8llu607qvqlllnelaqhyk2jzy6hxkmuzskhzpz5m6mwylj0h0akznfr3r7sw0e9n8ka2ycplll8ll6pp0j4c0pkvfpy06hd4gelhdvdp3798ghlx6m6eawkk5f4dcazw5cszq0lqyq5xlm42y5nv02th262u6tkmtmrq28nggx4mgvj35gewqav8wcn28jfgtphpthhwrs2pqys2s8zmwv6pur6s6vtyytar26hgp0tgcrc2xg0cxkqdfx3zyyckk769hp4p5dmvcfshc9a4j7feww6uvqc2mthvez7ttx" # noqa: E501
1377
+ },
1378
+ )
1379
+ ###
1380
+
1381
+ await wallet_1_rpc.create_offer_for_ids(
1382
+ {uint32(1): -5, cat_asset_id.hex(): 1},
1383
+ DEFAULT_TX_CONFIG,
1384
+ driver_dict=driver_dict,
1385
+ )
1386
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 2
1387
+ await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, batch_size=1)
1388
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
1389
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
1390
+
1391
+ await farm_transaction_block(full_node_api, wallet_node)
1392
+
1393
+ await wallet_1_rpc.create_offer_for_ids(
1394
+ {uint32(1): -5, cat_asset_id.hex(): 1},
1395
+ DEFAULT_TX_CONFIG,
1396
+ driver_dict=driver_dict,
1397
+ )
1398
+ await wallet_1_rpc.create_offer_for_ids(
1399
+ {uint32(1): 5, cat_asset_id.hex(): -1},
1400
+ DEFAULT_TX_CONFIG,
1401
+ driver_dict=driver_dict,
1402
+ )
1403
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 2
1404
+ await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, cancel_all=True)
1405
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
1406
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1407
+ await farm_transaction_block(full_node_api, wallet_node)
1408
+
1409
+ await wallet_1_rpc.create_offer_for_ids(
1410
+ {uint32(1): 5, cat_asset_id.hex(): -1},
1411
+ DEFAULT_TX_CONFIG,
1412
+ driver_dict=driver_dict,
1413
+ )
1414
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 1
1415
+ await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, asset_id=bytes32([0] * 32))
1416
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 1
1417
+ await wallet_1_rpc.cancel_offers(DEFAULT_TX_CONFIG, asset_id=cat_asset_id)
1418
+ assert len([o for o in await wallet_1_rpc.get_all_offers() if o.status == TradeStatus.PENDING_ACCEPT.value]) == 0
1419
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1420
+
1421
+ with pytest.raises(ValueError, match="not currently supported"):
1422
+ await wallet_1_rpc.create_offer_for_ids(
1423
+ {uint32(1): -5, cat_asset_id.hex(): 1},
1424
+ DEFAULT_TX_CONFIG,
1425
+ driver_dict=driver_dict,
1426
+ timelock_info=ConditionValidTimes(min_secs_since_created=uint64(1)),
1427
+ )
1428
+
1429
+
1430
+ @pytest.mark.anyio
1431
+ async def test_get_coin_records_by_names(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
1432
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1433
+ wallet_node: WalletNode = env.wallet_1.node
1434
+ client: WalletRpcClient = env.wallet_1.rpc_client
1435
+ store = wallet_node.wallet_state_manager.coin_store
1436
+ full_node_api = env.full_node.api
1437
+ # Generate some funds
1438
+ generated_funds = await generate_funds(full_node_api, env.wallet_1, 5)
1439
+ address = encode_puzzle_hash(await env.wallet_1.wallet.get_new_puzzlehash(), "txch")
1440
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
1441
+
1442
+ # Spend half of it back to the same wallet get some spent coins in the wallet
1443
+ tx = (await client.send_transaction(1, uint64(generated_funds / 2), address, DEFAULT_TX_CONFIG)).transaction
1444
+ assert tx.spend_bundle is not None
1445
+ await time_out_assert(20, tx_in_mempool, True, client, tx.name)
1446
+ await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
1447
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=5)
1448
+ # Prepare some records and parameters first
1449
+ result = await store.get_coin_records()
1450
+ coins = {record.coin for record in result.records}
1451
+ coins_unspent = {record.coin for record in result.records if not record.spent}
1452
+ coin_ids = [coin.name() for coin in coins]
1453
+ coin_ids_unspent = [coin.name() for coin in coins_unspent]
1454
+ assert len(coin_ids) > 0
1455
+ assert len(coin_ids_unspent) > 0
1456
+ # Do some queries to trigger all parameters
1457
+ # 1. Empty coin_ids
1458
+ assert await client.get_coin_records_by_names([]) == []
1459
+ # 2. All coins
1460
+ rpc_result = await client.get_coin_records_by_names(coin_ids + coin_ids_unspent)
1461
+ assert {record.coin for record in rpc_result} == {*coins, *coins_unspent}
1462
+ # 3. All spent coins
1463
+ rpc_result = await client.get_coin_records_by_names(coin_ids, include_spent_coins=True)
1464
+ assert {record.coin for record in rpc_result} == coins
1465
+ # 4. All unspent coins
1466
+ rpc_result = await client.get_coin_records_by_names(coin_ids_unspent, include_spent_coins=False)
1467
+ assert {record.coin for record in rpc_result} == coins_unspent
1468
+ # 5. Filter start/end height
1469
+ filter_records = result.records[:10]
1470
+ assert len(filter_records) == 10
1471
+ filter_coin_ids = [record.name() for record in filter_records]
1472
+ filter_coins = {record.coin for record in filter_records}
1473
+ min_height = min(record.confirmed_block_height for record in filter_records)
1474
+ max_height = max(record.confirmed_block_height for record in filter_records)
1475
+ assert min_height != max_height
1476
+ rpc_result = await client.get_coin_records_by_names(filter_coin_ids, start_height=min_height, end_height=max_height)
1477
+ assert {record.coin for record in rpc_result} == filter_coins
1478
+ # 8. Test the failure case
1479
+ with pytest.raises(ValueError, match="not found"):
1480
+ await client.get_coin_records_by_names(coin_ids, include_spent_coins=False)
1481
+
1482
+
1483
+ @pytest.mark.anyio
1484
+ async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
1485
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1486
+
1487
+ wallet_1: Wallet = env.wallet_1.wallet
1488
+ wallet_2: Wallet = env.wallet_2.wallet
1489
+ wallet_1_node: WalletNode = env.wallet_1.node
1490
+ wallet_2_node: WalletNode = env.wallet_2.node
1491
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
1492
+ wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
1493
+ full_node_api: FullNodeSimulator = env.full_node.api
1494
+ wallet_1_id = wallet_1.id()
1495
+
1496
+ await generate_funds(env.full_node.api, env.wallet_1, 5)
1497
+
1498
+ # Create a DID wallet
1499
+ res = await wallet_1_rpc.create_new_did_wallet(amount=1, tx_config=DEFAULT_TX_CONFIG, name="Profile 1")
1500
+ assert res["success"]
1501
+ did_wallet_id_0 = res["wallet_id"]
1502
+ did_id_0 = res["my_did"]
1503
+
1504
+ # Get wallet name
1505
+ res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
1506
+ assert res["success"]
1507
+ assert res["name"] == "Profile 1"
1508
+ nft_wallet: WalletProtocol = wallet_1_node.wallet_state_manager.wallets[did_wallet_id_0 + 1]
1509
+ assert isinstance(nft_wallet, NFTWallet)
1510
+ assert nft_wallet.get_name() == "Profile 1 NFT Wallet"
1511
+
1512
+ # Set wallet name
1513
+ new_wallet_name = "test name"
1514
+ res = await wallet_1_rpc.did_set_wallet_name(did_wallet_id_0, new_wallet_name)
1515
+ assert res["success"]
1516
+ res = await wallet_1_rpc.did_get_wallet_name(did_wallet_id_0)
1517
+ assert res["success"]
1518
+ assert res["name"] == new_wallet_name
1519
+ with pytest.raises(ValueError, match="wallet id 1 is of type Wallet but type DIDWallet is required"):
1520
+ await wallet_1_rpc.did_set_wallet_name(wallet_1_id, new_wallet_name)
1521
+
1522
+ # Check DID ID
1523
+ res = await wallet_1_rpc.get_did_id(did_wallet_id_0)
1524
+ assert res["success"]
1525
+ assert did_id_0 == res["my_did"]
1526
+ # Create backup file
1527
+ res = await wallet_1_rpc.create_did_backup_file(did_wallet_id_0, "backup.did")
1528
+ assert res["success"]
1529
+
1530
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1531
+ await farm_transaction_block(full_node_api, wallet_1_node)
1532
+ # Update recovery list
1533
+ update_res = await wallet_1_rpc.update_did_recovery_list(did_wallet_id_0, [did_id_0], 1, DEFAULT_TX_CONFIG)
1534
+ assert len(update_res.transactions) > 0
1535
+ res = await wallet_1_rpc.get_did_recovery_list(did_wallet_id_0)
1536
+ assert res["num_required"] == 1
1537
+ assert res["recovery_list"][0] == did_id_0
1538
+
1539
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1540
+ await farm_transaction_block(full_node_api, wallet_1_node)
1541
+
1542
+ # Update metadata
1543
+ with pytest.raises(ValueError, match="wallet id 1 is of type Wallet but type DIDWallet is required"):
1544
+ await wallet_1_rpc.update_did_metadata(wallet_1_id, {"Twitter": "Https://test"}, DEFAULT_TX_CONFIG)
1545
+ await wallet_1_rpc.update_did_metadata(did_wallet_id_0, {"Twitter": "Https://test"}, DEFAULT_TX_CONFIG)
1546
+
1547
+ res = await wallet_1_rpc.get_did_metadata(did_wallet_id_0)
1548
+ assert res["metadata"]["Twitter"] == "Https://test"
1549
+
1550
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1551
+ await farm_transaction_block(full_node_api, wallet_1_node)
1552
+
1553
+ # Transfer DID
1554
+ addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
1555
+ await wallet_1_rpc.did_transfer_did(did_wallet_id_0, addr, 0, True, DEFAULT_TX_CONFIG)
1556
+
1557
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1558
+ await farm_transaction_block(full_node_api, wallet_1_node)
1559
+
1560
+ async def num_wallets() -> int:
1561
+ return len(await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries())
1562
+
1563
+ await time_out_assert(30, num_wallets, 2)
1564
+
1565
+ did_wallets = list(
1566
+ filter(
1567
+ lambda w: (w.type == WalletType.DECENTRALIZED_ID),
1568
+ await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(),
1569
+ )
1570
+ )
1571
+ did_wallet_2: WalletProtocol = wallet_2_node.wallet_state_manager.wallets[did_wallets[0].id]
1572
+ assert isinstance(did_wallet_2, DIDWallet)
1573
+ assert (
1574
+ encode_puzzle_hash(bytes32.from_hexstr(did_wallet_2.get_my_DID()), AddressType.DID.hrp(wallet_2_node.config))
1575
+ == did_id_0
1576
+ )
1577
+ metadata = json.loads(did_wallet_2.did_info.metadata)
1578
+ assert metadata["Twitter"] == "Https://test"
1579
+
1580
+ last_did_coin = await did_wallet_2.get_coin()
1581
+ await wallet_2_rpc.did_message_spend(did_wallet_2.id(), DEFAULT_TX_CONFIG, push=True)
1582
+ await wallet_2_node.wallet_state_manager.add_interested_coin_ids([last_did_coin.name()])
1583
+
1584
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1585
+ await farm_transaction_block(full_node_api, wallet_2_node)
1586
+
1587
+ next_did_coin = await did_wallet_2.get_coin()
1588
+ assert next_did_coin.parent_coin_info == last_did_coin.name()
1589
+ last_did_coin = next_did_coin
1590
+
1591
+ await wallet_2_rpc.did_message_spend(did_wallet_2.id(), DEFAULT_TX_CONFIG.override(reuse_puzhash=True), push=True)
1592
+ await wallet_2_node.wallet_state_manager.add_interested_coin_ids([last_did_coin.name()])
1593
+
1594
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1595
+ await farm_transaction_block(full_node_api, wallet_2_node)
1596
+
1597
+ next_did_coin = await did_wallet_2.get_coin()
1598
+ assert next_did_coin.parent_coin_info == last_did_coin.name()
1599
+ assert next_did_coin.puzzle_hash == last_did_coin.puzzle_hash
1600
+
1601
+ # Test did_get_pubkey
1602
+ pubkey_res = await wallet_2_rpc.get_did_pubkey(DIDGetPubkey(did_wallet_2.id()))
1603
+ assert isinstance(pubkey_res.pubkey, G1Element)
1604
+
1605
+
1606
+ @pytest.mark.anyio
1607
+ async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
1608
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1609
+ wallet_1_node: WalletNode = env.wallet_1.node
1610
+ wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client
1611
+ wallet_2: Wallet = env.wallet_2.wallet
1612
+ wallet_2_node: WalletNode = env.wallet_2.node
1613
+ wallet_2_rpc: WalletRpcClient = env.wallet_2.rpc_client
1614
+ full_node_api: FullNodeSimulator = env.full_node.api
1615
+
1616
+ await generate_funds(env.full_node.api, env.wallet_1, 5)
1617
+
1618
+ res = await wallet_1_rpc.create_new_nft_wallet(None)
1619
+ nft_wallet_id = res["wallet_id"]
1620
+ mint_res = await wallet_1_rpc.mint_nft(
1621
+ nft_wallet_id,
1622
+ None,
1623
+ None,
1624
+ "0xD4584AD463139FA8C0D9F68F4B59F185",
1625
+ ["https://www.chia.net/img/branding/chia-logo.svg"],
1626
+ DEFAULT_TX_CONFIG,
1627
+ )
1628
+
1629
+ spend_bundle = mint_res.spend_bundle
1630
+
1631
+ await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
1632
+
1633
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=15)
1634
+
1635
+ nft_wallet: WalletProtocol = wallet_1_node.wallet_state_manager.wallets[nft_wallet_id]
1636
+ assert isinstance(nft_wallet, NFTWallet)
1637
+
1638
+ async def have_nfts():
1639
+ return await nft_wallet.get_nft_count() > 0
1640
+
1641
+ await time_out_assert(15, have_nfts, True)
1642
+
1643
+ # Test with the hex version of nft_id
1644
+ nft_id = (await nft_wallet.get_current_nfts())[0].coin.name().hex()
1645
+ nft_info = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
1646
+ assert nft_info["nft_coin_id"][2:] == (await nft_wallet.get_current_nfts())[0].coin.name().hex()
1647
+ # Test with the bech32m version of nft_id
1648
+ hmr_nft_id = encode_puzzle_hash(
1649
+ (await nft_wallet.get_current_nfts())[0].coin.name(), AddressType.NFT.hrp(wallet_1_node.config)
1650
+ )
1651
+ nft_info = (await wallet_1_rpc.get_nft_info(hmr_nft_id))["nft_info"]
1652
+ assert nft_info["nft_coin_id"][2:] == (await nft_wallet.get_current_nfts())[0].coin.name().hex()
1653
+
1654
+ addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
1655
+ await wallet_1_rpc.transfer_nft(nft_wallet_id, nft_id, addr, 0, DEFAULT_TX_CONFIG)
1656
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
1657
+ await farm_transaction_block(full_node_api, wallet_1_node)
1658
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 0)
1659
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_1_node, timeout=5)
1660
+
1661
+ await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=5)
1662
+
1663
+ nft_wallet_id_1 = (
1664
+ await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(wallet_type=WalletType.NFT)
1665
+ )[0].id
1666
+ nft_wallet_1: WalletProtocol = wallet_2_node.wallet_state_manager.wallets[nft_wallet_id_1]
1667
+ assert isinstance(nft_wallet_1, NFTWallet)
1668
+ nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id, False))["nft_info"]
1669
+ assert nft_info_1 == nft_info
1670
+ nft_info_1 = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
1671
+ assert nft_info_1["nft_coin_id"][2:] == (await nft_wallet_1.get_current_nfts())[0].coin.name().hex()
1672
+ # Cross-check NFT
1673
+ nft_info_2 = (await wallet_2_rpc.list_nfts(nft_wallet_id_1))["nft_list"][0]
1674
+ assert nft_info_1 == nft_info_2
1675
+
1676
+ # Test royalty endpoint
1677
+ royalty_summary = await wallet_1_rpc.nft_calculate_royalties(
1678
+ {
1679
+ "my asset": ("my address", uint16(10000)),
1680
+ },
1681
+ {
1682
+ None: uint64(10000),
1683
+ },
1684
+ )
1685
+ assert royalty_summary == {
1686
+ "my asset": [
1687
+ {
1688
+ "asset": None,
1689
+ "address": "my address",
1690
+ "amount": 10000,
1691
+ }
1692
+ ],
1693
+ }
1694
+
1695
+
1696
+ async def _check_delete_key(
1697
+ client: WalletRpcClient, wallet_node: WalletNode, farmer_fp: int, pool_fp: int, observer: bool = False
1698
+ ) -> None:
1699
+ # Add in reward addresses into farmer and pool for testing delete key checks
1700
+ # set farmer to first private key
1701
+ create_sk = master_sk_to_wallet_sk_unhardened if observer else master_sk_to_wallet_sk
1702
+
1703
+ sk = await wallet_node.get_key_for_fingerprint(farmer_fp, private=True)
1704
+ assert sk is not None
1705
+ farmer_ph = create_puzzlehash_for_pk(create_sk(sk, uint32(0)).get_g1())
1706
+
1707
+ sk = await wallet_node.get_key_for_fingerprint(pool_fp, private=True)
1708
+ assert sk is not None
1709
+ pool_ph = create_puzzlehash_for_pk(create_sk(sk, uint32(0)).get_g1())
1710
+
1711
+ with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
1712
+ test_config["farmer"]["xch_target_address"] = encode_puzzle_hash(farmer_ph, "txch")
1713
+ test_config["pool"]["xch_target_address"] = encode_puzzle_hash(pool_ph, "txch")
1714
+ save_config(wallet_node.root_path, "config.yaml", test_config)
1715
+
1716
+ # Check farmer_fp key
1717
+ resp = await client.check_delete_key(CheckDeleteKey(uint32(farmer_fp)))
1718
+ assert resp.fingerprint == farmer_fp
1719
+ assert resp.used_for_farmer_rewards is True
1720
+ assert resp.used_for_pool_rewards is False
1721
+
1722
+ # Check pool_fp key
1723
+ resp = await client.check_delete_key(CheckDeleteKey(uint32(pool_fp)))
1724
+ assert resp.fingerprint == pool_fp
1725
+ assert resp.used_for_farmer_rewards is False
1726
+ assert resp.used_for_pool_rewards is True
1727
+
1728
+ # Check unknown key
1729
+ resp = await client.check_delete_key(CheckDeleteKey(uint32(123456), uint16(10)))
1730
+ assert resp.fingerprint == 123456
1731
+ assert resp.used_for_farmer_rewards is False
1732
+ assert resp.used_for_pool_rewards is False
1733
+
1734
+
1735
+ @pytest.mark.anyio
1736
+ async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
1737
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1738
+
1739
+ wallet: Wallet = env.wallet_1.wallet
1740
+ wallet_node: WalletNode = env.wallet_1.node
1741
+ client: WalletRpcClient = env.wallet_1.rpc_client
1742
+
1743
+ address = await client.get_next_address(1, True)
1744
+ assert len(address) > 10
1745
+
1746
+ pks = (await client.get_public_keys()).pk_fingerprints
1747
+ assert len(pks) == 1
1748
+
1749
+ await generate_funds(env.full_node.api, env.wallet_1)
1750
+
1751
+ assert (await client.get_height_info()) > 0
1752
+
1753
+ ph = await wallet.get_new_puzzlehash()
1754
+ addr = encode_puzzle_hash(ph, "txch")
1755
+ tx_amount = uint64(15600000)
1756
+ await env.full_node.api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
1757
+ created_tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
1758
+
1759
+ await time_out_assert(20, tx_in_mempool, True, client, created_tx.name)
1760
+ assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1
1761
+ await client.delete_unconfirmed_transactions(1)
1762
+ assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 0
1763
+
1764
+ sk_resp = await client.get_private_key(GetPrivateKey(pks[0]))
1765
+ assert sk_resp.private_key.fingerprint == pks[0]
1766
+ assert sk_resp.private_key.seed is not None
1767
+
1768
+ resp = await client.generate_mnemonic()
1769
+ assert len(resp.mnemonic) == 24
1770
+
1771
+ await client.add_key(AddKey(resp.mnemonic))
1772
+
1773
+ pks = (await client.get_public_keys()).pk_fingerprints
1774
+ assert len(pks) == 2
1775
+
1776
+ await client.log_in(LogIn(pks[1]))
1777
+ sk_resp = await client.get_private_key(GetPrivateKey(pks[1]))
1778
+ assert sk_resp.private_key.fingerprint == pks[1]
1779
+
1780
+ # test hardened keys
1781
+ await _check_delete_key(client=client, wallet_node=wallet_node, farmer_fp=pks[0], pool_fp=pks[1], observer=False)
1782
+
1783
+ # test observer keys
1784
+ await _check_delete_key(client=client, wallet_node=wallet_node, farmer_fp=pks[0], pool_fp=pks[1], observer=True)
1785
+
1786
+ # set farmer to empty string
1787
+ with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
1788
+ test_config["farmer"]["xch_target_address"] = ""
1789
+ save_config(wallet_node.root_path, "config.yaml", test_config)
1790
+
1791
+ # Check key
1792
+ delete_key_resp = await client.check_delete_key(CheckDeleteKey(pks[1]))
1793
+ assert delete_key_resp.fingerprint == pks[1]
1794
+ assert delete_key_resp.used_for_farmer_rewards is False
1795
+ assert delete_key_resp.used_for_pool_rewards is True
1796
+
1797
+ # set farmer and pool to empty string
1798
+ with lock_and_load_config(wallet_node.root_path, "config.yaml") as test_config:
1799
+ test_config["farmer"]["xch_target_address"] = ""
1800
+ test_config["pool"]["xch_target_address"] = ""
1801
+ save_config(wallet_node.root_path, "config.yaml", test_config)
1802
+
1803
+ # Check key
1804
+ delete_key_resp = await client.check_delete_key(CheckDeleteKey(pks[0]))
1805
+ assert delete_key_resp.fingerprint == pks[0]
1806
+ assert delete_key_resp.used_for_farmer_rewards is False
1807
+ assert delete_key_resp.used_for_pool_rewards is False
1808
+
1809
+ await client.delete_key(DeleteKey(pks[0]))
1810
+ await client.log_in(LogIn(uint32(pks[1])))
1811
+ assert len((await client.get_public_keys()).pk_fingerprints) == 1
1812
+
1813
+ assert not (await client.get_sync_status())
1814
+
1815
+ wallets = await client.get_wallets()
1816
+ assert len(wallets) == 1
1817
+ assert await get_unconfirmed_balance(client, int(wallets[0]["id"])) == 0
1818
+
1819
+ with pytest.raises(ValueError):
1820
+ await client.send_transaction(wallets[0]["id"], uint64(100), addr, DEFAULT_TX_CONFIG)
1821
+
1822
+ # Delete all keys
1823
+ await client.delete_all_keys()
1824
+ assert len((await client.get_public_keys()).pk_fingerprints) == 0
1825
+
1826
+
1827
+ @pytest.mark.anyio
1828
+ async def test_select_coins_rpc(wallet_rpc_environment: WalletRpcTestEnvironment):
1829
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1830
+
1831
+ wallet_2: Wallet = env.wallet_2.wallet
1832
+ wallet_node: WalletNode = env.wallet_1.node
1833
+ full_node_api: FullNodeSimulator = env.full_node.api
1834
+ client: WalletRpcClient = env.wallet_1.rpc_client
1835
+ client_2: WalletRpcClient = env.wallet_2.rpc_client
1836
+
1837
+ funds = await generate_funds(full_node_api, env.wallet_1)
1838
+
1839
+ addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
1840
+ coin_300: List[Coin]
1841
+ tx_amounts: List[uint64] = [uint64(1000), uint64(300), uint64(1000), uint64(1000), uint64(10000)]
1842
+ for tx_amount in tx_amounts:
1843
+ funds -= tx_amount
1844
+ # create coins for tests
1845
+ tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
1846
+ spend_bundle = tx.spend_bundle
1847
+ assert spend_bundle is not None
1848
+ for coin in spend_bundle.additions():
1849
+ if coin.amount == uint64(300):
1850
+ coin_300 = [coin]
1851
+
1852
+ await time_out_assert(20, tx_in_mempool, True, client, tx.name)
1853
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
1854
+ await time_out_assert(20, get_confirmed_balance, funds, client, 1)
1855
+
1856
+ # test min coin amount
1857
+ min_coins: List[Coin] = await client_2.select_coins(
1858
+ amount=1000,
1859
+ wallet_id=1,
1860
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(min_coin_amount=uint64(1001)),
1861
+ )
1862
+ assert min_coins is not None
1863
+ assert len(min_coins) == 1 and min_coins[0].amount == uint64(10000)
1864
+
1865
+ # test max coin amount
1866
+ max_coins: List[Coin] = await client_2.select_coins(
1867
+ amount=2000,
1868
+ wallet_id=1,
1869
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
1870
+ min_coin_amount=uint64(999), max_coin_amount=uint64(9999)
1871
+ ),
1872
+ )
1873
+ assert max_coins is not None
1874
+ assert len(max_coins) == 2 and max_coins[0].amount == uint64(1000)
1875
+
1876
+ # test excluded coin amounts
1877
+ non_1000_amt: int = sum(a for a in tx_amounts if a != 1000)
1878
+ excluded_amt_coins: List[Coin] = await client_2.select_coins(
1879
+ amount=non_1000_amt,
1880
+ wallet_id=1,
1881
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
1882
+ )
1883
+ assert excluded_amt_coins is not None
1884
+ assert (
1885
+ len(excluded_amt_coins) == len(tuple(a for a in tx_amounts if a != 1000))
1886
+ and sum(c.amount for c in excluded_amt_coins) == non_1000_amt
1887
+ )
1888
+
1889
+ # test excluded coins
1890
+ with pytest.raises(ValueError):
1891
+ await client_2.select_coins(
1892
+ amount=5000,
1893
+ wallet_id=1,
1894
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
1895
+ excluded_coin_ids=[c.name() for c in min_coins]
1896
+ ),
1897
+ )
1898
+ excluded_test = await client_2.select_coins(
1899
+ amount=1300,
1900
+ wallet_id=1,
1901
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_ids=[c.name() for c in coin_300]),
1902
+ )
1903
+ assert len(excluded_test) == 2
1904
+ for coin in excluded_test:
1905
+ assert coin != coin_300[0]
1906
+
1907
+ # test backwards compatibility in the RPC
1908
+ identical_test = (
1909
+ await client_2.fetch(
1910
+ "select_coins",
1911
+ {
1912
+ "amount": 1300,
1913
+ "wallet_id": 1,
1914
+ "exclude_coins": [c.to_json_dict() for c in coin_300],
1915
+ },
1916
+ )
1917
+ )["coins"]
1918
+ assert len(identical_test) == 2
1919
+ for coin in identical_test:
1920
+ assert coin != coin_300[0]
1921
+
1922
+ # test get coins
1923
+ all_coins, _, _ = await client_2.get_spendable_coins(
1924
+ wallet_id=1,
1925
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(
1926
+ excluded_coin_ids=[c.name() for c in excluded_amt_coins]
1927
+ ),
1928
+ )
1929
+ assert set(excluded_amt_coins).intersection({rec.coin for rec in all_coins}) == set()
1930
+ all_coins, _, _ = await client_2.get_spendable_coins(
1931
+ wallet_id=1,
1932
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
1933
+ )
1934
+ assert len([rec for rec in all_coins if rec.coin.amount == 1000]) == 0
1935
+ all_coins_2, _, _ = await client_2.get_spendable_coins(
1936
+ wallet_id=1,
1937
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(max_coin_amount=uint64(999)),
1938
+ )
1939
+ assert all_coins_2[0].coin == coin_300[0]
1940
+ with pytest.raises(ValueError): # validate fail on invalid coin id.
1941
+ await client_2.get_spendable_coins(
1942
+ wallet_id=1,
1943
+ coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_ids=[b"a"]),
1944
+ )
1945
+
1946
+
1947
+ @pytest.mark.anyio
1948
+ async def test_get_coin_records_rpc(wallet_rpc_environment: WalletRpcTestEnvironment) -> None:
1949
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1950
+ wallet_node: WalletNode = env.wallet_1.node
1951
+ client: WalletRpcClient = env.wallet_1.rpc_client
1952
+ store = wallet_node.wallet_state_manager.coin_store
1953
+
1954
+ for record in [record_1, record_2, record_3, record_4, record_5, record_6, record_7, record_8, record_9]:
1955
+ await store.add_coin_record(record)
1956
+
1957
+ async def run_test_case(
1958
+ test_case: str,
1959
+ test_request: GetCoinRecords,
1960
+ test_total_count: Optional[int],
1961
+ test_records: List[WalletCoinRecord],
1962
+ ):
1963
+ response = await client.get_coin_records(test_request)
1964
+ assert response["coin_records"] == [coin.to_json_dict_parsed_metadata() for coin in test_records], test_case
1965
+ assert response["total_count"] == test_total_count, test_case
1966
+
1967
+ for name, tests in {
1968
+ "offset_limit": get_coin_records_offset_limit_tests,
1969
+ "wallet_id": get_coin_records_wallet_id_tests,
1970
+ "wallet_type": get_coin_records_wallet_type_tests,
1971
+ "coin_type": get_coin_records_coin_type_tests,
1972
+ "coin_id_filter": get_coin_records_coin_id_filter_tests,
1973
+ "puzzle_hash_filter": get_coin_records_puzzle_hash_filter_tests,
1974
+ "parent_coin_id_filter": get_coin_records_parent_coin_id_filter_tests,
1975
+ "amount_filter": get_coin_records_amount_filter_tests,
1976
+ "amount_range": get_coin_records_amount_range_tests,
1977
+ "confirmed_range": get_coin_records_confirmed_range_tests,
1978
+ "spent_range": get_coin_records_spent_range_tests,
1979
+ "order": get_coin_records_order_tests,
1980
+ "reverse": get_coin_records_reverse_tests,
1981
+ }.items():
1982
+ for i, (request, expected_records) in enumerate(tests):
1983
+ await run_test_case(f"{name}-{i}", request, None, expected_records)
1984
+
1985
+ for name, total_count_tests in {
1986
+ "total_count": get_coin_records_include_total_count_tests,
1987
+ "mixed": get_coin_records_mixed_tests,
1988
+ }.items():
1989
+ for i, (request, expected_total_count, expected_records) in enumerate(total_count_tests):
1990
+ await run_test_case(f"{name}-{i}", request, expected_total_count, expected_records)
1991
+
1992
+
1993
+ @pytest.mark.anyio
1994
+ async def test_get_coin_records_rpc_limits(
1995
+ wallet_rpc_environment: WalletRpcTestEnvironment,
1996
+ seeded_random: random.Random,
1997
+ ) -> None:
1998
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
1999
+ wallet_node: WalletNode = env.wallet_1.node
2000
+ client: WalletRpcClient = env.wallet_1.rpc_client
2001
+ rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
2002
+ assert rpc_server is not None
2003
+ api: WalletRpcApi = cast(WalletRpcApi, rpc_server.rpc_api)
2004
+ store = wallet_node.wallet_state_manager.coin_store
2005
+
2006
+ # Adjust the limits for faster testing
2007
+ WalletRpcApi.max_get_coin_records_limit = uint32(5)
2008
+ WalletRpcApi.max_get_coin_records_filter_items = uint32(5)
2009
+
2010
+ max_coins = api.max_get_coin_records_limit * 10
2011
+ coin_records = [
2012
+ WalletCoinRecord(
2013
+ Coin(bytes32.random(seeded_random), bytes32.random(seeded_random), uint64(seeded_random.randrange(2**64))),
2014
+ uint32(seeded_random.randrange(2**32)),
2015
+ uint32(0),
2016
+ False,
2017
+ False,
2018
+ WalletType.STANDARD_WALLET,
2019
+ uint32(0),
2020
+ CoinType.NORMAL,
2021
+ None,
2022
+ )
2023
+ for _ in range(max_coins)
2024
+ ]
2025
+ for record in coin_records:
2026
+ await store.add_coin_record(record)
2027
+
2028
+ limit = api.max_get_coin_records_limit
2029
+ response_records = []
2030
+ for i in range(int(max_coins / api.max_get_coin_records_limit)):
2031
+ offset = uint32(api.max_get_coin_records_limit * i)
2032
+ response = await client.get_coin_records(GetCoinRecords(limit=limit, offset=offset, include_total_count=True))
2033
+ response_records.extend(list(response["coin_records"]))
2034
+
2035
+ assert len(response_records) == max_coins
2036
+ # Make sure we got all expected records
2037
+ parsed_records = [coin.to_json_dict_parsed_metadata() for coin in coin_records]
2038
+ for expected_record in parsed_records:
2039
+ assert expected_record in response_records
2040
+
2041
+ # Request coins with the max number of filter items
2042
+ max_filter_items = api.max_get_coin_records_filter_items
2043
+ filter_records = coin_records[:max_filter_items]
2044
+ coin_id_filter = HashFilter.include([coin.name() for coin in filter_records])
2045
+ puzzle_hash_filter = HashFilter.include([coin.coin.puzzle_hash for coin in filter_records])
2046
+ parent_coin_id_filter = HashFilter.include([coin.coin.parent_coin_info for coin in filter_records])
2047
+ amount_filter = AmountFilter.include([uint64(coin.coin.amount) for coin in coin_records[:max_filter_items]])
2048
+ for request in [
2049
+ GetCoinRecords(coin_id_filter=coin_id_filter),
2050
+ GetCoinRecords(puzzle_hash_filter=puzzle_hash_filter),
2051
+ GetCoinRecords(parent_coin_id_filter=parent_coin_id_filter),
2052
+ GetCoinRecords(amount_filter=amount_filter),
2053
+ GetCoinRecords(
2054
+ coin_id_filter=coin_id_filter,
2055
+ puzzle_hash_filter=puzzle_hash_filter,
2056
+ parent_coin_id_filter=parent_coin_id_filter,
2057
+ amount_filter=amount_filter,
2058
+ ),
2059
+ ]:
2060
+ response = await client.get_coin_records(request)
2061
+ parsed_records = [coin.to_json_dict_parsed_metadata() for coin in filter_records]
2062
+ for expected_record in parsed_records:
2063
+ assert expected_record in response["coin_records"]
2064
+
2065
+
2066
+ @pytest.mark.anyio
2067
+ async def test_get_coin_records_rpc_failures(
2068
+ wallet_rpc_environment: WalletRpcTestEnvironment,
2069
+ seeded_random: random.Random,
2070
+ ) -> None:
2071
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2072
+ client: WalletRpcClient = env.wallet_1.rpc_client
2073
+ rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
2074
+ assert rpc_server is not None
2075
+ api = cast(WalletRpcApi, rpc_server.rpc_api)
2076
+
2077
+ too_many_hashes = [bytes32.random(seeded_random) for _ in range(api.max_get_coin_records_filter_items + 1)]
2078
+ too_many_amounts = [
2079
+ uint64(uint64(seeded_random.randrange(2**64))) for _ in range(api.max_get_coin_records_filter_items + 1)
2080
+ ]
2081
+ # Run requests which exceeds the allowed limit and contain too much filter items
2082
+ for name, request in {
2083
+ "limit": GetCoinRecords(limit=uint32(api.max_get_coin_records_limit + 1)),
2084
+ "coin_id_filter": GetCoinRecords(coin_id_filter=HashFilter.include(too_many_hashes)),
2085
+ "puzzle_hash_filter": GetCoinRecords(puzzle_hash_filter=HashFilter.include(too_many_hashes)),
2086
+ "parent_coin_id_filter": GetCoinRecords(parent_coin_id_filter=HashFilter.include(too_many_hashes)),
2087
+ "amount_filter": GetCoinRecords(amount_filter=AmountFilter.include(too_many_amounts)),
2088
+ }.items():
2089
+ with pytest.raises(ValueError, match=name):
2090
+ await client.get_coin_records(request)
2091
+
2092
+ # Type validation is handled via `Streamable.from_json_dict´ but the below should make at least sure it triggers.
2093
+ for field, value in {
2094
+ "offset": "invalid",
2095
+ "limit": "invalid",
2096
+ "wallet_id": "invalid",
2097
+ "wallet_type": 100,
2098
+ "coin_type": 100,
2099
+ "coin_id_filter": "invalid",
2100
+ "puzzle_hash_filter": "invalid",
2101
+ "parent_coin_id_filter": "invalid",
2102
+ "amount_filter": "invalid",
2103
+ "amount_range": "invalid",
2104
+ "confirmed_range": "invalid",
2105
+ "spent_range": "invalid",
2106
+ "order": 8,
2107
+ }.items():
2108
+ with pytest.raises((ConversionError, InvalidTypeError, ValueError)):
2109
+ json_dict = GetCoinRecords().to_json_dict()
2110
+ json_dict[field] = value
2111
+ await api.get_coin_records(json_dict)
2112
+
2113
+
2114
+ @pytest.mark.anyio
2115
+ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironment):
2116
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2117
+
2118
+ wallet_2: Wallet = env.wallet_2.wallet
2119
+ wallet_node: WalletNode = env.wallet_1.node
2120
+ full_node_api: FullNodeSimulator = env.full_node.api
2121
+ client: WalletRpcClient = env.wallet_1.rpc_client
2122
+ client_2: WalletRpcClient = env.wallet_2.rpc_client
2123
+
2124
+ await generate_funds(full_node_api, env.wallet_1)
2125
+
2126
+ env.wallet_2.node.config["enable_notifications"] = True
2127
+ env.wallet_2.node.config["required_notification_amount"] = 100000000000
2128
+ tx = await client.send_notification(
2129
+ await wallet_2.get_new_puzzlehash(),
2130
+ b"hello",
2131
+ uint64(100000000000),
2132
+ fee=uint64(100000000000),
2133
+ )
2134
+
2135
+ assert tx.spend_bundle is not None
2136
+ await time_out_assert(
2137
+ 5,
2138
+ full_node_api.full_node.mempool_manager.get_spendbundle,
2139
+ tx.spend_bundle,
2140
+ tx.spend_bundle.name(),
2141
+ )
2142
+ await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
2143
+ await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(100000000000))
2144
+
2145
+ notification = (await client_2.get_notifications(GetNotifications())).notifications[0]
2146
+ assert [notification] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
2147
+ assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(0), uint32(0)))).notifications
2148
+ assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, uint32(1)))).notifications
2149
+ assert [] == (await client_2.get_notifications(GetNotifications(None, uint32(1), None))).notifications
2150
+ assert [notification] == (await client_2.get_notifications(GetNotifications(None, None, None))).notifications
2151
+ assert await client_2.delete_notifications()
2152
+ assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
2153
+
2154
+ tx = await client.send_notification(
2155
+ await wallet_2.get_new_puzzlehash(),
2156
+ b"hello",
2157
+ uint64(100000000000),
2158
+ fee=uint64(100000000000),
2159
+ )
2160
+
2161
+ assert tx.spend_bundle is not None
2162
+ await time_out_assert(
2163
+ 5,
2164
+ full_node_api.full_node.mempool_manager.get_spendbundle,
2165
+ tx.spend_bundle,
2166
+ tx.spend_bundle.name(),
2167
+ )
2168
+ await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
2169
+ await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(200000000000))
2170
+
2171
+ notification = (await client_2.get_notifications(GetNotifications())).notifications[0]
2172
+ assert await client_2.delete_notifications([notification.id])
2173
+ assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications
2174
+
2175
+
2176
+ # The signatures below were made from an ephemeral key pair that isn't included in the test code.
2177
+ # When modifying this test, any key can be used to generate signatures. Only the pubkey needs to
2178
+ # be included in the test code.
2179
+ #
2180
+ # Example 1:
2181
+ # $ chia keys generate
2182
+ # $ chia keys sign -d 'hello world' -t 'm/12381/8444/1/1'
2183
+ #
2184
+ # Example 2:
2185
+ # $ chia wallet get_address
2186
+ # xch1vk0dj7cx7d638h80mcuw70xqlnr56pmuhzajemn5ym02vhl3mzyqrrd4wp
2187
+ # $ chia wallet sign_message -m $(echo -n 'hello world' | xxd -p)
2188
+ # -a xch1vk0dj7cx7d638h80mcuw70xqlnr56pmuhzajemn5ym02vhl3mzyqrrd4wp
2189
+ #
2190
+ @pytest.mark.parametrize(
2191
+ ["rpc_request", "rpc_response"],
2192
+ [
2193
+ # Valid signatures
2194
+ (
2195
+ # chia keys sign -d "Let's eat, Grandma" -t "m/12381/8444/1/1"
2196
+ {
2197
+ "message": "4c65742773206561742c204772616e646d61", # Let's eat, Grandma
2198
+ "pubkey": (
2199
+ "89d8e2a225c2ff543222bd0f2ba457a44acbdd147e4dfa02"
2200
+ "eadaef73eae49450dc708fd7c86800b60e8bc456e77563e4"
2201
+ ),
2202
+ "signature": (
2203
+ "8006f63537563f038321eeda25f3838613d8f938e95f19d1d19ccbe634e9ee4d69552536aab08b4fe961305"
2204
+ "e534ffddf096199ae936b272dac88c936e8774bfc7a6f24025085026db3b7c3c41b472db3daf99b5e6cabf2"
2205
+ "6034d8782d10ef148d"
2206
+ ),
2207
+ },
2208
+ VerifySignatureResponse(isValid=True),
2209
+ ),
2210
+ (
2211
+ # chia wallet sign_message -m $(echo -n 'Happy happy joy joy' | xxd -p)
2212
+ # -a xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh
2213
+ {
2214
+ "message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
2215
+ "pubkey": (
2216
+ "8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
2217
+ "03a1df2dd477d622be928379625b759ef3b388b286ee8658"
2218
+ ),
2219
+ "signature": (
2220
+ "a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
2221
+ "5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
2222
+ "91246eb1c7f1b66a6a"
2223
+ ),
2224
+ "signing_mode": SigningMode.CHIP_0002.value,
2225
+ },
2226
+ VerifySignatureResponse(isValid=True),
2227
+ ),
2228
+ (
2229
+ # chia wallet sign_message -m $(echo -n 'Happy happy joy joy' | xxd -p)
2230
+ # -a xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh
2231
+ {
2232
+ "message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
2233
+ "pubkey": (
2234
+ "8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
2235
+ "03a1df2dd477d622be928379625b759ef3b388b286ee8658"
2236
+ ),
2237
+ "signature": (
2238
+ "a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
2239
+ "5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
2240
+ "91246eb1c7f1b66a6a"
2241
+ ),
2242
+ "signing_mode": SigningMode.CHIP_0002.value,
2243
+ "address": "xch1e2pcue5q7t4sg8gygz3aht369sk78rzzs92zx65ktn9a9qurw35saajvkh",
2244
+ },
2245
+ VerifySignatureResponse(isValid=True),
2246
+ ),
2247
+ (
2248
+ {
2249
+ "message": "4f7a6f6e65", # Ozone
2250
+ "pubkey": (
2251
+ "8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
2252
+ "24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
2253
+ ),
2254
+ "signature": (
2255
+ "92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
2256
+ "c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
2257
+ "334981b974a6cebfd0"
2258
+ ),
2259
+ "signing_mode": SigningMode.CHIP_0002_P2_DELEGATED_CONDITIONS.value,
2260
+ "address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
2261
+ },
2262
+ VerifySignatureResponse(isValid=True),
2263
+ ),
2264
+ # Negative tests
2265
+ (
2266
+ # Message was modified
2267
+ {
2268
+ "message": "4c6574277320656174204772616e646d61", # Let's eat Grandma
2269
+ "pubkey": (
2270
+ "89d8e2a225c2ff543222bd0f2ba457a44acbdd147e4dfa02"
2271
+ "eadaef73eae49450dc708fd7c86800b60e8bc456e77563e4"
2272
+ ),
2273
+ "signature": (
2274
+ "8006f63537563f038321eeda25f3838613d8f938e95f19d1d19ccbe634e9ee4d69552536aab08b4fe961305"
2275
+ "e534ffddf096199ae936b272dac88c936e8774bfc7a6f24025085026db3b7c3c41b472db3daf99b5e6cabf2"
2276
+ "6034d8782d10ef148d"
2277
+ ),
2278
+ },
2279
+ VerifySignatureResponse(isValid=False, error="Signature is invalid."),
2280
+ ),
2281
+ (
2282
+ # Valid signature but address doesn't match pubkey
2283
+ {
2284
+ "message": "4861707079206861707079206a6f79206a6f79", # Happy happy joy joy
2285
+ "pubkey": (
2286
+ "8e156d106f1b0ff0ebbe5ab27b1797a19cf3e895a7a435b0"
2287
+ "03a1df2dd477d622be928379625b759ef3b388b286ee8658"
2288
+ ),
2289
+ "signature": (
2290
+ "a804111f80be2ed0d4d3fdd139c8fe20cd506b99b03592563d85292abcbb9cd6ff6df2e7a13093e330d66aa"
2291
+ "5218bbe0e17677c9a23a9f18dbe488b7026be59d476161f5e6f0eea109cd7be22b1f74fda9c80c6b845ecc6"
2292
+ "91246eb1c7f1b66a6a"
2293
+ ),
2294
+ "signing_mode": SigningMode.CHIP_0002.value,
2295
+ "address": "xch1d0rekc2javy5gpruzmcnk4e4qq834jzlvxt5tcgl2ylt49t26gdsjen7t0",
2296
+ },
2297
+ VerifySignatureResponse(isValid=False, error="Public key doesn't match the address"),
2298
+ ),
2299
+ (
2300
+ {
2301
+ "message": "4f7a6f6e65", # Ozone
2302
+ "pubkey": (
2303
+ "8fba5482e6c798a06ee1fd95deaaa83f11c46da06006ab35"
2304
+ "24e917f4e116c2bdec69d6098043ca568290ac366e5e2dc5"
2305
+ ),
2306
+ "signature": (
2307
+ "92a5124d53b74e4197d075277d0b31eda1571353415c4a87952035aa392d4e9206b35e4af959e7135e45db1"
2308
+ "c884b8b970f9cbffd42291edc1acdb124554f04608b8d842c19e1404d306f881fa79c0e287bdfcf36a6e5da"
2309
+ "334981b974a6cebfd0"
2310
+ ),
2311
+ "address": "xch1hh9phcc8tt703dla70qthlhrxswy88va04zvc7vd8cx2v6a5ywyst8mgul",
2312
+ },
2313
+ VerifySignatureResponse(isValid=False, error="Public key doesn't match the address"),
2314
+ ),
2315
+ ],
2316
+ )
2317
+ @pytest.mark.parametrize("prefix_hex_strings", [True, False], ids=["with 0x", "no 0x"])
2318
+ @pytest.mark.anyio
2319
+ @pytest.mark.limit_consensus_modes(reason="irrelevant")
2320
+ async def test_verify_signature(
2321
+ wallet_rpc_environment: WalletRpcTestEnvironment,
2322
+ rpc_request: Dict[str, Any],
2323
+ rpc_response: Dict[str, Any],
2324
+ prefix_hex_strings: bool,
2325
+ ):
2326
+ rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
2327
+ assert rpc_server is not None
2328
+ updated_request = rpc_request.copy()
2329
+ updated_request["pubkey"] = ("0x" if prefix_hex_strings else "") + updated_request["pubkey"]
2330
+ updated_request["signature"] = ("0x" if prefix_hex_strings else "") + updated_request["signature"]
2331
+ res = await wallet_rpc_environment.wallet_1.rpc_client.verify_signature(
2332
+ VerifySignature.from_json_dict(updated_request)
2333
+ )
2334
+ assert res == rpc_response
2335
+
2336
+
2337
+ @pytest.mark.anyio
2338
+ @pytest.mark.limit_consensus_modes(reason="irrelevant")
2339
+ async def test_set_auto_claim(wallet_rpc_environment: WalletRpcTestEnvironment):
2340
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2341
+ full_node_api: FullNodeSimulator = env.full_node.api
2342
+ rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
2343
+ await generate_funds(full_node_api, env.wallet_1)
2344
+ assert rpc_server is not None
2345
+ api: WalletRpcApi = cast(WalletRpcApi, rpc_server.rpc_api)
2346
+ req = {"enabled": False, "tx_fee": -1, "min_amount": 100}
2347
+ has_exception = False
2348
+ try:
2349
+ # Manually using API to test error condition
2350
+ await api.set_auto_claim(req)
2351
+ except ConversionError:
2352
+ has_exception = True
2353
+ assert has_exception
2354
+ req = {"enabled": False, "batch_size": 0, "min_amount": 100}
2355
+ res = await env.wallet_1.rpc_client.set_auto_claim(
2356
+ AutoClaimSettings(enabled=False, batch_size=uint16(0), min_amount=uint64(100))
2357
+ )
2358
+ assert not res.enabled
2359
+ assert res.tx_fee == 0
2360
+ assert res.min_amount == 100
2361
+ assert res.batch_size == 50
2362
+
2363
+
2364
+ @pytest.mark.anyio
2365
+ @pytest.mark.limit_consensus_modes(reason="irrelevant")
2366
+ async def test_get_auto_claim(wallet_rpc_environment: WalletRpcTestEnvironment):
2367
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2368
+ full_node_api: FullNodeSimulator = env.full_node.api
2369
+ rpc_server: Optional[RpcServer] = wallet_rpc_environment.wallet_1.service.rpc_server
2370
+ await generate_funds(full_node_api, env.wallet_1)
2371
+ assert rpc_server is not None
2372
+ res = await env.wallet_1.rpc_client.get_auto_claim()
2373
+ assert not res.enabled
2374
+ assert res.tx_fee == 0
2375
+ assert res.min_amount == 0
2376
+ assert res.batch_size == 50
2377
+
2378
+
2379
+ @pytest.mark.anyio
2380
+ async def test_set_wallet_resync_on_startup(wallet_rpc_environment: WalletRpcTestEnvironment):
2381
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2382
+ full_node_api: FullNodeSimulator = env.full_node.api
2383
+ client: WalletRpcClient = env.wallet_1.rpc_client
2384
+ await generate_funds(full_node_api, env.wallet_1)
2385
+ wc = env.wallet_1.rpc_client
2386
+ await wc.create_new_did_wallet(1, DEFAULT_TX_CONFIG, 0)
2387
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
2388
+ await farm_transaction_block(full_node_api, env.wallet_1.node)
2389
+ await time_out_assert(20, wc.get_synced)
2390
+
2391
+ nft_wallet = await wc.create_new_nft_wallet(None)
2392
+ nft_wallet_id = nft_wallet["wallet_id"]
2393
+ address = await wc.get_next_address(env.wallet_1.wallet.id(), True)
2394
+ await wc.mint_nft(
2395
+ nft_wallet_id,
2396
+ tx_config=DEFAULT_TX_CONFIG,
2397
+ royalty_address=address,
2398
+ target_address=address,
2399
+ hash="deadbeef",
2400
+ uris=["http://test.nft"],
2401
+ )
2402
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1)
2403
+ await farm_transaction_block(full_node_api, env.wallet_1.node)
2404
+ await time_out_assert(20, wc.get_synced)
2405
+
2406
+ wallet_node: WalletNode = env.wallet_1.node
2407
+ wallet_node_2: WalletNode = env.wallet_2.node
2408
+ # Test Clawback resync
2409
+ tx = (
2410
+ await wc.send_transaction(
2411
+ wallet_id=1,
2412
+ amount=uint64(500),
2413
+ address=address,
2414
+ tx_config=DEFAULT_TX_CONFIG,
2415
+ fee=uint64(0),
2416
+ puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
2417
+ )
2418
+ ).transaction
2419
+ clawback_coin_id = tx.additions[0].name()
2420
+ assert tx.spend_bundle is not None
2421
+ await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
2422
+ await time_out_assert(20, wc.get_synced)
2423
+ await asyncio.sleep(10)
2424
+ resp = await wc.spend_clawback_coins([clawback_coin_id], 0)
2425
+ assert resp["success"]
2426
+ assert len(resp["transaction_ids"]) == 1
2427
+ await time_out_assert_not_none(
2428
+ 10, full_node_api.full_node.mempool_manager.get_spendbundle, bytes32.from_hexstr(resp["transaction_ids"][0])
2429
+ )
2430
+ await farm_transaction_block(full_node_api, wallet_node)
2431
+ await time_out_assert(20, wc.get_synced)
2432
+ wallet_node_2._close()
2433
+ await wallet_node_2._await_closed()
2434
+ # set flag to reset wallet sync data on start
2435
+ await client.set_wallet_resync_on_startup()
2436
+ fingerprint = wallet_node.logged_in_fingerprint
2437
+ assert wallet_node._wallet_state_manager
2438
+ # 2 reward coins, 1 DID, 1 NFT, 1 clawbacked coin
2439
+ assert len(await wallet_node._wallet_state_manager.coin_store.get_all_unspent_coins()) == 5
2440
+ assert await wallet_node._wallet_state_manager.nft_store.count() == 1
2441
+ # standard wallet, did wallet, nft wallet, did nft wallet
2442
+ assert len(await wallet_node.wallet_state_manager.user_store.get_all_wallet_info_entries()) == 4
2443
+ before_txs = await wallet_node.wallet_state_manager.tx_store.get_all_transactions()
2444
+ wallet_node._close()
2445
+ await wallet_node._await_closed()
2446
+ config = load_config(wallet_node.root_path, "config.yaml")
2447
+ # check that flag was set in config file
2448
+ assert config["wallet"]["reset_sync_for_fingerprint"] == fingerprint
2449
+ new_config = wallet_node.config.copy()
2450
+ new_config["reset_sync_for_fingerprint"] = config["wallet"]["reset_sync_for_fingerprint"]
2451
+ wallet_node_2.config = new_config
2452
+ wallet_node_2.root_path = wallet_node.root_path
2453
+ wallet_node_2.local_keychain = wallet_node.local_keychain
2454
+ # use second node to start the same wallet, reusing config and db
2455
+ await wallet_node_2._start_with_fingerprint(fingerprint)
2456
+ assert wallet_node_2._wallet_state_manager
2457
+ after_txs = await wallet_node_2.wallet_state_manager.tx_store.get_all_transactions()
2458
+ # transactions should be the same
2459
+ assert after_txs == before_txs
2460
+ # Check clawback
2461
+ clawback_tx = await wallet_node_2.wallet_state_manager.tx_store.get_transaction_record(clawback_coin_id)
2462
+ assert clawback_tx is not None
2463
+ assert clawback_tx.confirmed
2464
+ # only coin_store was populated in this case, but now should be empty
2465
+ assert len(await wallet_node_2._wallet_state_manager.coin_store.get_all_unspent_coins()) == 0
2466
+ assert await wallet_node_2._wallet_state_manager.nft_store.count() == 0
2467
+ # we don't delete wallets
2468
+ assert len(await wallet_node_2.wallet_state_manager.user_store.get_all_wallet_info_entries()) == 4
2469
+ updated_config = load_config(wallet_node.root_path, "config.yaml")
2470
+ # check that it's disabled after reset
2471
+ assert updated_config["wallet"].get("reset_sync_for_fingerprint") is None
2472
+ wallet_node_2._close()
2473
+ await wallet_node_2._await_closed()
2474
+
2475
+
2476
+ @pytest.mark.anyio
2477
+ async def test_set_wallet_resync_on_startup_disable(wallet_rpc_environment: WalletRpcTestEnvironment):
2478
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2479
+ full_node_api: FullNodeSimulator = env.full_node.api
2480
+ client: WalletRpcClient = env.wallet_1.rpc_client
2481
+ await generate_funds(full_node_api, env.wallet_1)
2482
+ wallet_node: WalletNode = env.wallet_1.node
2483
+ wallet_node_2: WalletNode = env.wallet_2.node
2484
+ wallet_node_2._close()
2485
+ await wallet_node_2._await_closed()
2486
+ # set flag to reset wallet sync data on start
2487
+ await client.set_wallet_resync_on_startup()
2488
+ fingerprint = wallet_node.logged_in_fingerprint
2489
+ assert wallet_node._wallet_state_manager
2490
+ assert len(await wallet_node._wallet_state_manager.coin_store.get_all_unspent_coins()) == 2
2491
+ before_txs = await wallet_node.wallet_state_manager.tx_store.get_all_transactions()
2492
+ await client.set_wallet_resync_on_startup(False)
2493
+ wallet_node._close()
2494
+ await wallet_node._await_closed()
2495
+ config = load_config(wallet_node.root_path, "config.yaml")
2496
+ # check that flag was set in config file
2497
+ assert config["wallet"].get("reset_sync_for_fingerprint") is None
2498
+ new_config = wallet_node.config.copy()
2499
+ new_config["reset_sync_for_fingerprint"] = config["wallet"].get("reset_sync_for_fingerprint")
2500
+ wallet_node_2.config = new_config
2501
+ wallet_node_2.root_path = wallet_node.root_path
2502
+ wallet_node_2.local_keychain = wallet_node.local_keychain
2503
+ # use second node to start the same wallet, reusing config and db
2504
+ await wallet_node_2._start_with_fingerprint(fingerprint)
2505
+ assert wallet_node_2._wallet_state_manager
2506
+ after_txs = await wallet_node_2.wallet_state_manager.tx_store.get_all_transactions()
2507
+ # transactions should be the same
2508
+ assert after_txs == before_txs
2509
+ # only coin_store was populated in this case, but now should be empty
2510
+ assert len(await wallet_node_2._wallet_state_manager.coin_store.get_all_unspent_coins()) == 2
2511
+ wallet_node_2._close()
2512
+ await wallet_node_2._await_closed()
2513
+
2514
+
2515
+ @pytest.mark.anyio
2516
+ @pytest.mark.limit_consensus_modes(reason="irrelevant")
2517
+ async def test_set_wallet_resync_schema(wallet_rpc_environment: WalletRpcTestEnvironment):
2518
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2519
+ full_node_api: FullNodeSimulator = env.full_node.api
2520
+ await generate_funds(full_node_api, env.wallet_1)
2521
+ wallet_node: WalletNode = env.wallet_1.node
2522
+ fingerprint = wallet_node.logged_in_fingerprint
2523
+ assert fingerprint
2524
+ db_path = wallet_node.wallet_state_manager.db_path
2525
+ assert await wallet_node.reset_sync_db(
2526
+ db_path, fingerprint
2527
+ ), "Schema has been changed, reset sync db won't work, please update WalletNode.reset_sync_db function"
2528
+ dbw: DBWrapper2 = wallet_node.wallet_state_manager.db_wrapper
2529
+ conn: aiosqlite.Connection
2530
+ async with dbw.writer() as conn:
2531
+ await conn.execute("CREATE TABLE blah(temp int)")
2532
+ await wallet_node.reset_sync_db(db_path, fingerprint)
2533
+ assert (
2534
+ len(list(await conn.execute_fetchall("SELECT name FROM sqlite_master WHERE type='table' AND name='blah'"))) == 0
2535
+ )
2536
+
2537
+
2538
+ @pytest.mark.anyio
2539
+ async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironment):
2540
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2541
+
2542
+ wallet_node: WalletNode = env.wallet_1.node
2543
+ client: WalletRpcClient = env.wallet_1.rpc_client
2544
+ full_node_api: FullNodeSimulator = env.full_node.api
2545
+ full_node_rpc: FullNodeRpcClient = env.full_node.rpc_client
2546
+
2547
+ await generate_funds(full_node_api, env.wallet_1, 1)
2548
+
2549
+ # Send to a CAT with an anyone can spend TAIL
2550
+ our_ph: bytes32 = await env.wallet_1.wallet.get_new_puzzlehash()
2551
+ cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), Program.to(1))
2552
+ addr = encode_puzzle_hash(
2553
+ cat_puzzle.get_tree_hash(),
2554
+ "txch",
2555
+ )
2556
+ tx_amount = uint64(100)
2557
+
2558
+ tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
2559
+ transaction_id = tx.name
2560
+ spend_bundle = tx.spend_bundle
2561
+ assert spend_bundle is not None
2562
+
2563
+ await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
2564
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
2565
+
2566
+ # Do the eve spend back to our wallet
2567
+ cat_coin = next(c for c in spend_bundle.additions() if c.amount == tx_amount)
2568
+ eve_spend = WalletSpendBundle(
2569
+ [
2570
+ make_spend(
2571
+ cat_coin,
2572
+ cat_puzzle,
2573
+ Program.to(
2574
+ [
2575
+ Program.to([[51, our_ph, tx_amount, [our_ph]], [51, None, -113, None, None]]),
2576
+ None,
2577
+ cat_coin.name(),
2578
+ coin_as_list(cat_coin),
2579
+ [cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_coin.amount],
2580
+ 0,
2581
+ 0,
2582
+ ]
2583
+ ),
2584
+ )
2585
+ ],
2586
+ G2Element(),
2587
+ )
2588
+ await full_node_rpc.push_tx(eve_spend)
2589
+ await farm_transaction(full_node_api, wallet_node, eve_spend)
2590
+
2591
+ # Make sure we have the CAT
2592
+ res = await client.create_wallet_for_existing_cat(Program.to(None).get_tree_hash())
2593
+ assert res["success"]
2594
+ cat_wallet_id = res["wallet_id"]
2595
+ await time_out_assert(20, get_confirmed_balance, tx_amount, client, cat_wallet_id)
2596
+
2597
+ # Attempt to melt it fully
2598
+ tx = (
2599
+ await client.cat_spend(
2600
+ cat_wallet_id,
2601
+ amount=uint64(0),
2602
+ tx_config=DEFAULT_TX_CONFIG,
2603
+ inner_address=encode_puzzle_hash(our_ph, "txch"),
2604
+ cat_discrepancy=(tx_amount * -1, Program.to(None), Program.to(None)),
2605
+ )
2606
+ ).transaction
2607
+ transaction_id = tx.name
2608
+ spend_bundle = tx.spend_bundle
2609
+ assert spend_bundle is not None
2610
+
2611
+ await time_out_assert(20, tx_in_mempool, True, client, transaction_id)
2612
+ await farm_transaction(full_node_api, wallet_node, spend_bundle)
2613
+
2614
+ await time_out_assert(20, get_confirmed_balance, 0, client, cat_wallet_id)
2615
+
2616
+
2617
+ @pytest.mark.anyio
2618
+ async def test_get_balances(wallet_rpc_environment: WalletRpcTestEnvironment):
2619
+ env: WalletRpcTestEnvironment = wallet_rpc_environment
2620
+
2621
+ client: WalletRpcClient = env.wallet_1.rpc_client
2622
+ wallet_node: WalletNode = env.wallet_1.node
2623
+
2624
+ full_node_api: FullNodeSimulator = env.full_node.api
2625
+
2626
+ await generate_funds(full_node_api, env.wallet_1, 1)
2627
+
2628
+ await time_out_assert(20, client.get_synced)
2629
+ # Creates a CAT wallet with 100 mojos and a CAT with 20 mojos
2630
+ await client.create_new_cat_and_wallet(uint64(100), test=True)
2631
+
2632
+ await time_out_assert(20, client.get_synced)
2633
+ res = await client.create_new_cat_and_wallet(uint64(20), test=True)
2634
+ assert res["success"]
2635
+ await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 2)
2636
+ await farm_transaction_block(full_node_api, wallet_node)
2637
+ await time_out_assert(20, client.get_synced)
2638
+ bal = await client.get_wallet_balances()
2639
+ assert len(bal) == 3
2640
+ assert bal["1"]["confirmed_wallet_balance"] == 1999999999880
2641
+ assert bal["2"]["confirmed_wallet_balance"] == 100
2642
+ assert bal["3"]["confirmed_wallet_balance"] == 20
2643
+ bal_ids = await client.get_wallet_balances([3, 2])
2644
+ assert len(bal_ids) == 2
2645
+ assert bal["2"]["confirmed_wallet_balance"] == 100
2646
+ assert bal["3"]["confirmed_wallet_balance"] == 20
2647
+
2648
+
2649
+ @pytest.mark.parametrize(
2650
+ "wallet_environments",
2651
+ [
2652
+ {
2653
+ "num_environments": 1,
2654
+ "blocks_needed": [1],
2655
+ }
2656
+ ],
2657
+ indirect=True,
2658
+ )
2659
+ @pytest.mark.limit_consensus_modes([ConsensusMode.PLAIN], reason="irrelevant")
2660
+ @pytest.mark.anyio
2661
+ async def test_split_coins(wallet_environments: WalletTestFramework) -> None:
2662
+ env = wallet_environments.environments[0]
2663
+ env.wallet_aliases = {
2664
+ "xch": 1,
2665
+ "cat": 2,
2666
+ }
2667
+
2668
+ # Test XCH first
2669
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
2670
+ target_coin = list(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope))[0]
2671
+ assert target_coin.amount == 250_000_000_000
2672
+
2673
+ xch_request = SplitCoins(
2674
+ wallet_id=uint32(1),
2675
+ number_of_coins=uint16(100),
2676
+ amount_per_coin=uint64(100),
2677
+ target_coin_id=target_coin.name(),
2678
+ fee=uint64(1_000_000_000_000), # 1 XCH
2679
+ push=True,
2680
+ )
2681
+
2682
+ with pytest.raises(ResponseFailureError, match="501 coins is greater then the maximum limit of 500 coins"):
2683
+ await env.rpc_client.split_coins(
2684
+ dataclasses.replace(xch_request, number_of_coins=uint16(501)),
2685
+ wallet_environments.tx_config,
2686
+ )
2687
+
2688
+ with pytest.raises(ResponseFailureError, match="Could not find coin with ID 00000000000000000"):
2689
+ await env.rpc_client.split_coins(
2690
+ dataclasses.replace(xch_request, target_coin_id=bytes32([0] * 32)),
2691
+ wallet_environments.tx_config,
2692
+ )
2693
+
2694
+ with pytest.raises(ResponseFailureError, match="is less than the total amount of the split"):
2695
+ await env.rpc_client.split_coins(
2696
+ dataclasses.replace(xch_request, amount_per_coin=uint64(1_000_000_000_000)),
2697
+ wallet_environments.tx_config,
2698
+ )
2699
+
2700
+ with pytest.raises(ResponseFailureError, match="Wallet with ID 42 does not exist"):
2701
+ await env.rpc_client.split_coins(
2702
+ dataclasses.replace(xch_request, wallet_id=uint32(42)),
2703
+ wallet_environments.tx_config,
2704
+ )
2705
+
2706
+ env.wallet_state_manager.wallets[uint32(42)] = object() # type: ignore[assignment]
2707
+ with pytest.raises(ResponseFailureError, match="Cannot split coins from non-fungible wallet types"):
2708
+ await env.rpc_client.split_coins(
2709
+ dataclasses.replace(xch_request, wallet_id=uint32(42)),
2710
+ wallet_environments.tx_config,
2711
+ )
2712
+ del env.wallet_state_manager.wallets[uint32(42)]
2713
+
2714
+ response = await env.rpc_client.split_coins(
2715
+ dataclasses.replace(xch_request, number_of_coins=uint16(0)),
2716
+ wallet_environments.tx_config,
2717
+ )
2718
+ assert response == SplitCoinsResponse([], [])
2719
+
2720
+ await env.rpc_client.split_coins(
2721
+ xch_request,
2722
+ wallet_environments.tx_config,
2723
+ )
2724
+
2725
+ await wallet_environments.process_pending_states(
2726
+ [
2727
+ WalletStateTransition(
2728
+ pre_block_balance_updates={
2729
+ "xch": {
2730
+ "unconfirmed_wallet_balance": -1_000_000_000_000, # just the fee
2731
+ "spendable_balance": -2_000_000_000_000,
2732
+ "pending_change": 1_000_000_000_000,
2733
+ "max_send_amount": -2_000_000_000_000,
2734
+ "pending_coin_removal_count": 2,
2735
+ }
2736
+ },
2737
+ post_block_balance_updates={
2738
+ "xch": {
2739
+ "confirmed_wallet_balance": -1_000_000_000_000, # just the fee
2740
+ "spendable_balance": 1_000_000_000_000,
2741
+ "pending_change": -1_000_000_000_000,
2742
+ "max_send_amount": 1_000_000_000_000,
2743
+ "pending_coin_removal_count": -2,
2744
+ "unspent_coin_count": 99, # split 1 into 100 i.e. +99
2745
+ }
2746
+ },
2747
+ )
2748
+ ]
2749
+ )
2750
+
2751
+ # Now do CATs
2752
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
2753
+ cat_wallet = await CATWallet.create_new_cat_wallet(
2754
+ env.wallet_state_manager,
2755
+ env.xch_wallet,
2756
+ {"identifier": "genesis_by_id"},
2757
+ uint64(50),
2758
+ action_scope,
2759
+ )
2760
+
2761
+ await wallet_environments.process_pending_states(
2762
+ [
2763
+ WalletStateTransition(
2764
+ # no need to test this, it is tested elsewhere
2765
+ pre_block_balance_updates={
2766
+ "xch": {"set_remainder": True},
2767
+ "cat": {"init": True, "set_remainder": True},
2768
+ },
2769
+ post_block_balance_updates={
2770
+ "xch": {"set_remainder": True},
2771
+ "cat": {"set_remainder": True},
2772
+ },
2773
+ )
2774
+ ]
2775
+ )
2776
+
2777
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
2778
+ target_coin = list(await cat_wallet.select_coins(uint64(50), action_scope))[0]
2779
+ assert target_coin.amount == 50
2780
+
2781
+ cat_request = SplitCoins(
2782
+ wallet_id=uint32(2),
2783
+ number_of_coins=uint16(50),
2784
+ amount_per_coin=uint64(1),
2785
+ target_coin_id=target_coin.name(),
2786
+ push=True,
2787
+ )
2788
+
2789
+ await env.rpc_client.split_coins(
2790
+ cat_request,
2791
+ wallet_environments.tx_config,
2792
+ )
2793
+
2794
+ await wallet_environments.process_pending_states(
2795
+ [
2796
+ WalletStateTransition(
2797
+ pre_block_balance_updates={
2798
+ "cat": {
2799
+ "unconfirmed_wallet_balance": 0,
2800
+ "spendable_balance": -50,
2801
+ "pending_change": 50,
2802
+ "max_send_amount": -50,
2803
+ "pending_coin_removal_count": 1,
2804
+ }
2805
+ },
2806
+ post_block_balance_updates={
2807
+ "cat": {
2808
+ "confirmed_wallet_balance": 0,
2809
+ "spendable_balance": 50,
2810
+ "pending_change": -50,
2811
+ "max_send_amount": 50,
2812
+ "pending_coin_removal_count": -1,
2813
+ "unspent_coin_count": 49, # split 1 into 50 i.e. +49
2814
+ }
2815
+ },
2816
+ )
2817
+ ]
2818
+ )
2819
+
2820
+
2821
+ @pytest.mark.parametrize(
2822
+ "wallet_environments",
2823
+ [
2824
+ {
2825
+ "num_environments": 1,
2826
+ "blocks_needed": [2],
2827
+ }
2828
+ ],
2829
+ indirect=True,
2830
+ )
2831
+ @pytest.mark.limit_consensus_modes([ConsensusMode.PLAIN], reason="irrelevant")
2832
+ @pytest.mark.anyio
2833
+ async def test_combine_coins(wallet_environments: WalletTestFramework) -> None:
2834
+ env = wallet_environments.environments[0]
2835
+ env.wallet_aliases = {
2836
+ "xch": 1,
2837
+ "cat": 2,
2838
+ }
2839
+
2840
+ # Should have 4 coins, two 1.75 XCH, two 0.25 XCH
2841
+
2842
+ # Grab one of the 0.25 ones to specify
2843
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
2844
+ target_coin = list(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope))[0]
2845
+ assert target_coin.amount == 250_000_000_000
2846
+
2847
+ # These parameters will give us the maximum amount of behavior coverage
2848
+ # - More amount than the coin we specify
2849
+ # - Less amount than will have to be selected in order create it
2850
+ # - Higher # coins than necessary to create it
2851
+ fee = uint64(100)
2852
+ xch_combine_request = CombineCoins(
2853
+ wallet_id=uint32(1),
2854
+ target_coin_amount=uint64(1_000_000_000_000),
2855
+ number_of_coins=uint16(3),
2856
+ target_coin_ids=[target_coin.name()],
2857
+ fee=fee,
2858
+ push=True,
2859
+ )
2860
+
2861
+ # Test some error cases first
2862
+ with pytest.raises(ResponseFailureError, match="greater then the maximum limit"):
2863
+ await env.rpc_client.combine_coins(
2864
+ dataclasses.replace(xch_combine_request, number_of_coins=uint16(501)),
2865
+ wallet_environments.tx_config,
2866
+ )
2867
+
2868
+ with pytest.raises(ResponseFailureError, match="You need at least two coins to combine"):
2869
+ await env.rpc_client.combine_coins(
2870
+ dataclasses.replace(xch_combine_request, number_of_coins=uint16(0)),
2871
+ wallet_environments.tx_config,
2872
+ )
2873
+
2874
+ with pytest.raises(ResponseFailureError, match="More coin IDs specified than desired number of coins to combine"):
2875
+ await env.rpc_client.combine_coins(
2876
+ dataclasses.replace(xch_combine_request, target_coin_ids=[bytes32([0] * 32)] * 100),
2877
+ wallet_environments.tx_config,
2878
+ )
2879
+
2880
+ with pytest.raises(ResponseFailureError, match="Wallet with ID 50 does not exist"):
2881
+ await env.rpc_client.combine_coins(
2882
+ dataclasses.replace(xch_combine_request, wallet_id=uint32(50)),
2883
+ wallet_environments.tx_config,
2884
+ )
2885
+
2886
+ env.wallet_state_manager.wallets[uint32(42)] = object() # type: ignore[assignment]
2887
+ with pytest.raises(ResponseFailureError, match="Cannot combine coins from non-fungible wallet types"):
2888
+ await env.rpc_client.combine_coins(
2889
+ dataclasses.replace(xch_combine_request, wallet_id=uint32(42)),
2890
+ wallet_environments.tx_config,
2891
+ )
2892
+ del env.wallet_state_manager.wallets[uint32(42)]
2893
+
2894
+ # Now push the request
2895
+ await env.rpc_client.combine_coins(
2896
+ xch_combine_request,
2897
+ wallet_environments.tx_config,
2898
+ )
2899
+
2900
+ await wallet_environments.process_pending_states(
2901
+ [
2902
+ WalletStateTransition(
2903
+ pre_block_balance_updates={
2904
+ "xch": {
2905
+ "unconfirmed_wallet_balance": -fee,
2906
+ "spendable_balance": -2_250_000_000_000,
2907
+ "pending_change": 2_250_000_000_000 - fee,
2908
+ "max_send_amount": -2_250_000_000_000,
2909
+ "pending_coin_removal_count": 3,
2910
+ }
2911
+ },
2912
+ post_block_balance_updates={
2913
+ "xch": {
2914
+ "confirmed_wallet_balance": -fee,
2915
+ "spendable_balance": 2_250_000_000_000 - fee,
2916
+ "pending_change": -(2_250_000_000_000 - fee),
2917
+ "max_send_amount": 2_250_000_000_000 - fee,
2918
+ "pending_coin_removal_count": -3,
2919
+ "unspent_coin_count": -1, # combine 3 into 1 + change
2920
+ }
2921
+ },
2922
+ )
2923
+ ]
2924
+ )
2925
+
2926
+ # Now do CATs
2927
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
2928
+ cat_wallet = await CATWallet.create_new_cat_wallet(
2929
+ env.wallet_state_manager,
2930
+ env.xch_wallet,
2931
+ {"identifier": "genesis_by_id"},
2932
+ uint64(50),
2933
+ action_scope,
2934
+ )
2935
+
2936
+ await wallet_environments.process_pending_states(
2937
+ [
2938
+ WalletStateTransition(
2939
+ # no need to test this, it is tested elsewhere
2940
+ pre_block_balance_updates={
2941
+ "xch": {"set_remainder": True},
2942
+ "cat": {"init": True, "set_remainder": True},
2943
+ },
2944
+ post_block_balance_updates={
2945
+ "xch": {"set_remainder": True},
2946
+ "cat": {"set_remainder": True},
2947
+ },
2948
+ )
2949
+ ]
2950
+ )
2951
+
2952
+ BIG_COIN_AMOUNT = uint64(30)
2953
+ SMALL_COIN_AMOUNT = uint64(15)
2954
+ REALLY_SMALL_COIN_AMOUNT = uint64(5)
2955
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope:
2956
+ await cat_wallet.generate_signed_transaction(
2957
+ [BIG_COIN_AMOUNT, SMALL_COIN_AMOUNT, REALLY_SMALL_COIN_AMOUNT],
2958
+ [await env.xch_wallet.get_puzzle_hash(new=action_scope.config.tx_config.reuse_puzhash)] * 3,
2959
+ action_scope,
2960
+ )
2961
+
2962
+ await wallet_environments.process_pending_states(
2963
+ [
2964
+ WalletStateTransition(
2965
+ # no need to test this, it is tested elsewhere
2966
+ pre_block_balance_updates={
2967
+ "xch": {"set_remainder": True},
2968
+ "cat": {"init": True, "set_remainder": True},
2969
+ },
2970
+ post_block_balance_updates={
2971
+ "xch": {"set_remainder": True},
2972
+ "cat": {"set_remainder": True},
2973
+ },
2974
+ )
2975
+ ]
2976
+ )
2977
+
2978
+ # We're going to test that we select the two smaller coins
2979
+ cat_combine_request = CombineCoins(
2980
+ wallet_id=uint32(2),
2981
+ target_coin_amount=None,
2982
+ number_of_coins=uint16(2),
2983
+ target_coin_ids=[],
2984
+ largest_first=False,
2985
+ fee=fee,
2986
+ push=True,
2987
+ )
2988
+
2989
+ await env.rpc_client.combine_coins(
2990
+ cat_combine_request,
2991
+ wallet_environments.tx_config,
2992
+ )
2993
+
2994
+ await wallet_environments.process_pending_states(
2995
+ [
2996
+ WalletStateTransition(
2997
+ pre_block_balance_updates={
2998
+ "xch": {
2999
+ "unconfirmed_wallet_balance": -fee,
3000
+ "set_remainder": True, # We only really care that a fee was in fact attached
3001
+ },
3002
+ "cat": {
3003
+ "spendable_balance": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
3004
+ "pending_change": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
3005
+ "max_send_amount": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
3006
+ "pending_coin_removal_count": 2,
3007
+ },
3008
+ },
3009
+ post_block_balance_updates={
3010
+ "xch": {
3011
+ "confirmed_wallet_balance": -fee,
3012
+ "set_remainder": True, # We only really care that a fee was in fact attached
3013
+ },
3014
+ "cat": {
3015
+ "spendable_balance": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
3016
+ "pending_change": -SMALL_COIN_AMOUNT - REALLY_SMALL_COIN_AMOUNT,
3017
+ "max_send_amount": SMALL_COIN_AMOUNT + REALLY_SMALL_COIN_AMOUNT,
3018
+ "pending_coin_removal_count": -2,
3019
+ "unspent_coin_count": -1,
3020
+ },
3021
+ },
3022
+ )
3023
+ ]
3024
+ )
3025
+
3026
+
3027
+ @pytest.mark.parametrize(
3028
+ "wallet_environments",
3029
+ [
3030
+ {
3031
+ "num_environments": 1,
3032
+ "blocks_needed": [2],
3033
+ "trusted": True, # irrelevant
3034
+ "reuse_puzhash": True, # irrelevant
3035
+ }
3036
+ ],
3037
+ indirect=True,
3038
+ )
3039
+ @pytest.mark.limit_consensus_modes(reason="irrelevant")
3040
+ @pytest.mark.anyio
3041
+ async def test_fee_bigger_than_selection_coin_combining(wallet_environments: WalletTestFramework) -> None:
3042
+ """
3043
+ This tests the case where the coins we would otherwise select are not enough to pay the fee.
3044
+ """
3045
+
3046
+ env = wallet_environments.environments[0]
3047
+ env.wallet_aliases = {
3048
+ "xch": 1,
3049
+ "cat": 2,
3050
+ }
3051
+
3052
+ # Should have 4 coins, two 1.75 XCH, two 0.25 XCH
3053
+
3054
+ # Grab one of the 0.25 ones to specify
3055
+ async with env.wallet_state_manager.new_action_scope(wallet_environments.tx_config) as action_scope:
3056
+ target_coin = list(await env.xch_wallet.select_coins(uint64(250_000_000_000), action_scope))[0]
3057
+ assert target_coin.amount == 250_000_000_000
3058
+
3059
+ fee = uint64(1_750_000_000_000)
3060
+ # Under standard circumstances we would select the small coins, but this is not enough to pay the fee
3061
+ # Instead, we will grab the big coin first and combine it with one of the smaller coins
3062
+ xch_combine_request = CombineCoins(
3063
+ wallet_id=uint32(1),
3064
+ number_of_coins=uint16(2),
3065
+ fee=fee,
3066
+ largest_first=False,
3067
+ push=True,
3068
+ )
3069
+
3070
+ # First test an error where fee selection causes too many coins to be selected
3071
+ with pytest.raises(ResponseFailureError, match="without selecting more coins than specified: 3"):
3072
+ await env.rpc_client.combine_coins(
3073
+ dataclasses.replace(xch_combine_request, fee=uint64(2_250_000_000_000)),
3074
+ wallet_environments.tx_config,
3075
+ )
3076
+
3077
+ await env.rpc_client.combine_coins(
3078
+ xch_combine_request,
3079
+ wallet_environments.tx_config,
3080
+ )
3081
+
3082
+ await wallet_environments.process_pending_states(
3083
+ [
3084
+ WalletStateTransition(
3085
+ pre_block_balance_updates={
3086
+ "xch": {
3087
+ "unconfirmed_wallet_balance": -fee,
3088
+ "spendable_balance": -2_000_000_000_000,
3089
+ "pending_change": 250_000_000_000,
3090
+ "max_send_amount": -2_000_000_000_000,
3091
+ "pending_coin_removal_count": 2,
3092
+ }
3093
+ },
3094
+ post_block_balance_updates={
3095
+ "xch": {
3096
+ "confirmed_wallet_balance": -fee,
3097
+ "spendable_balance": 250_000_000_000,
3098
+ "pending_change": -250_000_000_000,
3099
+ "max_send_amount": 250_000_000_000,
3100
+ "pending_coin_removal_count": -2,
3101
+ "unspent_coin_count": -1, # combine 2 into 1
3102
+ }
3103
+ },
3104
+ )
3105
+ ]
3106
+ )