ootle-py 0.1.0__tar.gz

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 (307) hide show
  1. ootle_py-0.1.0/.gitignore +46 -0
  2. ootle_py-0.1.0/LICENSE +29 -0
  3. ootle_py-0.1.0/PKG-INFO +287 -0
  4. ootle_py-0.1.0/README.md +257 -0
  5. ootle_py-0.1.0/docs/ootle-rs/README.md +92 -0
  6. ootle_py-0.1.0/examples/README.md +70 -0
  7. ootle_py-0.1.0/examples/stealth/README.md +80 -0
  8. ootle_py-0.1.0/pyproject.toml +224 -0
  9. ootle_py-0.1.0/scripts/_wasm_vendor.py +54 -0
  10. ootle_py-0.1.0/scripts/regen_fixtures.py +128 -0
  11. ootle_py-0.1.0/scripts/unasync.py +321 -0
  12. ootle_py-0.1.0/scripts/update_wasm.py +163 -0
  13. ootle_py-0.1.0/src/ootle/__init__.py +183 -0
  14. ootle_py-0.1.0/src/ootle/__main__.py +6 -0
  15. ootle_py-0.1.0/src/ootle/_async/__init__.py +0 -0
  16. ootle_py-0.1.0/src/ootle/_async/_balances.py +85 -0
  17. ootle_py-0.1.0/src/ootle/_async/_client_builders.py +35 -0
  18. ootle_py-0.1.0/src/ootle/_async/_client_reads.py +61 -0
  19. ootle_py-0.1.0/src/ootle/_async/_client_send.py +60 -0
  20. ootle_py-0.1.0/src/ootle/_async/_client_stealth_parse.py +50 -0
  21. ootle_py-0.1.0/src/ootle/_async/_client_stealth_reads.py +82 -0
  22. ootle_py-0.1.0/src/ootle/_async/_client_writes.py +61 -0
  23. ootle_py-0.1.0/src/ootle/_async/_endpoints.py +151 -0
  24. ootle_py-0.1.0/src/ootle/_async/_events.py +95 -0
  25. ootle_py-0.1.0/src/ootle/_async/_offload.py +31 -0
  26. ootle_py-0.1.0/src/ootle/_async/_resolver.py +120 -0
  27. ootle_py-0.1.0/src/ootle/_async/_resolver_helpers.py +199 -0
  28. ootle_py-0.1.0/src/ootle/_async/_send.py +126 -0
  29. ootle_py-0.1.0/src/ootle/_async/_substates.py +24 -0
  30. ootle_py-0.1.0/src/ootle/_async/_transport.py +197 -0
  31. ootle_py-0.1.0/src/ootle/_async/_watcher/__init__.py +12 -0
  32. ootle_py-0.1.0/src/ootle/_async/_watcher/_actor.py +160 -0
  33. ootle_py-0.1.0/src/ootle/_async/_watcher/_pending.py +156 -0
  34. ootle_py-0.1.0/src/ootle/_async/builders/__init__.py +8 -0
  35. ootle_py-0.1.0/src/ootle/_async/builders/_arg_coerce.py +70 -0
  36. ootle_py-0.1.0/src/ootle/_async/builders/_base.py +80 -0
  37. ootle_py-0.1.0/src/ootle/_async/builders/_common.py +36 -0
  38. ootle_py-0.1.0/src/ootle/_async/builders/account.py +106 -0
  39. ootle_py-0.1.0/src/ootle/_async/builders/component.py +148 -0
  40. ootle_py-0.1.0/src/ootle/_async/builders/faucet.py +163 -0
  41. ootle_py-0.1.0/src/ootle/_async/client.py +171 -0
  42. ootle_py-0.1.0/src/ootle/_async/stealth/__init__.py +9 -0
  43. ootle_py-0.1.0/src/ootle/_async/stealth/_authorizer_helpers.py +171 -0
  44. ootle_py-0.1.0/src/ootle/_async/stealth/_authorizer_sign.py +86 -0
  45. ootle_py-0.1.0/src/ootle/_async/stealth/_builder_helpers.py +143 -0
  46. ootle_py-0.1.0/src/ootle/_async/stealth/_builder_prepare.py +126 -0
  47. ootle_py-0.1.0/src/ootle/_async/stealth/_spec.py +49 -0
  48. ootle_py-0.1.0/src/ootle/_async/stealth/_view_secret.py +51 -0
  49. ootle_py-0.1.0/src/ootle/_async/stealth/authorizer.py +199 -0
  50. ootle_py-0.1.0/src/ootle/_async/stealth/faucet_path.py +140 -0
  51. ootle_py-0.1.0/src/ootle/_async/stealth/transfer.py +198 -0
  52. ootle_py-0.1.0/src/ootle/_crypto/__init__.py +74 -0
  53. ootle_py-0.1.0/src/ootle/_crypto/_bridge/__init__.py +109 -0
  54. ootle_py-0.1.0/src/ootle/_crypto/_bridge/_abi.py +104 -0
  55. ootle_py-0.1.0/src/ootle/_crypto/_bridge/_imports.py +75 -0
  56. ootle_py-0.1.0/src/ootle/_crypto/_provider.py +111 -0
  57. ootle_py-0.1.0/src/ootle/_crypto/_stealth_provider.py +135 -0
  58. ootle_py-0.1.0/src/ootle/_crypto/_stealth_provider_sync.py +75 -0
  59. ootle_py-0.1.0/src/ootle/_crypto/_wasm_provider.py +148 -0
  60. ootle_py-0.1.0/src/ootle/_crypto/_wasm_results.py +108 -0
  61. ootle_py-0.1.0/src/ootle/_crypto/_wasm_runtime.py +168 -0
  62. ootle_py-0.1.0/src/ootle/_crypto/_wasm_stealth.py +99 -0
  63. ootle_py-0.1.0/src/ootle/_crypto/_wasm_stealth_helpers.py +38 -0
  64. ootle_py-0.1.0/src/ootle/_crypto/_wasm_stealth_outputs.py +155 -0
  65. ootle_py-0.1.0/src/ootle/_crypto/wasm/README.md +25 -0
  66. ootle_py-0.1.0/src/ootle/_crypto/wasm/VERSION +4 -0
  67. ootle_py-0.1.0/src/ootle/_crypto/wasm/__init__.py +6 -0
  68. ootle_py-0.1.0/src/ootle/_crypto/wasm/ootle_wasm_bg.wasm +0 -0
  69. ootle_py-0.1.0/src/ootle/_instructions.py +129 -0
  70. ootle_py-0.1.0/src/ootle/_signing.py +136 -0
  71. ootle_py-0.1.0/src/ootle/_stealth_balance_proof.py +69 -0
  72. ootle_py-0.1.0/src/ootle/_sync/__init__.py +0 -0
  73. ootle_py-0.1.0/src/ootle/_sync/_balances.py +85 -0
  74. ootle_py-0.1.0/src/ootle/_sync/_client_builders.py +35 -0
  75. ootle_py-0.1.0/src/ootle/_sync/_client_reads.py +61 -0
  76. ootle_py-0.1.0/src/ootle/_sync/_client_send.py +60 -0
  77. ootle_py-0.1.0/src/ootle/_sync/_client_stealth_parse.py +50 -0
  78. ootle_py-0.1.0/src/ootle/_sync/_client_stealth_reads.py +82 -0
  79. ootle_py-0.1.0/src/ootle/_sync/_client_writes.py +61 -0
  80. ootle_py-0.1.0/src/ootle/_sync/_endpoints.py +147 -0
  81. ootle_py-0.1.0/src/ootle/_sync/_events.py +95 -0
  82. ootle_py-0.1.0/src/ootle/_sync/_offload.py +26 -0
  83. ootle_py-0.1.0/src/ootle/_sync/_resolver.py +120 -0
  84. ootle_py-0.1.0/src/ootle/_sync/_resolver_helpers.py +199 -0
  85. ootle_py-0.1.0/src/ootle/_sync/_send.py +124 -0
  86. ootle_py-0.1.0/src/ootle/_sync/_substates.py +24 -0
  87. ootle_py-0.1.0/src/ootle/_sync/_transport.py +197 -0
  88. ootle_py-0.1.0/src/ootle/_sync/_watcher/__init__.py +12 -0
  89. ootle_py-0.1.0/src/ootle/_sync/_watcher/_actor.py +174 -0
  90. ootle_py-0.1.0/src/ootle/_sync/_watcher/_pending.py +146 -0
  91. ootle_py-0.1.0/src/ootle/_sync/builders/__init__.py +8 -0
  92. ootle_py-0.1.0/src/ootle/_sync/builders/_arg_coerce.py +70 -0
  93. ootle_py-0.1.0/src/ootle/_sync/builders/_base.py +80 -0
  94. ootle_py-0.1.0/src/ootle/_sync/builders/_common.py +36 -0
  95. ootle_py-0.1.0/src/ootle/_sync/builders/account.py +106 -0
  96. ootle_py-0.1.0/src/ootle/_sync/builders/component.py +148 -0
  97. ootle_py-0.1.0/src/ootle/_sync/builders/faucet.py +163 -0
  98. ootle_py-0.1.0/src/ootle/_sync/client.py +167 -0
  99. ootle_py-0.1.0/src/ootle/_sync/stealth/__init__.py +9 -0
  100. ootle_py-0.1.0/src/ootle/_sync/stealth/_authorizer_helpers.py +171 -0
  101. ootle_py-0.1.0/src/ootle/_sync/stealth/_authorizer_sign.py +86 -0
  102. ootle_py-0.1.0/src/ootle/_sync/stealth/_builder_helpers.py +143 -0
  103. ootle_py-0.1.0/src/ootle/_sync/stealth/_builder_prepare.py +126 -0
  104. ootle_py-0.1.0/src/ootle/_sync/stealth/_spec.py +49 -0
  105. ootle_py-0.1.0/src/ootle/_sync/stealth/_view_secret.py +51 -0
  106. ootle_py-0.1.0/src/ootle/_sync/stealth/authorizer.py +197 -0
  107. ootle_py-0.1.0/src/ootle/_sync/stealth/faucet_path.py +140 -0
  108. ootle_py-0.1.0/src/ootle/_sync/stealth/transfer.py +198 -0
  109. ootle_py-0.1.0/src/ootle/_transaction_builder.py +194 -0
  110. ootle_py-0.1.0/src/ootle/_transaction_builder_build.py +39 -0
  111. ootle_py-0.1.0/src/ootle/_transaction_builder_fees.py +58 -0
  112. ootle_py-0.1.0/src/ootle/_transaction_builder_merge.py +134 -0
  113. ootle_py-0.1.0/src/ootle/_types/__init__.py +5 -0
  114. ootle_py-0.1.0/src/ootle/_types/_bor_value.py +128 -0
  115. ootle_py-0.1.0/src/ootle/_types/_cbor.py +78 -0
  116. ootle_py-0.1.0/src/ootle/_types/_indexer_json.py +134 -0
  117. ootle_py-0.1.0/src/ootle/_types/_instruction_args.py +105 -0
  118. ootle_py-0.1.0/src/ootle/_types/_instruction_variants.py +133 -0
  119. ootle_py-0.1.0/src/ootle/_types/_json.py +142 -0
  120. ootle_py-0.1.0/src/ootle/_types/_json_helpers.py +87 -0
  121. ootle_py-0.1.0/src/ootle/_types/_reject_reason_json.py +114 -0
  122. ootle_py-0.1.0/src/ootle/_types/_sse_events.py +50 -0
  123. ootle_py-0.1.0/src/ootle/_types/_substate_json.py +138 -0
  124. ootle_py-0.1.0/src/ootle/_types/_tari_constants.py +24 -0
  125. ootle_py-0.1.0/src/ootle/_types/_tx_body.py +97 -0
  126. ootle_py-0.1.0/src/ootle/_types/address.py +140 -0
  127. ootle_py-0.1.0/src/ootle/_types/amount.py +30 -0
  128. ootle_py-0.1.0/src/ootle/_types/diff_summary.py +43 -0
  129. ootle_py-0.1.0/src/ootle/_types/events.py +53 -0
  130. ootle_py-0.1.0/src/ootle/_types/instructions.py +69 -0
  131. ootle_py-0.1.0/src/ootle/_types/keys.py +76 -0
  132. ootle_py-0.1.0/src/ootle/_types/network.py +67 -0
  133. ootle_py-0.1.0/src/ootle/_types/outcome.py +59 -0
  134. ootle_py-0.1.0/src/ootle/_types/receipt.py +52 -0
  135. ootle_py-0.1.0/src/ootle/_types/reject_reason.py +192 -0
  136. ootle_py-0.1.0/src/ootle/_types/stealth/__init__.py +64 -0
  137. ootle_py-0.1.0/src/ootle/_types/stealth/_json.py +135 -0
  138. ootle_py-0.1.0/src/ootle/_types/stealth/_proofs.py +98 -0
  139. ootle_py-0.1.0/src/ootle/_types/stealth/_substate_utxo.py +76 -0
  140. ootle_py-0.1.0/src/ootle/_types/stealth/_unspent.py +118 -0
  141. ootle_py-0.1.0/src/ootle/_types/stealth/encrypted_data.py +112 -0
  142. ootle_py-0.1.0/src/ootle/_types/stealth/one_time_pubkey.py +51 -0
  143. ootle_py-0.1.0/src/ootle/_types/stealth/output.py +163 -0
  144. ootle_py-0.1.0/src/ootle/_types/stealth/requirements.py +165 -0
  145. ootle_py-0.1.0/src/ootle/_types/stealth/statements.py +184 -0
  146. ootle_py-0.1.0/src/ootle/_types/substate.py +109 -0
  147. ootle_py-0.1.0/src/ootle/_types/template.py +16 -0
  148. ootle_py-0.1.0/src/ootle/_types/transaction.py +192 -0
  149. ootle_py-0.1.0/src/ootle/_types/want_input.py +69 -0
  150. ootle_py-0.1.0/src/ootle/_version.py +31 -0
  151. ootle_py-0.1.0/src/ootle/_wallet_seal.py +51 -0
  152. ootle_py-0.1.0/src/ootle/_wallet_stealth.py +123 -0
  153. ootle_py-0.1.0/src/ootle/cli.py +44 -0
  154. ootle_py-0.1.0/src/ootle/errors.py +141 -0
  155. ootle_py-0.1.0/src/ootle/wallet.py +187 -0
  156. ootle_py-0.1.0/tests/__init__.py +0 -0
  157. ootle_py-0.1.0/tests/_async/__init__.py +0 -0
  158. ootle_py-0.1.0/tests/_async/_helpers.py +13 -0
  159. ootle_py-0.1.0/tests/_async/integration/README.md +27 -0
  160. ootle_py-0.1.0/tests/_async/integration/__init__.py +0 -0
  161. ootle_py-0.1.0/tests/_async/integration/test_account_transfer_e2e.py +58 -0
  162. ootle_py-0.1.0/tests/_async/integration/test_balance_e2e.py +52 -0
  163. ootle_py-0.1.0/tests/_async/integration/test_dry_run_e2e.py +31 -0
  164. ootle_py-0.1.0/tests/_async/integration/test_event_watching_e2e.py +33 -0
  165. ootle_py-0.1.0/tests/_async/integration/test_faucet_e2e.py +37 -0
  166. ootle_py-0.1.0/tests/_async/integration/test_multi_signer_e2e.py +67 -0
  167. ootle_py-0.1.0/tests/_async/stealth/__init__.py +0 -0
  168. ootle_py-0.1.0/tests/_async/stealth/_builder_helpers.py +118 -0
  169. ootle_py-0.1.0/tests/_async/stealth/test_authorizer.py +153 -0
  170. ootle_py-0.1.0/tests/_async/stealth/test_authorizer_multi_input.py +81 -0
  171. ootle_py-0.1.0/tests/_async/stealth/test_authorizer_spend.py +153 -0
  172. ootle_py-0.1.0/tests/_async/stealth/test_authorizer_view_secret.py +148 -0
  173. ootle_py-0.1.0/tests/_async/stealth/test_client_wiring.py +47 -0
  174. ootle_py-0.1.0/tests/_async/stealth/test_faucet_stealth.py +141 -0
  175. ootle_py-0.1.0/tests/_async/stealth/test_parse_unspent_output.py +83 -0
  176. ootle_py-0.1.0/tests/_async/stealth/test_read_helpers.py +151 -0
  177. ootle_py-0.1.0/tests/_async/stealth/test_stealth_transfer_builder.py +133 -0
  178. ootle_py-0.1.0/tests/_async/stealth/test_stealth_transfer_errors.py +82 -0
  179. ootle_py-0.1.0/tests/_async/stealth/test_stealth_transfer_prepare.py +188 -0
  180. ootle_py-0.1.0/tests/_async/test_balances.py +91 -0
  181. ootle_py-0.1.0/tests/_async/test_builders_account.py +134 -0
  182. ootle_py-0.1.0/tests/_async/test_builders_component.py +136 -0
  183. ootle_py-0.1.0/tests/_async/test_builders_component_extra.py +117 -0
  184. ootle_py-0.1.0/tests/_async/test_builders_faucet.py +60 -0
  185. ootle_py-0.1.0/tests/_async/test_client_dry_run.py +100 -0
  186. ootle_py-0.1.0/tests/_async/test_client_shell.py +50 -0
  187. ootle_py-0.1.0/tests/_async/test_resolver.py +138 -0
  188. ootle_py-0.1.0/tests/_async/test_seal_send.py +84 -0
  189. ootle_py-0.1.0/tests/_async/test_transport_network.py +30 -0
  190. ootle_py-0.1.0/tests/_async/test_transport_shell.py +73 -0
  191. ootle_py-0.1.0/tests/_async/test_transport_submit.py +69 -0
  192. ootle_py-0.1.0/tests/_async/test_transport_substate.py +69 -0
  193. ootle_py-0.1.0/tests/_async/test_watch_events.py +101 -0
  194. ootle_py-0.1.0/tests/_async/test_watcher.py +191 -0
  195. ootle_py-0.1.0/tests/_async/test_watcher_extra.py +194 -0
  196. ootle_py-0.1.0/tests/_async/test_watcher_fallback.py +82 -0
  197. ootle_py-0.1.0/tests/_async/test_watcher_url.py +45 -0
  198. ootle_py-0.1.0/tests/_helpers/__init__.py +0 -0
  199. ootle_py-0.1.0/tests/_helpers/crypto.py +98 -0
  200. ootle_py-0.1.0/tests/_helpers/instructions.py +29 -0
  201. ootle_py-0.1.0/tests/_helpers/signer.py +35 -0
  202. ootle_py-0.1.0/tests/_helpers/sse.py +87 -0
  203. ootle_py-0.1.0/tests/_helpers/substates.py +94 -0
  204. ootle_py-0.1.0/tests/_sync/__init__.py +0 -0
  205. ootle_py-0.1.0/tests/_sync/_helpers.py +13 -0
  206. ootle_py-0.1.0/tests/_sync/integration/__init__.py +0 -0
  207. ootle_py-0.1.0/tests/_sync/integration/test_account_transfer_e2e.py +58 -0
  208. ootle_py-0.1.0/tests/_sync/integration/test_balance_e2e.py +52 -0
  209. ootle_py-0.1.0/tests/_sync/integration/test_dry_run_e2e.py +31 -0
  210. ootle_py-0.1.0/tests/_sync/integration/test_event_watching_e2e.py +33 -0
  211. ootle_py-0.1.0/tests/_sync/integration/test_faucet_e2e.py +35 -0
  212. ootle_py-0.1.0/tests/_sync/integration/test_multi_signer_e2e.py +65 -0
  213. ootle_py-0.1.0/tests/_sync/stealth/__init__.py +8 -0
  214. ootle_py-0.1.0/tests/_sync/stealth/_builder_helpers.py +116 -0
  215. ootle_py-0.1.0/tests/_sync/stealth/test_authorizer_spend.py +153 -0
  216. ootle_py-0.1.0/tests/_sync/stealth/test_authorizer_view_secret.py +69 -0
  217. ootle_py-0.1.0/tests/_sync/stealth/test_read_helpers.py +147 -0
  218. ootle_py-0.1.0/tests/_sync/stealth/test_stealth_transfer.py +175 -0
  219. ootle_py-0.1.0/tests/_sync/test_balances.py +91 -0
  220. ootle_py-0.1.0/tests/_sync/test_builders_account.py +134 -0
  221. ootle_py-0.1.0/tests/_sync/test_builders_component.py +136 -0
  222. ootle_py-0.1.0/tests/_sync/test_builders_component_extra.py +117 -0
  223. ootle_py-0.1.0/tests/_sync/test_builders_faucet.py +60 -0
  224. ootle_py-0.1.0/tests/_sync/test_client_dry_run.py +100 -0
  225. ootle_py-0.1.0/tests/_sync/test_client_shell.py +50 -0
  226. ootle_py-0.1.0/tests/_sync/test_resolver.py +138 -0
  227. ootle_py-0.1.0/tests/_sync/test_seal_send.py +78 -0
  228. ootle_py-0.1.0/tests/_sync/test_transport_network.py +30 -0
  229. ootle_py-0.1.0/tests/_sync/test_transport_shell.py +73 -0
  230. ootle_py-0.1.0/tests/_sync/test_transport_submit.py +69 -0
  231. ootle_py-0.1.0/tests/_sync/test_transport_substate.py +69 -0
  232. ootle_py-0.1.0/tests/_sync/test_watch_events.py +101 -0
  233. ootle_py-0.1.0/tests/_sync/test_watcher.py +189 -0
  234. ootle_py-0.1.0/tests/_sync/test_watcher_extra.py +193 -0
  235. ootle_py-0.1.0/tests/_sync/test_watcher_fallback.py +67 -0
  236. ootle_py-0.1.0/tests/_sync/test_watcher_url.py +42 -0
  237. ootle_py-0.1.0/tests/conftest.py +44 -0
  238. ootle_py-0.1.0/tests/fixtures/README.md +29 -0
  239. ootle_py-0.1.0/tests/fixtures/addresses_corpus.json +51 -0
  240. ootle_py-0.1.0/tests/fixtures/crypto_vectors.json +122 -0
  241. ootle_py-0.1.0/tests/fixtures/stealth_unblind_vector.json +11 -0
  242. ootle_py-0.1.0/tests/fixtures/substate_examples/component.json +52 -0
  243. ootle_py-0.1.0/tests/fixtures/substate_examples/receipt.json +29 -0
  244. ootle_py-0.1.0/tests/fixtures/substate_examples/resource.json +25 -0
  245. ootle_py-0.1.0/tests/fixtures/substate_examples/unknown.json +10 -0
  246. ootle_py-0.1.0/tests/fixtures/substate_examples/vault.json +15 -0
  247. ootle_py-0.1.0/tests/integration/__init__.py +0 -0
  248. ootle_py-0.1.0/tests/integration/stealth/__init__.py +8 -0
  249. ootle_py-0.1.0/tests/integration/stealth/_helpers.py +106 -0
  250. ootle_py-0.1.0/tests/integration/stealth/conftest.py +60 -0
  251. ootle_py-0.1.0/tests/integration/stealth/test_stealth_faucet.py +51 -0
  252. ootle_py-0.1.0/tests/integration/stealth/test_stealth_spend.py +66 -0
  253. ootle_py-0.1.0/tests/integration/stealth/test_stealth_to_revealed.py +46 -0
  254. ootle_py-0.1.0/tests/integration/stealth/test_stealth_to_stealth.py +97 -0
  255. ootle_py-0.1.0/tests/integration/test_examples.py +50 -0
  256. ootle_py-0.1.0/tests/test_cli.py +34 -0
  257. ootle_py-0.1.0/tests/unit/__init__.py +0 -0
  258. ootle_py-0.1.0/tests/unit/stealth/__init__.py +0 -0
  259. ootle_py-0.1.0/tests/unit/stealth/_wasm_witness.py +54 -0
  260. ootle_py-0.1.0/tests/unit/stealth/conftest.py +13 -0
  261. ootle_py-0.1.0/tests/unit/stealth/test_output_invariants.py +180 -0
  262. ootle_py-0.1.0/tests/unit/stealth/test_statement_json.py +228 -0
  263. ootle_py-0.1.0/tests/unit/stealth/test_stealth_spec.py +94 -0
  264. ootle_py-0.1.0/tests/unit/stealth/test_wasm_stealth_aggregate.py +49 -0
  265. ootle_py-0.1.0/tests/unit/stealth/test_wasm_stealth_outputs.py +137 -0
  266. ootle_py-0.1.0/tests/unit/stealth/test_wasm_stealth_unblind.py +111 -0
  267. ootle_py-0.1.0/tests/unit/test_bor_value.py +67 -0
  268. ootle_py-0.1.0/tests/unit/test_builder_merge.py +128 -0
  269. ootle_py-0.1.0/tests/unit/test_cbor_encoders.py +82 -0
  270. ootle_py-0.1.0/tests/unit/test_crypto_address.py +49 -0
  271. ootle_py-0.1.0/tests/unit/test_crypto_borsh.py +27 -0
  272. ootle_py-0.1.0/tests/unit/test_crypto_bridge.py +70 -0
  273. ootle_py-0.1.0/tests/unit/test_crypto_keypair.py +52 -0
  274. ootle_py-0.1.0/tests/unit/test_crypto_provider_shape.py +27 -0
  275. ootle_py-0.1.0/tests/unit/test_crypto_runtime.py +36 -0
  276. ootle_py-0.1.0/tests/unit/test_crypto_seal.py +35 -0
  277. ootle_py-0.1.0/tests/unit/test_crypto_signing.py +40 -0
  278. ootle_py-0.1.0/tests/unit/test_ensure_stealth_capable.py +65 -0
  279. ootle_py-0.1.0/tests/unit/test_errors.py +125 -0
  280. ootle_py-0.1.0/tests/unit/test_indexer_json.py +146 -0
  281. ootle_py-0.1.0/tests/unit/test_instructions.py +114 -0
  282. ootle_py-0.1.0/tests/unit/test_instructions_codec.py +83 -0
  283. ootle_py-0.1.0/tests/unit/test_json_helpers.py +90 -0
  284. ootle_py-0.1.0/tests/unit/test_json_helpers_extra.py +81 -0
  285. ootle_py-0.1.0/tests/unit/test_logging.py +70 -0
  286. ootle_py-0.1.0/tests/unit/test_public_api_shape.py +138 -0
  287. ootle_py-0.1.0/tests/unit/test_reject_reason.py +141 -0
  288. ootle_py-0.1.0/tests/unit/test_resolver_helpers.py +132 -0
  289. ootle_py-0.1.0/tests/unit/test_resolver_helpers_extra.py +121 -0
  290. ootle_py-0.1.0/tests/unit/test_signing_local.py +136 -0
  291. ootle_py-0.1.0/tests/unit/test_substate_json.py +162 -0
  292. ootle_py-0.1.0/tests/unit/test_transaction_builder.py +164 -0
  293. ootle_py-0.1.0/tests/unit/test_types_address.py +84 -0
  294. ootle_py-0.1.0/tests/unit/test_types_amount.py +30 -0
  295. ootle_py-0.1.0/tests/unit/test_types_events.py +43 -0
  296. ootle_py-0.1.0/tests/unit/test_types_keys.py +92 -0
  297. ootle_py-0.1.0/tests/unit/test_types_network.py +29 -0
  298. ootle_py-0.1.0/tests/unit/test_types_outcome.py +39 -0
  299. ootle_py-0.1.0/tests/unit/test_types_substate.py +82 -0
  300. ootle_py-0.1.0/tests/unit/test_types_transaction.py +175 -0
  301. ootle_py-0.1.0/tests/unit/test_types_want_input.py +42 -0
  302. ootle_py-0.1.0/tests/unit/test_unasync_driver.py +122 -0
  303. ootle_py-0.1.0/tests/unit/test_update_wasm.py +102 -0
  304. ootle_py-0.1.0/tests/unit/test_vendored_wasm.py +36 -0
  305. ootle_py-0.1.0/tests/unit/test_wallet_fold.py +110 -0
  306. ootle_py-0.1.0/tests/unit/test_wallet_multisigner.py +181 -0
  307. ootle_py-0.1.0/tests/unit/test_wasm_results.py +110 -0
