Skip to content

Migrate SDK to snarkvm v4.8.1 (MainnetV0) with pytest KAT suite#44

Open
iamalwaysuncomfortable wants to merge 70 commits into
masterfrom
feat/snarkvm-4.7-migration
Open

Migrate SDK to snarkvm v4.8.1 (MainnetV0) with pytest KAT suite#44
iamalwaysuncomfortable wants to merge 70 commits into
masterfrom
feat/snarkvm-4.7-migration

Conversation

@iamalwaysuncomfortable

Copy link
Copy Markdown
Member

Summary

Migrates the aleo PyO3 crate from snarkvm 0.16.6 (Testnet3) to v4.8.1 (MainnetV0), replaces the ad-hoc test.py with a pytest suite validated against TS-SDK known-answer vectors, and restructures the package for per-network builds.

Architecture

  • Two-build model, no macro: flat single-network wrapper code; CurrentNetwork/CurrentAleo selected by Cargo feature (mainnet default / testnet). Each build emits a distinct pymodule (_aleolib_mainnet / _aleolib_testnet); Python namespaces them as aleo.mainnet / aleo.testnet (mainnet ships now, testnet is a build config away — zero source change).
  • snarkvm v4.8.1 via git tag (not yet on crates.io; latest published is 4.7.3), features: console, circuit, synthesizer, ledger, utilities, algorithms, parameters. rand 0.8→0.10 (required by snarkvm's RNG bounds).
  • coinbase/puzzle module dropped (removed upstream in 4.x).
  • Pythonic (anti-viem) surface: property getters (pk.address, record.owner/nonce/version), explicit PrivateKey.random() (no implicit-RNG constructor), dunders over to_string()/equals(), snake_case throughout; .pyi stubs retargeted to match.

Breaking API changes (from upstream 4.x)

  • Network.edition() removed (editions are per-program in 4.x)
  • RecordPlaintext.serial_number() requires a new record_view_key: Field arg
  • Transition.is_bond()/is_unbond()is_bond_public()/is_unbond_public()
  • Fee authorization: (base_fee: int, priority_fee: int, deployment_or_execution_id: Field) — priority fee now required
  • execution_cost returns (total, (storage, finalize)) as plain ints
  • verify_execution/verify_fee pin ConsensusVersion::V17/VarunaVersion::V2/InclusionVersion::V1 (current network tip; historical executions proven under older rules may be rejected — follow-up planned to parameterize)
  • Query.rest() validates the URL and can raise

Tests

sdk/python/tests (pytest, wired into CI replacing python3 test.py):

  • Known-answer tests against vendored ProvableHQ/sdk vectors (tests/vectors/*.json, source commit recorded): private key → view key → address triples; record ciphertext ownership + decryption (owner/nonce/microcredits/version asserted from actual decrypted plaintext)
  • Round-trips: sign/verify (incl. tampered-message negative), field/group parse↔serialize, field arithmetic, encryptor encrypt↔decrypt (incl. wrong-secret negative)
  • Programs smoke: credits.aleo parse + a real transfer_public authorization built and JSON round-tripped (offline, ~6s)

11/11 passing. maturin develop --features mainnet clean (0 crate warnings); testnet cfg builds.

Test plan

  • cd sdk && maturin develop --features mainnet && python -m pytest python/tests -v → 11/11
  • KAT vectors reproduce TS-SDK derivations exactly
  • cargo build --no-default-features --features testnet links
  • CircleCI green on this PR

🤖 Generated with Claude Code

iamalwaysuncomfortable and others added 28 commits July 8, 2026 14:29
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…3 paths

Replace hardcoded Testnet3 with cfg-selected MainnetV0/TestnetV0 network types.
Update imports to use 4.7.3 API paths: ledger::{block, query, store}, synthesizer for
Process and Trace. Remove deprecated coinbase module imports. Add feature-gated
AleoTestnetV0 import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lasses

Replaces single _aleolib pymodule with two cfg-gated variants:
- _aleolib_mainnet (default, no testnet feature)
- _aleolib_testnet (when testnet feature enabled)

Both delegate to shared register() function. Removes coinbase module
and drops CoinbasePuzzle, CoinbaseVerifyingKey, EpochChallenge, and
ProverSolution class registrations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace rand 0.8 Standard distribution sampling with snarkvm 4.7.3's
Uniform::rand API (rand 0.10). Upgrade sdk rand dep to 0.10 to align
with snarkvm's transitive rand version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The EDITION constant was removed in snarkvm 4.7.3. Replace with hardcoded 0 value.
Credits module requires no changes (pure native types).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…getters, random())

- Replace StdRng::from_entropy() with rand::make_rng::<StdRng>() (rand 0.10)
- Rename #[new] fn new() -> #[staticmethod] fn random() on PrivateKey and Account
- Add Record::version() getter (U8<N> deref'd to machine u8)
- Add #[getter] to pure accessors: PrivateKey::{address,view_key,compute_key,seed}, Signature::{challenge,response,compute_key}, ViewKey::to_address, ComputeKey::address, Account::{private_key,view_key,address}, RecordPlaintext::{owner,nonce}
- Fix Plaintext::Struct/Array variant to use std::sync::OnceLock (was OnceCell)
- Update serial_number() to accept record_view_key for new to_commitment() 3-arg signature

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Fix Transition: rename is_bond() → is_bond_public(), is_unbond() → is_unbond_public()
- Add #[getter] to ProgramID::name, network; Locator::program_id, name, network, resource; Program::id, functions, imports, source; Transition::id, program_id, function_name; Execution::execution_id; Response::outputs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…flow

- query.rs: QueryNative::try_from(url) replaces removed From<String>
- trace.rs: prove_execution/prove_fee take VarunaVersion::V2; prepare takes &QueryNative; rng via rand::make_rng
- process.rs: interior-mutability receivers (&self) for add_program/insert_*_key; add_program routes through Process::lock(); fee authorize uses (base_fee, priority_fee, id) plain-u64 signature; execution_cost uses free fn with ConsensusVersion::V15; verify_execution/verify_fee take (ConsensusVersion, VarunaVersion, InclusionVersion); ProcessAuthError/ProcessExecError mapped to anyhow
- fee.rs: payer/transition become #[getter]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Create sdk/python/aleo/mainnet.py with wildcard re-export from _aleolib_mainnet
- Update sdk/pyproject.toml module-name from aleo._aleolib to aleo._aleolib_mainnet
- Update sdk/python/aleo/__init__.py to import mainnet submodule (no star import)
- Update sdk/python/aleo/encryptor.py to import from .mainnet instead of top-level .
- Update sdk/.gitignore to cover all *.abi3.so build artifacts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Historical ciphertext from records.json (sdk@543b41e) parsed and
decrypted successfully under snarkvm 4.7.3 — KAT path taken, no
fallback needed.

Assertions:
- is_owner(vk) True / is_owner(non_owner_vk) False
- pt.owner (stripped of .private qualifier) == vector owner address
- pt.nonce == vector nonce
- pt.version == 0 (v0-era record; snarkvm 4.7.3 exposes _version=0u8)
- f"{microcredits}u64" in str(pt) — checks decrypted plaintext content
  against the vector value, not just vector-internal consistency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Delete sdk/python/test.py (legacy ad-hoc unittest referencing removed APIs)
- git mv _aleolib.pyi -> _aleolib_mainnet.pyi; add mainnet.pyi re-export stub
- Drop coinbase symbols (CoinbasePuzzle, CoinbaseVerifyingKey, EpochChallenge, ProverSolution) from stubs
- Apply API deltas: random() staticmethods, property accessors, renamed is_bond_public/is_unbond_public, updated authorize_fee signatures, Trace.prove_execution(locator: str), Network.edition removed
- CI: maturin develop --features mainnet; replace python3 test.py with pip install pytest + python -m pytest python/tests -v; fix double-cd (working_directory already in sdk/)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ViewKey: rename #[getter] fn to_address → fn address so Python is vk.address
- Stubs: update ViewKey.to_address → @Property address in both .pyi files
- Stubs: replace Optional[...] with X | None in __init__.pyi; drop unused Optional import
- lib.rs: add #![allow(non_local_definitions)] for pyo3 0.20 / newer rustc compat

Suite: 9/9 passed; zero aleo-crate compiler warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…add encryptor tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ypes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- snarkvm moves from crates.io 4.7.3 to git tag v4.8.1 (not yet published)
- ConsensusVersion pins bumped V15 -> V17 (4.8.x added V16/V17) in
  verify_execution, verify_fee, and execution_cost
- add .cargo/config.toml so plain cargo builds link pyo3 extension
  modules on macOS (maturin injects these flags itself)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n assembly, serial numbers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…stubs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ency

- sdk.yml: three parallel lanes — lint (fmt/clippy/pyright + testnet cfg
  check), fast pytest matrix on linux/macos/windows with pytest-xdist,
  and a proving job with a cached ~/.aleo parameter store
- remove unused openssl 0.10 dependency and the openssl/vendored flags
  from the wheels workflow (never referenced in src/)
- fix pyright reportUnusedImport on the aleo.mainnet re-export

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@iamalwaysuncomfortable iamalwaysuncomfortable force-pushed the feat/snarkvm-4.7-migration branch from e374825 to 5acd697 Compare July 8, 2026 19:30
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iamalwaysuncomfortable and others added 30 commits July 9, 2026 12:14
… exceptions

Replace catch_unwind/wrap_overflow with primitive checked_add/sub/mul/div/pow
and guarded rem/rem_wrapped; division and remainder by zero now raise
ZeroDivisionError, other failures raise OverflowError without any panic.
Fix vacuous Field.random test and add rem zero-divisor + I8 MIN%-1 pinned tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… hardening

- metadata.rs: refactor make_metadata to accept separate prover_meta and
  verifier_meta params; always read prover_checksum from each respective
  metadata JSON (mirrors wasm reference mechanics exactly). Update all 15
  static constructors to pass both XxxProver::METADATA and
  XxxVerifier::METADATA. Drop the now-unused verifier_checksum helper.
- offline_query.rs: replace .unwrap() with .expect("OfflineQuery
  serialization failed") in __str__ per codebase convention.
- dynamic_record.rs: use direct PartialEq comparison (self.0 == other.0)
  since DynamicRecordNative implements std PartialEq; remove string-form
  workaround.
- test_program_keys.py: add regex assertion for verifier filename format
  (bond_public.verifier.<7 hex chars>).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t_record_members, get_struct_members, address

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…shapes)

- test_program_get_record_members: assert microcredits member has type u64 and visibility private; _nonce has type group and visibility public
- test_program_get_struct_members: assert full dict shape with exactly {name, type} keys; verify x and y types are u32
- test_program_get_mappings: assert account mapping has key_type address and value_type u64
- test_program_get_function_inputs_record: new test for transfer_private record input path, verifying 3 inputs, record structure with microcredits/nonce members, and address/u64 field inputs

All assertions now match exact dict shapes from Rust implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 16 is_*_verifier checkers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eliminate DRY violation by reusing the read_prover_checksum function from
metadata.rs instead of duplicating it in proving_key.rs. All 30 checker call
sites (15 checkers x 2 cfg branches) now use the shared implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h_transition_info

Implements two decrypt methods on the Ciphertext pyclass in sdk/src/account/text.rs,
mirroring the wasm SDK's key derivation: compute_function_id + hash_psd4([fn_id, tvk, index]).
Tests use a self-generated MainnetV0 KAT (network ID 0) since the wasm KAT was testnet (ID 1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
….decrypt_transition

Add test_ciphertext_decrypt_cross_validates_with_transition_decrypt to
test_program_keys.py.  The existing Task 9 KAT tests only prove that
decrypt_with_transition_view_key decrypts its own self-generated output;
a shared derivation bug (e.g. wrong field order in hash_psd4) would not
be caught.  The new test exercises both paths against the vendored
hello_hello.aleo/main mainnet fixture (private input index 1 = "2u32"),
where Transition.decrypt_transition was independently tested in
test_chain_data.py, ensuring that any per-path derivation regression is
caught.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ery, Program/VK/PK/Ciphertext additions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…DynamicRecord)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…03 retry

- .gitignore had '*.pkl.idea/' concatenation (both patterns dead)
- test naming: 15 credits verifiers, not 16
- proving tests: api.explorer.provable.com/v2 (verified live) + 3-attempt
  retry around trace.prepare for transient API 503s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure-Python AleoNetworkClient (requests) and AsyncAleoNetworkClient (httpx)
mirroring TS SDK semantics: block/program/transaction endpoints, JWT refresh,
DPS sealed-box encryption via PyNaCl, retry on 5xx, wait_for_confirmation.
Shared logic in _client_common.py; security helpers in security.py.
519 tests pass (+62); pyright 0 errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…outing tests

- pyproject.toml: move dependencies=["requests>=2"] from invalid
  [project.dependencies] table to correct array under [project]
- network_client.py: wire transport param via _http() helper that routes
  all HTTP through callable transport when provided; document contract
- async_network_client.py: pass transport to httpx.AsyncClient(transport=...)
  in __init__; add 5 missing async methods (get_program_imports,
  get_program_import_names, get_program_object, get_program_mapping_plaintext,
  get_transaction_object) with async DFS _collect_program_imports
- _client_common.py: TypeVar'd retry_with_backoff signature Callable[[], T]->T
- test_network_client.py: remove @pytest.mark.slow + blanket except/skip from
  DPS tests; add Request-variant routing test (prove/request endpoint); add
  sync transport callable test; assert mock called once in block_range_50 test
- test_network_client_async.py: remove object.__new__ bypass, use __init__
  with MockTransport; add transport-wiring tests; add async parity tests for
  5 new methods; add Request-variant async DPS routing test
- Both: URL-encode commitments in get_state_paths via urllib.parse.quote

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…saction-object test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds sdk-abi/, a standalone maturin/PyO3 package (aleo-abi) that wraps
leo-abi@ba2c017 to expose generate_abi() and check_compatibility() to
Python. Also wires a lazy aleo.abi integration module into the main SDK
and extends sdk-wheels.yml with build-abi / sdist-abi jobs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion canary

- new test-abi job: builds both wheels, runs the aleo-abi KAT suite and
  the main-package hook tests for real, and asserts sdk/Cargo.lock never
  references leo's dev_skip_checks feature (build-graph isolation canary)
- hook tests skip gracefully where aleo-abi isn't installed (3-OS matrix)
- abi wheel jobs gated on the tests workflow like the main wheels

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e imports

The aleo package now genuinely depends on requests (PEP 621 fix), so
--no-index wheel installs broke every lane. Allow dependency resolution,
add the new client test deps to test lanes, and give the pyright action
an environment that can resolve requests/httpx/nacl imports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ptional import)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rty boundaries

pynacl/httpx are optional deps and aleo_abi is a separate package; strict
pyright's reportUnknownMemberType cascades from those boundary calls. Relax
only those three rules in the four glue modules; the ~40 core binding
modules stay fully strict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dirs

The legacy root setup.cfg injects --numprocesses/--timeout addopts
(needs xdist/timeout plugins) into any pytest run from repo root. Running
each suite from inside its package dir makes pytest use that package's
pytest.ini instead, so the test-abi lane needs no extra plugins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant