Skip to main content

Portable, offline-verifiable transaction evidence for Algorand and Voi, anchored in state proofs

Project description

avm-proofpack

Portable, offline-verifiable transaction evidence for Algorand and Voi, anchored in state proofs.

A proofpack bundle is a single JSON file binding a specific transaction to a chain's state proof commitments. Capture it once, while public nodes still hold the round, and anyone can re-check it forever, offline, with no node and no SDK: the verifier recomputes every hash and Merkle link from raw bytes and confirms the transaction is committed by the bundle's state proof message.

Read the trust model before you rely on it. Offline verification proves internal consistency down to one trust root: the bundle's own state proof message. That message is not cryptographically verified offline in this release, so offline-alone does not prove the message was genuinely produced by the chain's consensus stake. Raising that trust root is a separate, explicit step, --corroborate compares the message against independent nodes today; Falcon signature verification of the state proof (removing node trust entirely) is the planned next tier. Treat a structure-only result as "this bundle is internally consistent", not "consensus attests this". See SPEC.md section 6.

Zero runtime dependencies. Pure Python. Works unchanged on any AVM chain that generates state proofs (verified live against Algorand mainnet and Voi mainnet).

Why

Payment processors, custodians and auditors routinely assert "transaction X settled on chain Y" and back it with nothing but their own signature. Algorand and Voi commit, every 256 rounds, to a stake-signed vector commitment over their block headers, which makes such claims falsifiable: evidence can be checked against consensus itself rather than against the claimant.

Two facts shape the design:

  1. Public nodes prune. Non-archival nodes serve proof material for roughly the most recent 1,000 rounds only. Evidence must therefore be captured near settlement time and stored; it cannot be fetched years later on demand.
  2. Verification must not require trusting the capturer. A bundle carries raw canonical bytes, not quoted values; the verifier recomputes every hash and Merkle link itself.

Install

pip install avm-proofpack

(Or vendor the src/avm_proofpack directory; it is stdlib-only by design.)

Usage

Capture evidence for a confirmed transaction (any algod endpoint):

avm-proofpack capture \
  --node https://mainnet-api.algonode.cloud \
  --txid KOERHNGJPLUUQK4WJ4YPY5OC3BOINDH2J4E6HM2RYARSJ5R3FAYA \
  --round 63604900 \
  -o evidence.json

A state proof covers a round only after its 256-round interval closes (roughly 12 to 15 minutes); pass --wait 1200 to poll until then.

Verify it later. Structure-only verification is fully offline but reports the trust root as unattested and exits non-zero unless you either corroborate or explicitly accept unattested evidence:

avm-proofpack verify evidence.json --accept-unattested

Pin the chain and corroborate the trust root against independent nodes:

avm-proofpack verify evidence.json \
  --genesis-hash wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= \
  --corroborate https://mainnet-api.4160.nodely.dev,https://mainnet-api.algonode.cloud

Or pin by registry name (algorand-mainnet, algorand-testnet, voi-mainnet; mutually exclusive with --genesis-hash), letting --corroborate default expand to the chain's default node list:

avm-proofpack verify evidence.json --chain algorand-mainnet --corroborate default

Extract the payment facts of the proven transaction (same trust-root gating as verify; stdout is one JSON object):

avm-proofpack extract evidence.json --chain algorand-mainnet --corroborate default

Extraction covers the top-level transaction only: for appl transactions the output states explicitly that inner-transaction effects are not proven by a v1 bundle (SPEC.md section 6).

From Python:

from avm_proofpack import Bundle, capture, extract_payment, verify_bundle

bundle_dict = capture("https://mainnet-api.voi.nodely.io", txid, round_number)
bundle = Bundle.from_dict(bundle_dict)
report = verify_bundle(bundle)
print(report.checks)

facts = extract_payment(bundle, report=report)  # or omit report: verifies itself
print(facts.txn_type, facts.sender, facts.receiver, facts.amount)

