Web3.py-style facade (Aleo/AsyncAleo) + restore dropped testnet#45
Open
iamalwaysuncomfortable wants to merge 37 commits into
Open
Web3.py-style facade (Aleo/AsyncAleo) + restore dropped testnet#45iamalwaysuncomfortable wants to merge 37 commits into
iamalwaysuncomfortable wants to merge 37 commits into
Conversation
PR #44 was squash-merged to master without the testnet work (testnet.py, _aleolib_testnet.pyi, build-both.sh, merge_testnet_so.py, AGENTS.md, the network-aware client changes). This restores that bundle on top of master so the two-build (mainnet+testnet) packaging ships, matching the migration branch tip e897fd1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the F1 skeleton for the Web3.py-style facade:
- sdk/python/aleo/facade/{__init__,client,provider,errors}.py
- sdk/python/aleo/_facade_common.py (shared unit-conversion helpers)
- sdk/python/tests/test_facade_client.py (43 mocked tests, all green)
- Exports Aleo, HTTPProvider, AleoError + subclasses from aleo/__init__.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… all Move AleoError into _client_common (lowest layer); AleoNetworkError, AleoProvingError, and the RecordScanner errors now subclass it. Existing 'except AleoNetworkError' sites are unaffected (still subclasses). Corrects the F1 review finding that the internal errors were siblings, not subclasses, silently breaking the documented single-except contract; fixes the false 'conversion helpers' comment. is_connected keeps a broad catch (reachability probe, web3.py semantics). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds AccountModule attached to the Aleo client as aleo.account, with: - create() / from_private_key(s) / from_seed(field) account factories - export_encrypted / import_encrypted via PrivateKeyCiphertext - sign(msg, acct) / verify(addr, msg, sig) — raw-bytes signing - sign_value(value, acct) / verify_value(addr, value, sig) — structured Aleo Value signing - default_account fallback on sign/sign_value when account arg is omitted - No recover/signer-from-signature verb (privacy stance) 33 new offline tests in test_facade_account.py; suite 648 → 681 passed; pyright strict 0 errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…port network-agnostic Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g reads, ABI Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… dedup 404 mapping; bool-rejection regression test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ransaction/transact/delegate) + result inspection Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uto-sourcing seam Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nces; document single-fee-record path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sync; make input_type_name public Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le quickstart example; snarkvm 4.8.1 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/verify_value Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…), route decode-by-id through network (404→TransactionNotFound), get_transaction_object passthrough
…es in __init__.pyi; type functions.<fn>() as BoundCall - Guard the eager `from . import testnet` so mainnet-only builds (CI proving/network + aleo-abi lanes) can `import aleo` (was ModuleNotFoundError: aleo._aleolib_testnet). - Mirror __init__.py's re-export block into __init__.pyi so pyright resolves `from aleo import Aleo/AsyncAleo/HTTPProvider/AleoError/...` (fixes lint lane on examples/facade_quickstart.py). - Annotate _FunctionCaller.__call__ -> BoundCall (via TYPE_CHECKING) so the verb ladder (simulate/transact/delegate) resolves without isinstance narrowing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ip cleanly test_testnet.py does a direct `import aleo.testnet`, which bypasses the __init__.py guard and hard-fails collection on mainnet-only builds (proving/network lane runs -m slow but pytest imports the module at collection regardless of markers). Skip the module when the extension isn't built. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…anes Previously only the OS-matrix lanes built the testnet extension; the proving/network and aleo-abi lanes were mainnet-only. Build the merged two-network wheel in those lanes too (deps are cached, so the second feature build is a warm recompile) and run test_testnet.py explicitly — in the proving lane it's otherwise deselected by -m slow. Now testnet is exercised in every test lane. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…linux build) The manylinux build runs maturin in a Docker container that writes sdk/dist as root; the host-side merge_testnet_so.py step then hit PermissionError creating the .whl.tmp. Reclaim ownership (Linux only) before repacking. macOS/Windows build natively and are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kpandl
approved these changes
Jul 10, 2026
…ivate roundtrip) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…LEO_E2E_*) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… LocalRecordScanner); ALEO_E2E_* vars; snarkvm 4.8.1 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…te gets priority_fee only The SDK's execution_cost can undershoot a node's required base (storage) fee (devnode fee-schedule skew). Add a base_fee override to the sync+async verb ladder + _authorize_fee so callers can overpay; devnode e2e now overpay via DEVNODE_OVERPAY_BASE_FEE and the transact tests pass against a real devnode. delegate never overrides base_fee — only priority_fee (sync+async). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…scanner) transfer_public_to_private (delegate) -> hosted-scanner discovery (polled) -> transfer_private (delegate). Env-gated on ALEO_E2E_* (DPS + scanner creds); skips cleanly without them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t was using mainnet types) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ing (sync+async) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ecord correct paths + SDK drift Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, network-correctness guard) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fast lane excludes slow/live/devnode The live object suite + creds-gated testnet e2e are marked `live` (not `slow`), so no CI lane runs them (fast lanes filter not-slow-and-not-live-and-not-devnode; the proving lane runs -m slow). They run only locally via -m live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Back to snarkvm v4.8.1 (the consensus-heights/set_consensus_version_heights approach didn't unblock the devnode private roundtrip — the vanishing-polynomial is deeper — and pinned an unmerged snarkvm branch). Remove the stale set_consensus_version_heights .pyi stubs. Delete LocalRecordScanner: the private roundtrip lives on testnet with the hosted scanner (aleo.records), so the client-side scanner is unused. Scrub docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /pubkey handshake mints an ephemeral X25519 key held ONLY on the prover backend that served it, so the follow-up /prove POST must stick to that same backend via its LB affinity cookie. Both clients hand-built a Cookie header from `set-cookie`, which (a) comma-joins multiple Set-Cookie values, (b) carries attributes (Path/Secure/...), and (c) makes requests/httpx SKIP the cookie jar — silently dropping affinity when the LB sets more than one cookie. Rely on the persistent jar instead: sync forwards the parsed `pk_resp.cookies` via the `cookies=` kwarg (covers the custom-transport path that bypasses `self._session`); async relies on the AsyncClient jar (which wraps custom transports at the client level). Adds an async cookie-affinity test mirroring the sync one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Default ALEO_E2E_ENDPOINT to https://api.provable.com/v2 (JWT origin derives correctly from it; reads resolve under /v2/{network}). - Transfer 1 microcredit to self (a valid recipient) instead of a fabricated burn address whose checksum failed Value.parse. - Gate the delegated-proving tests on ALEO_E2E_PROVER_URI: the prover is a distinct service host from the read/JWT API, so there is no sensible fallback — the pubkey/prove handshake 404s against the read node. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The delegated-proving handshake is a Provable *service* at the API origin under
the /prove/{network} prefix (sibling to /scanner) — e.g.
https://api.provable.com/prove/testnet/pubkey — NOT the read node's
/v2/{network} base (which 404s) nor the bare origin. Default the prover base to
{jwt_origin(base_url)}/prove/{network} so the handshake resolves to
{base}/pubkey and {base}/prove/authorization. An explicit HTTPProvider(prover_uri=)
override still wins.
Verified end-to-end: test_delegate_transfer_public_live now PASSES against live
testnet (real JWT + /pubkey affinity cookie + delegated proof). Also drops the
ALEO_E2E_PROVER_URI gate (the default now derives correctly) and corrects the
AGENTS.md DPS section (wrong host + stale drift note).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dual-network live e2e
Scanner base now derives from the endpoint origin under /scanner (a Provable
service sibling to /prove and /jwts) — {origin}/scanner/{network} — instead of
the read node's /v2/{network} base (which 404s). HTTPProvider gains a
consumer_id field; api_key + consumer_id flow through the one provider to BOTH
the delegated prover and the hosted scanner, so a user configures only
https://api.provable.com — reads, proving, and scanning all derive from its
origin. The scanner's own /pubkey→register/encrypted handshake keeps affinity
via its requests.Session / httpx.AsyncClient cookie jar.
Live e2e (tests/e2e/test_live_e2e.py, renamed from test_testnet_e2e.py) is now
parametrised over testnet AND mainnet (one at a time). Verified live:
delegate_transfer_public and hosted_record_scanner PASS on both networks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Web3.py-style facade for the Aleo Python SDK (+ restore dropped testnet)
Adds a high-level, Web3.py-shaped facade over the existing PyO3 bindings so
Python developers get an ergonomic
Aleo(provider)/AsyncAleo(provider)client instead of driving the low-level
Process/authorize/execute/provesequence by hand. Built phase-by-phase (F1–F8) with a review gate after each
phase and a final whole-branch review.
Also restores testnet
The squash-merge of #44 landed on
masterwithout the testnet bundle(
testnet.py,_aleolib_testnet.pyi,build-both.sh,merge_testnet_so.py,AGENTS.md, the network-aware client changes). The first commit here restoresit, so the two-build (mainnet and testnet) packaging ships and the facade's
network-aware dispatch works.
What's in the facade
Aleo(provider)client —HTTPProvider,is_connected,get_balance,unit helpers,
is_valid_address,network_id/network_name, lazyprocess,escape hatches (
.network_client,.process,.raw).AleoError(
TransactionNotFound,ProgramNotFound,ExecutionError,TransactionConfirmationTimeout, …); oneexcept AleoErrorcatches all.aleo.account—create/from_private_key/from_seed, encryptedimport/export,
sign/verify(bytes) andsign_value/verify_value(structured Aleo values). No signer-recovery verb (privacy stance).
aleo.network— typed pass-throughs over the network client;submit_transaction+send_raw_transactionalias;wait_for_transaction.aleo.programs—get(id)→ boundProgramwith a dynamic.functionsnamespace built from the program's real function set(web3.py's ABI-driven
contract.functionsanalog), input coercion againstdeclared types, mapping reads, and ABI generation (local + from the web).
simulate/call/authorize→Authorization;build_transaction→Transaction;transact→ tx id;delegate→delegated-proving result.
delegatedefaults to the prover's fee masterpaying (no fee funding needed) — the frictionless flagship path. Result
inspection via
.outputs/.decoded();aleo.decode_transition.aleo.records+RecordProvider— record scanning/discovery over theRecordScanner; auto-sources a private-fee record;
aleo.record_providerisswappable (self-hosted scanner escape hatch). Docstrings note the
view-key-sharing tradeoff of delegated scanning.
AsyncAleo— async mirror over the async network client + record scanner;shares all pure orchestration with the sync path; blocking
Processwork runsin
asyncio.to_thread.sdk/python/examples/facade_quickstart.py.Testing
pyrightstrict = 0. Network/proving paths arebehind
@pytest.mark.slow(deselected by default); the delegated-proving pathwas validated live against the real prover earlier in development.
binding, fee-master default) verified correct on both sync and async rungs.
No Rust changes — pure Python over the existing bindings.