@@ -0,0 +1,46 @@
1
+ # Python build artefacts
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ build/
7
+ dist/
8
+ wheels/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # Tooling caches
18
+ .ruff_cache/
19
+ .pytest_cache/
20
+ .pyright/
21
+ .mypy_cache/
22
+ .tox/
23
+ .nox/
24
+
25
+ # Coverage
26
+ .coverage
27
+ .coverage.*
28
+ coverage.xml
29
+ htmlcov/
30
+
31
+ # uv lock cache (lockfile itself is committed)
32
+ .uv-cache/
33
+
34
+ # Editor / OS
35
+ .idea/
36
+ .vscode/
37
+ .DS_Store
38
+ *.swp
39
+ *.swo
40
+
41
+ # Local environment overrides
42
+ .env
43
+ .env.*
44
+ !.env.example
45
+
46
+ .claude/worktrees
ootle_py-0.1.0/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, The Tari Developer Community
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: ootle-py
3
+ Version: 0.1.0
4
+ Summary: Async-first Python client library for the Tari L2 (Ootle) network.
5
+ Project-URL: Homepage, https://github.com/tari-project/ootle-python
6
+ Project-URL: Documentation, https://github.com/tari-project/ootle-python/tree/main/docs
7
+ Project-URL: Repository, https://github.com/tari-project/ootle-python
8
+ Project-URL: Issues, https://github.com/tari-project/ootle-python/issues
9
+ Project-URL: Changelog, https://github.com/tari-project/ootle-python/blob/main/CHANGELOG.md
10
+ Author: The Tari Project
11
+ License: BSD-3-Clause
12
+ License-File: LICENSE
13
+ Keywords: async,blockchain,client,layer-2,ootle,tari,wasm
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: BSD License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: Implementation :: CPython
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.13
25
+ Requires-Dist: cbor2>=5.6
26
+ Requires-Dist: httpx-sse>=0.4.3
27
+ Requires-Dist: httpx>=0.28.1
28
+ Requires-Dist: wasmtime>=44.0.0
29
+ Description-Content-Type: text/markdown
30
+
31
+ # ootle
32
+
33
+ A modern, async-first **Python client library for the Tari L2 network**
34
+ (codename _Ootle_). It is the Pythonic counterpart to the Rust crate
35
+ `ootle-rs` and the JavaScript package `@tari-project/ootle-wasm`.
36
+
37
+ > **Status: pre-1.0.** The `0.x.y` API may shift; every breaking change
38
+ > is recorded in [`CHANGELOG.md`](CHANGELOG.md).
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pip install ootle-py
44
+ # or
45
+ uv add ootle-py
46
+ ```
47
+
48
+ The distribution name on PyPI is **`ootle-py`**; you import it as `ootle`:
49
+
50
+ ```python
51
+ from ootle import AsyncOotleClient, OotleClient
52
+ ```
53
+
54
+ Requirements: Python **>= 3.13**. The package is pure-Python; the Tari
55
+ WASM crypto blob ships inside the wheel — no native build step, and the
56
+ same `py3-none-any` wheel runs everywhere.
57
+
58
+ ## Quick start: read-only query
59
+
60
+ Connect to a LocalNet indexer and read an account balance. No wallet is
61
+ needed for read-only queries.
62
+
63
+ ```python
64
+ import asyncio
65
+ from ootle import (
66
+ AsyncOotleClient, ComponentAddress, Network,
67
+ ResourceAddress, TARI_TOKEN, default_indexer_url,
68
+ )
69
+
70
+
71
+ async def main() -> None:
72
+ account = ComponentAddress("component_...your-address...")
73
+ async with AsyncOotleClient.connect(default_indexer_url(Network.LOCAL_NET)) as client:
74
+ balance = await client.get_account_balance(account, ResourceAddress(TARI_TOKEN))
75
+ print(f"TARI balance: {balance}")
76
+ for resource, amount in (await client.get_account_balances(account)).items():
77
+ print(f" {resource}: {amount}")
78
+
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ Full version: [`examples/balance_query.py`](examples/balance_query.py)
84
+ (and the sync mirror, [`examples/balance_query_sync.py`](examples/balance_query_sync.py)).
85
+
86
+ ## Quick start: public transfer
87
+
88
+ Faucet a fresh sender, then transfer TARI to a fresh recipient, watching
89
+ each transaction to finalisation. Note that `seal_transaction` is
90
+ **synchronous** — only the I/O calls are awaited.
91
+
92
+ ```python
93
+ import asyncio
94
+ from ootle import (
95
+ AsyncOotleClient, LocalSigner, Network, OotleSecretKey,
96
+ OotleWallet, ResourceAddress, TARI, TARI_TOKEN, default_indexer_url,
97
+ )
98
+
99
+
100
+ async def main() -> None:
101
+ sender = OotleSecretKey.random(Network.LOCAL_NET)
102
+ wallet = OotleWallet(default=LocalSigner(sender))
103
+ resource = ResourceAddress(TARI_TOKEN)
104
+ async with AsyncOotleClient.connect(
105
+ default_indexer_url(Network.LOCAL_NET), wallet=wallet
106
+ ) as client:
107
+ # Faucet the public XTR faucet's fixed dispense into the sender.
108
+ funded = await client.faucet().take_funds().pay_fee(500).prepare()
109
+ await (await client.send_transaction(client.seal_transaction(funded))).watch()
110
+
111
+ # Transfer 2 TARI to a fresh recipient.
112
+ recipient = OotleSecretKey.random(Network.LOCAL_NET).to_address()
113
+ unsigned = await (
114
+ client.account()
115
+ .pay_fee(1000)
116
+ .public_transfer(recipient, resource, 2 * TARI)
117
+ .prepare()
118
+ )
119
+ sealed = client.seal_transaction(unsigned)
120
+ await (await client.send_transaction(sealed)).watch()
121
+
122
+
123
+ asyncio.run(main())
124
+ ```
125
+
126
+ Register extra signers with `wallet.register(LocalSigner(...))` and they
127
+ are folded in automatically at seal time (multi-signer co-authorisation);
128
+ `manual_co_signing.py` shows the explicit authorize → attach → seal
129
+ hand-off for remote-signer / HSM setups. Estimate fees first with
130
+ `await client.send_dry_run(unsigned)`. Full file:
131
+ [`examples/fungible_transfer.py`](examples/fungible_transfer.py).
132
+
133
+ ## Stealth (confidential) transfers
134
+
135
+ Confidential transfers run **entirely on the vendored `ootle-wasm`
136
+ blob** — the default crypto provider. Bulletproofs, ElGamal viewable
137
+ balances, balance-proof signatures, and input-mask aggregation all
138
+ execute locally; **no wallet daemon or external service is required.**
139
+
140
+ Build the transfer, hydrate the balance proof with an
141
+ `AsyncWalletStealthAuthorizer`, then seal with the wallet's default
142
+ signer:
143
+
144
+ ```python
145
+ import asyncio
146
+ from ootle import (
147
+ AsyncOotleClient, AsyncStealthTransfer, AsyncWalletStealthAuthorizer,
148
+ LocalSigner, Network, OotleSecretKey, OotleWallet, Output,
149
+ ResourceAddress, TARI, TARI_TOKEN, default_indexer_url,
150
+ )
151
+
152
+
153
+ async def main() -> None:
154
+ sender = OotleSecretKey.random(Network.LOCAL_NET)
155
+ wallet = OotleWallet(default=LocalSigner(sender))
156
+ resource = ResourceAddress(TARI_TOKEN)
157
+ recipient = OotleSecretKey.random(Network.LOCAL_NET).to_address()
158
+ async with AsyncOotleClient.connect(
159
+ default_indexer_url(Network.LOCAL_NET), wallet=wallet
160
+ ) as client:
161
+ # ... faucet the sender first (see examples/stealth/) ...
162
+ transfer = AsyncStealthTransfer(client, resource)
163
+ transfer.spend_revealed_input(sender.to_address().to_component_address(), 5 * TARI)
164
+ transfer.to_stealth_output(
165
+ Output(destination=recipient, amount=4 * TARI, resource_address=resource)
166
+ )
167
+ transfer.to_revealed_output(TARI)
168
+ transfer.pay_fee_from_revealed(500)
169
+
170
+ spec = await transfer.prepare()
171
+ authorizer = AsyncWalletStealthAuthorizer(
172
+ wallet, spec, view_secret=sender.view_secret
173
+ )
174
+ hydrated = await authorizer.prepare(client)
175
+ sealed = client.seal_transaction(hydrated.unsigned)
176
+ await (await client.send_transaction(sealed)).watch()
177
+
178
+
179
+ asyncio.run(main())
180
+ ```
181
+
182
+ Stealth supports revealed and stealth inputs and outputs, mixed in one
183
+ transfer: `spend_revealed_input` / `spend_stealth_input` (with input-mask
184
+ aggregation) feed `to_stealth_output` / `to_revealed_output`, with fees
185
+ paid from a revealed bucket (`pay_fee_from_revealed`) or a stealth
186
+ account's revealed vault (`pay_fee_from_stealth`). The public faucet has
187
+ a stealth path too (`IFaucet.take_funds_stealth`), and owned UTXOs are
188
+ read back with `AsyncOotleClient.decrypt_owned_utxo` (AEAD owner-read).
189
+
190
+ The full set of runnable stealth examples — faucet deposit,
191
+ stealth↔revealed, stealth↔stealth, spending an owned UTXO, and the sync
192
+ mirror — lives in [`examples/stealth/`](examples/stealth/README.md).
193
+
194
+ ## Sync vs. async
195
+
196
+ Every async API has a sync mirror with the same shape. Swap the imports
197
+ and drop the `await`s:
198
+
199
+ ```python
200
+ from ootle import OotleClient # sync
201
+ from ootle import AsyncOotleClient # async
202
+ ```
203
+
204
+ The sync names mirror the async ones — `IAccount` / `IAsyncAccount`,
205
+ `StealthTransfer` / `AsyncStealthTransfer`, `PendingTransaction` /
206
+ `AsyncPendingTransaction`, and so on. The sync tree is **generated** from
207
+ the async source by [`scripts/unasync.py`](scripts/unasync.py) and
208
+ committed; CI asserts the two trees stay byte-identical. Both ship in the
209
+ wheel.
210
+
211
+ ## Running the examples
212
+
213
+ The examples are self-contained — each generates fresh keys and faucets
214
+ its own funds against a LocalNet indexer:
215
+
216
+ ```bash
217
+ OOTLE_INDEXER_URL=http://localhost:12500 \
218
+ uv run python -m examples.fungible_transfer
219
+ ```
220
+
221
+ See [`examples/README.md`](examples/README.md) and
222
+ [`examples/stealth/README.md`](examples/stealth/README.md) for the full
223
+ catalogue and the few examples that need an external artifact (a deployed
224
+ template, a live component to watch, …).
225
+
226
+ ## Logging
227
+
228
+ Every internal module emits diagnostics under the `ootle.*` logger
229
+ namespace. The library installs a `NullHandler` on `ootle` so
230
+ unconfigured callers see nothing; opt in by configuring `logging`:
231
+
232
+ ```python
233
+ import logging
234
+ logging.basicConfig(level=logging.INFO)
235
+ logging.getLogger("ootle").setLevel(logging.DEBUG)
236
+ ```
237
+
238
+ Useful sub-namespaces:
239
+
240
+ - `ootle._async._transport` / `ootle._sync._transport` — request paths.
241
+ - `ootle._async._watcher` / `ootle._sync._watcher` — SSE open/close, fallbacks.
242
+ - `ootle._async._resolver` / `ootle._sync._resolver` — chunked substate fetches.
243
+ - `ootle._crypto._wasm_runtime` — WASM blob load + SHA-256 verification.
244
+ - `ootle.wallet` — multi-signer co-authorisation traces.
245
+
246
+ ## Scope
247
+
248
+ **In v1:** read-only queries · the public XTR faucet · public transfers
249
+ (`IAccount`) · multi-signer co-authorisation · the untyped component DSL
250
+ (`IComponent.call_function` / `call_method`) · dry-run fee estimation ·
251
+ transaction and component event watching · confidential **stealth
252
+ transfers** (revealed + stealth inputs/outputs, input-mask aggregation,
253
+ the faucet stealth path, and UTXO read helpers), all backed by the
254
+ vendored WASM blob.
255
+
256
+ **Not in v1:**
257
+
258
+ - **Typed templates / `ootle_template!`** — only the untyped
259
+ `IComponent` path ships; there is no codegen for typed bindings.
260
+ - **HD wallets, BIP-32, mnemonics** — caller-application concerns.
261
+ - **Borsh in Python** — all Borsh encoding/decoding is delegated to the
262
+ WASM blob, never reimplemented in Python.
263
+
264
+ ## Contributing
265
+
266
+ The engineering contract — including the **200-line file limit** and the
267
+ strict typing rules — is in [`CLAUDE.md`](CLAUDE.md).
268
+
269
+ ```bash
270
+ make sync # bootstrap the venv
271
+ make ci # the gate the pipeline runs
272
+ make unasync # regenerate src/ootle/_sync/ from _async/
273
+ ```
274
+
275
+ After any change under `src/ootle/_async/`, run `make unasync` and commit
276
+ the regenerated `_sync/` mirror in the same commit — `make ci` fails on
277
+ drift.
278
+
279
+ ## License & acknowledgements
280
+
281
+ Released under the **BSD 3-Clause** license — see [`LICENSE`](LICENSE).
282
+
283
+ The vendored WASM blob is built from
284
+ [`@tari-project/ootle-wasm`](https://www.npmjs.com/package/@tari-project/ootle-wasm)
285
+ and ships inside the wheel under
286
+ `src/ootle/_crypto/wasm/ootle_wasm_bg.wasm`, with its upstream version
287
+ and SHA-256 recorded in `VERSION` and verified at load.
@@ -0,0 +1,257 @@
1
+ # ootle
2
+
3
+ A modern, async-first **Python client library for the Tari L2 network**
4
+ (codename _Ootle_). It is the Pythonic counterpart to the Rust crate
5
+ `ootle-rs` and the JavaScript package `@tari-project/ootle-wasm`.
6
+
7
+ > **Status: pre-1.0.** The `0.x.y` API may shift; every breaking change
8
+ > is recorded in [`CHANGELOG.md`](CHANGELOG.md).
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install ootle-py
14
+ # or
15
+ uv add ootle-py
16
+ ```
17
+
18
+ The distribution name on PyPI is **`ootle-py`**; you import it as `ootle`:
19
+
20
+ ```python
21
+ from ootle import AsyncOotleClient, OotleClient
22
+ ```
23
+
24
+ Requirements: Python **>= 3.13**. The package is pure-Python; the Tari
25
+ WASM crypto blob ships inside the wheel — no native build step, and the
26
+ same `py3-none-any` wheel runs everywhere.
27
+
28
+ ## Quick start: read-only query
29
+
30
+ Connect to a LocalNet indexer and read an account balance. No wallet is
31
+ needed for read-only queries.
32
+
33
+ ```python
34
+ import asyncio
35
+ from ootle import (
36
+ AsyncOotleClient, ComponentAddress, Network,
37
+ ResourceAddress, TARI_TOKEN, default_indexer_url,
38
+ )
39
+
40
+
41
+ async def main() -> None:
42
+ account = ComponentAddress("component_...your-address...")
43
+ async with AsyncOotleClient.connect(default_indexer_url(Network.LOCAL_NET)) as client:
44
+ balance = await client.get_account_balance(account, ResourceAddress(TARI_TOKEN))
45
+ print(f"TARI balance: {balance}")
46
+ for resource, amount in (await client.get_account_balances(account)).items():
47
+ print(f" {resource}: {amount}")
48
+
49
+
50
+ asyncio.run(main())
51
+ ```
52
+
53
+ Full version: [`examples/balance_query.py`](examples/balance_query.py)
54
+ (and the sync mirror, [`examples/balance_query_sync.py`](examples/balance_query_sync.py)).
55
+
56
+ ## Quick start: public transfer
57
+
58
+ Faucet a fresh sender, then transfer TARI to a fresh recipient, watching
59
+ each transaction to finalisation. Note that `seal_transaction` is
60
+ **synchronous** — only the I/O calls are awaited.
61
+
62
+ ```python
63
+ import asyncio
64
+ from ootle import (
65
+ AsyncOotleClient, LocalSigner, Network, OotleSecretKey,
66
+ OotleWallet, ResourceAddress, TARI, TARI_TOKEN, default_indexer_url,
67
+ )
68
+
69
+
70
+ async def main() -> None:
71
+ sender = OotleSecretKey.random(Network.LOCAL_NET)
72
+ wallet = OotleWallet(default=LocalSigner(sender))
73
+ resource = ResourceAddress(TARI_TOKEN)
74
+ async with AsyncOotleClient.connect(
75
+ default_indexer_url(Network.LOCAL_NET), wallet=wallet
76
+ ) as client:
77
+ # Faucet the public XTR faucet's fixed dispense into the sender.
78
+ funded = await client.faucet().take_funds().pay_fee(500).prepare()
79
+ await (await client.send_transaction(client.seal_transaction(funded))).watch()
80
+
81
+ # Transfer 2 TARI to a fresh recipient.
82
+ recipient = OotleSecretKey.random(Network.LOCAL_NET).to_address()
83
+ unsigned = await (
84
+ client.account()
85
+ .pay_fee(1000)
86
+ .public_transfer(recipient, resource, 2 * TARI)
87
+ .prepare()
88
+ )
89
+ sealed = client.seal_transaction(unsigned)
90
+ await (await client.send_transaction(sealed)).watch()
91
+
92
+
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ Register extra signers with `wallet.register(LocalSigner(...))` and they
97
+ are folded in automatically at seal time (multi-signer co-authorisation);
98
+ `manual_co_signing.py` shows the explicit authorize → attach → seal
99
+ hand-off for remote-signer / HSM setups. Estimate fees first with
100
+ `await client.send_dry_run(unsigned)`. Full file:
101
+ [`examples/fungible_transfer.py`](examples/fungible_transfer.py).
102
+
103
+ ## Stealth (confidential) transfers
104
+
105
+ Confidential transfers run **entirely on the vendored `ootle-wasm`
106
+ blob** — the default crypto provider. Bulletproofs, ElGamal viewable
107
+ balances, balance-proof signatures, and input-mask aggregation all
108
+ execute locally; **no wallet daemon or external service is required.**
109
+
110
+ Build the transfer, hydrate the balance proof with an
111
+ `AsyncWalletStealthAuthorizer`, then seal with the wallet's default
112
+ signer:
113
+
114
+ ```python
115
+ import asyncio
116
+ from ootle import (
117
+ AsyncOotleClient, AsyncStealthTransfer, AsyncWalletStealthAuthorizer,
118
+ LocalSigner, Network, OotleSecretKey, OotleWallet, Output,
119
+ ResourceAddress, TARI, TARI_TOKEN, default_indexer_url,
120
+ )
121
+
122
+
123
+ async def main() -> None:
124
+ sender = OotleSecretKey.random(Network.LOCAL_NET)
125
+ wallet = OotleWallet(default=LocalSigner(sender))
126
+ resource = ResourceAddress(TARI_TOKEN)
127
+ recipient = OotleSecretKey.random(Network.LOCAL_NET).to_address()
128
+ async with AsyncOotleClient.connect(
129
+ default_indexer_url(Network.LOCAL_NET), wallet=wallet
130
+ ) as client:
131
+ # ... faucet the sender first (see examples/stealth/) ...
132
+ transfer = AsyncStealthTransfer(client, resource)
133
+ transfer.spend_revealed_input(sender.to_address().to_component_address(), 5 * TARI)
134
+ transfer.to_stealth_output(
135
+ Output(destination=recipient, amount=4 * TARI, resource_address=resource)
136
+ )
137
+ transfer.to_revealed_output(TARI)
138
+ transfer.pay_fee_from_revealed(500)
139
+
140
+ spec = await transfer.prepare()
141
+ authorizer = AsyncWalletStealthAuthorizer(
142
+ wallet, spec, view_secret=sender.view_secret
143
+ )
144
+ hydrated = await authorizer.prepare(client)
145
+ sealed = client.seal_transaction(hydrated.unsigned)
146
+ await (await client.send_transaction(sealed)).watch()
147
+
148
+
149
+ asyncio.run(main())
150
+ ```
151
+
152
+ Stealth supports revealed and stealth inputs and outputs, mixed in one
153
+ transfer: `spend_revealed_input` / `spend_stealth_input` (with input-mask
154
+ aggregation) feed `to_stealth_output` / `to_revealed_output`, with fees
155
+ paid from a revealed bucket (`pay_fee_from_revealed`) or a stealth
156
+ account's revealed vault (`pay_fee_from_stealth`). The public faucet has
157
+ a stealth path too (`IFaucet.take_funds_stealth`), and owned UTXOs are
158
+ read back with `AsyncOotleClient.decrypt_owned_utxo` (AEAD owner-read).
159
+
160
+ The full set of runnable stealth examples — faucet deposit,
161
+ stealth↔revealed, stealth↔stealth, spending an owned UTXO, and the sync
162
+ mirror — lives in [`examples/stealth/`](examples/stealth/README.md).
163
+
164
+ ## Sync vs. async
165
+
166
+ Every async API has a sync mirror with the same shape. Swap the imports
167
+ and drop the `await`s:
168
+
169
+ ```python
170
+ from ootle import OotleClient # sync
171
+ from ootle import AsyncOotleClient # async
172
+ ```
173
+
174
+ The sync names mirror the async ones — `IAccount` / `IAsyncAccount`,
175
+ `StealthTransfer` / `AsyncStealthTransfer`, `PendingTransaction` /
176
+ `AsyncPendingTransaction`, and so on. The sync tree is **generated** from
177
+ the async source by [`scripts/unasync.py`](scripts/unasync.py) and
178
+ committed; CI asserts the two trees stay byte-identical. Both ship in the
179
+ wheel.
180
+
181
+ ## Running the examples
182
+
183
+ The examples are self-contained — each generates fresh keys and faucets
184
+ its own funds against a LocalNet indexer:
185
+
186
+ ```bash
187
+ OOTLE_INDEXER_URL=http://localhost:12500 \
188
+ uv run python -m examples.fungible_transfer
189
+ ```
190
+
191
+ See [`examples/README.md`](examples/README.md) and
192
+ [`examples/stealth/README.md`](examples/stealth/README.md) for the full
193
+ catalogue and the few examples that need an external artifact (a deployed
194
+ template, a live component to watch, …).
195
+
196
+ ## Logging
197
+
198
+ Every internal module emits diagnostics under the `ootle.*` logger
199
+ namespace. The library installs a `NullHandler` on `ootle` so
200
+ unconfigured callers see nothing; opt in by configuring `logging`:
201
+
202
+ ```python
203
+ import logging
204
+ logging.basicConfig(level=logging.INFO)
205
+ logging.getLogger("ootle").setLevel(logging.DEBUG)
206
+ ```
207
+
208
+ Useful sub-namespaces:
209
+
210
+ - `ootle._async._transport` / `ootle._sync._transport` — request paths.
211
+ - `ootle._async._watcher` / `ootle._sync._watcher` — SSE open/close, fallbacks.
212
+ - `ootle._async._resolver` / `ootle._sync._resolver` — chunked substate fetches.
213
+ - `ootle._crypto._wasm_runtime` — WASM blob load + SHA-256 verification.
214
+ - `ootle.wallet` — multi-signer co-authorisation traces.
215
+
216
+ ## Scope
217
+
218
+ **In v1:** read-only queries · the public XTR faucet · public transfers
219
+ (`IAccount`) · multi-signer co-authorisation · the untyped component DSL
220
+ (`IComponent.call_function` / `call_method`) · dry-run fee estimation ·
221
+ transaction and component event watching · confidential **stealth
222
+ transfers** (revealed + stealth inputs/outputs, input-mask aggregation,
223
+ the faucet stealth path, and UTXO read helpers), all backed by the
224
+ vendored WASM blob.
225
+
226
+ **Not in v1:**
227
+
228
+ - **Typed templates / `ootle_template!`** — only the untyped
229
+ `IComponent` path ships; there is no codegen for typed bindings.
230
+ - **HD wallets, BIP-32, mnemonics** — caller-application concerns.
231
+ - **Borsh in Python** — all Borsh encoding/decoding is delegated to the
232
+ WASM blob, never reimplemented in Python.
233
+
234
+ ## Contributing
235
+
236
+ The engineering contract — including the **200-line file limit** and the
237
+ strict typing rules — is in [`CLAUDE.md`](CLAUDE.md).
238
+
239
+ ```bash
240
+ make sync # bootstrap the venv
241
+ make ci # the gate the pipeline runs
242
+ make unasync # regenerate src/ootle/_sync/ from _async/
243
+ ```
244
+
245
+ After any change under `src/ootle/_async/`, run `make unasync` and commit
246
+ the regenerated `_sync/` mirror in the same commit — `make ci` fails on
247
+ drift.
248
+
249
+ ## License & acknowledgements
250
+
251
+ Released under the **BSD 3-Clause** license — see [`LICENSE`](LICENSE).
252
+
253
+ The vendored WASM blob is built from
254
+ [`@tari-project/ootle-wasm`](https://www.npmjs.com/package/@tari-project/ootle-wasm)
255
+ and ships inside the wheel under
256
+ `src/ootle/_crypto/wasm/ootle_wasm_bg.wasm`, with its upstream version
257
+ and SHA-256 recorded in `VERSION` and verified at load.