extract_payment refuses to read facts out of unverified bytes: it either verifies the bundle itself or cross-checks the supplied report against the bundle. matches_reference closes the hashed-receipt privacy loop (docs/INTEGRATION.md): it recomputes e.g. sha256("{tenant}:{txid}:{network}") from the verified txid and compares in constant time.

What verification proves

Offline, from the bundle's raw bytes alone, the verifier re-derives:

  1. the transaction id (both its SHA-512/256 and SHA-256 forms) from the canonical transaction bytes, binding the claimed txid to the evidence;
  2. the transaction Merkle leaf and its membership in the block's SHA-256 transaction commitment;
  3. the block hash, the light block header encoding, and its membership in the state proof's block-headers commitment.

The remaining trust root is the state proof message itself. --corroborate reduces that from "the capturing node was honest" to "these independent nodes do not all collude". Cryptographic verification of the state proof's Falcon signatures (removing node trust entirely) is the planned next tier; the bundle already stores the raw state proof so existing bundles will upgrade without recapture.

Two operational consequences of node pruning:

  • Capture promptly. Non-archival nodes serve proof material for only ~1,000 rounds. Capture soon after the interval closes, or use an archival node.
  • Corroborate promptly, or against an archival node. --corroborate queries /v2/stateproofs/{round}, which is subject to the same pruning, so corroboration of a months-old round needs an archival node. Corroborating at or near capture time (the intended workflow) always works.

See SPEC.md for the format and the full verification algorithm, and docs/INTEGRATION.md for how to attach bundles to payment-protocol receipts.

What this is not

  • Not a bridge. A bundle proves an event happened; it does not make anyone pay. Redemption safety is an escrow-design problem, deliberately out of scope.
  • Not stronger than the chain. An adversary controlling a supermajority of stake could sign a false history; evidence inherits the source chain's security assumption, no more.
  • Not private. A bundle necessarily reveals the transaction it proves. Systems that hash transaction ids for privacy should treat bundle disclosure as opt-in (see docs/INTEGRATION.md).

Development

pip install -e .[dev]
python -m pytest            # offline: unit vectors, live-captured fixtures, tamper suite

The test fixtures are real bundles captured from Algorand and Voi mainnet. The tamper suite mutates every component of every fixture and requires verification to fail closed each time.

Licence

Apache-2.0, Copyright 2026 AlgoVoi. Created and maintained by AlgoVoi and offered as a gift to the Algorand and Voi communities: free to use, modify, and redistribute, with the attribution in NOTICE retained (Apache-2.0, Section 4). Cryptographic semantics (domain separators, vector commitment structure, canonical encoding) follow go-algorand; each is cited at its point of use in the source. avm-proofpack is a clean-room implementation and contains no go-algorand code.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

avm_proofpack-0.2.0.tar.gz (51.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

avm_proofpack-0.2.0-py3-none-any.whl (42.7 kB view details)

Uploaded Python 3

File details

Details for the file avm_proofpack-0.2.0.tar.gz.

File metadata

  • Download URL: avm_proofpack-0.2.0.tar.gz
  • Upload date:
  • Size: 51.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for avm_proofpack-0.2.0.tar.gz
Algorithm Hash digest
SHA256 cee42b81de30c0a4b50f283f6967ab4047e6e6d989088c557df2ef45f7462dc6
MD5 09a7f1c6b68cad0602dee3e69bf0a7bf
BLAKE2b-256 b6a5305b7c0616a09bef38f0baa0dc7e16bf4556846233781d013cbc0a4d285b

See more details on using hashes here.

File details

Details for the file avm_proofpack-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: avm_proofpack-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 42.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for avm_proofpack-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 213d57992a5636f0539abb58ac4cb12ab2f908f3e71402a7441fb2e60ebf9894
MD5 044c0bcdcf4ffb2195b86bc0949bf45d
BLAKE2b-256 2ab4b443dc3bb6336dfc3457e917211f0eb00fdd426308012f68f172a969bf7e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page