avm-mesh 0.4.2__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.
- avm_mesh-0.4.2/PKG-INFO +333 -0
- avm_mesh-0.4.2/README.md +309 -0
- avm_mesh-0.4.2/pyproject.toml +33 -0
- avm_mesh-0.4.2/setup.cfg +4 -0
- avm_mesh-0.4.2/src/avm_mesh/__init__.py +45 -0
- avm_mesh-0.4.2/src/avm_mesh/__main__.py +3 -0
- avm_mesh-0.4.2/src/avm_mesh/a2a.py +150 -0
- avm_mesh-0.4.2/src/avm_mesh/attestation.py +283 -0
- avm_mesh-0.4.2/src/avm_mesh/bond.py +199 -0
- avm_mesh-0.4.2/src/avm_mesh/cli.py +138 -0
- avm_mesh-0.4.2/src/avm_mesh/effects.py +162 -0
- avm_mesh-0.4.2/src/avm_mesh/produce.py +120 -0
- avm_mesh-0.4.2/src/avm_mesh/rails/__init__.py +16 -0
- avm_mesh-0.4.2/src/avm_mesh/rails/__main__.py +25 -0
- avm_mesh-0.4.2/src/avm_mesh/rails/verifier.py +142 -0
- avm_mesh-0.4.2/src/avm_mesh/receipt.py +283 -0
- avm_mesh-0.4.2/src/avm_mesh/sealed.py +161 -0
- avm_mesh-0.4.2/src/avm_mesh/service.py +277 -0
- avm_mesh-0.4.2/src/avm_mesh/trust.py +141 -0
- avm_mesh-0.4.2/src/avm_mesh.egg-info/PKG-INFO +333 -0
- avm_mesh-0.4.2/src/avm_mesh.egg-info/SOURCES.txt +35 -0
- avm_mesh-0.4.2/src/avm_mesh.egg-info/dependency_links.txt +1 -0
- avm_mesh-0.4.2/src/avm_mesh.egg-info/requires.txt +2 -0
- avm_mesh-0.4.2/src/avm_mesh.egg-info/top_level.txt +1 -0
- avm_mesh-0.4.2/tests/test_a2a.py +166 -0
- avm_mesh-0.4.2/tests/test_attestation.py +129 -0
- avm_mesh-0.4.2/tests/test_bond.py +92 -0
- avm_mesh-0.4.2/tests/test_corroboration.py +101 -0
- avm_mesh-0.4.2/tests/test_end_to_end.py +148 -0
- avm_mesh-0.4.2/tests/test_fail_closed.py +195 -0
- avm_mesh-0.4.2/tests/test_level2_real_falcon.py +149 -0
- avm_mesh-0.4.2/tests/test_produce.py +66 -0
- avm_mesh-0.4.2/tests/test_rails_verifier.py +293 -0
- avm_mesh-0.4.2/tests/test_receipt.py +167 -0
- avm_mesh-0.4.2/tests/test_sealed.py +165 -0
- avm_mesh-0.4.2/tests/test_service.py +222 -0
- avm_mesh-0.4.2/tests/test_trust.py +91 -0
avm_mesh-0.4.2/PKG-INFO
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: avm-mesh
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Settlement receipts for the AVM mesh: digest-and-pointer evidence that resolves to a verifiable proofpack
|
|
5
|
+
Author-email: AlgoVoi <chopmob@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/chopmob-cloud/avm-mesh
|
|
8
|
+
Project-URL: Repository, https://github.com/chopmob-cloud/avm-mesh
|
|
9
|
+
Project-URL: Issues, https://github.com/chopmob-cloud/avm-mesh/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/chopmob-cloud/avm-mesh/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: algorand,voi,avm,state-proof,settlement,a2a,x402,falcon
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Security :: Cryptography
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: avm-proofpack>=0.4.1
|
|
23
|
+
Requires-Dist: avm-effectproof>=0.1.1
|
|
24
|
+
|
|
25
|
+
# avm-mesh
|
|
26
|
+
|
|
27
|
+
Settlement receipts for the Algorand/Voi mesh.
|
|
28
|
+
|
|
29
|
+
A bridge is a separate system bolted onto two chains: its own validator set, its own token, its own
|
|
30
|
+
contracts, its own hosting, and therefore its own attack surface. `avm-mesh` takes the opposite
|
|
31
|
+
position — **the node set is the interop layer**. A node on the source chain produces evidence for free
|
|
32
|
+
because it already holds the full block. A node on the destination chain verifies that evidence because
|
|
33
|
+
it *cannot* see those blocks. Nothing new needs to be trusted; the asymmetry is the protocol.
|
|
34
|
+
|
|
35
|
+
## The problem with what exists
|
|
36
|
+
|
|
37
|
+
Every other cross-chain message answers "who says this happened?" with "us". Wormhole says the
|
|
38
|
+
Guardians, Axelar says its validator set, LayerZero says its DVNs. You are asked to trust a new
|
|
39
|
+
federation that did not previously exist, and to run it you must be online, because verification is a
|
|
40
|
+
query against someone else's indexer.
|
|
41
|
+
|
|
42
|
+
`avm-proofpack` answers with a Falcon-1024 state proof — Algorand and Voi sign their consensus state
|
|
43
|
+
with a NIST-selected post-quantum scheme, and a state proof verifies offline against the genesis hash.
|
|
44
|
+
No indexer, no RPC, no federation, and PQC by construction rather than by roadmap.
|
|
45
|
+
|
|
46
|
+
## What a receipt is
|
|
47
|
+
|
|
48
|
+
A settlement receipt is what actually moves between meshes. It is deliberately tiny:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"version": 1,
|
|
53
|
+
"chain": "mainnet-v1.0",
|
|
54
|
+
"txid": "J6FPFF4CKP4XUWUEDLEENV7EV5YDKFZHZJC6UYYWBDVBJLAT7IIA",
|
|
55
|
+
"round": 66457600,
|
|
56
|
+
"trust_root": "stake_attested",
|
|
57
|
+
"pointer": {
|
|
58
|
+
"uri": "ipfs://.../proofpack.json",
|
|
59
|
+
"digest": "9f2c...",
|
|
60
|
+
"first_attested_round": 66457345,
|
|
61
|
+
"last_attested_round": 66457600
|
|
62
|
+
},
|
|
63
|
+
"corroboration": []
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The proofpack itself is 428–834 KB, so it is **stored once per attested interval**, never per
|
|
68
|
+
transaction. The receipt commits to it by digest and points at it.
|
|
69
|
+
|
|
70
|
+
### Three properties, each enforced rather than documented
|
|
71
|
+
|
|
72
|
+
1. **Substitution.** The receipt commits to the bundle by SHA-256 over canonical JSON. Swapping the
|
|
73
|
+
bundle behind the pointer changes the digest and the receipt is refused.
|
|
74
|
+
2. **Upgrade.** The receipt carries the tier the bundle *earned*. The verifier re-derives that tier and
|
|
75
|
+
refuses a mismatch **in either direction**, so a structural bundle cannot be advertised as
|
|
76
|
+
`stake_attested`, and an honest verifier cannot quietly accept a weaker claim than the evidence
|
|
77
|
+
supports.
|
|
78
|
+
3. **Identity.** Chain, txid and round are taken from verification, not from the caller. A perfectly
|
|
79
|
+
valid bundle for a different transaction is not evidence for this one.
|
|
80
|
+
|
|
81
|
+
Pass `expected_genesis_hash_b64` to pin the chain itself:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
verify_receipt(receipt, resolve=storage.read, trusted_voters=anchor,
|
|
85
|
+
expected_genesis_hash_b64="wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Without it, `receipt.chain` is only checked against the bundle's genesis **id** — a string a fork can
|
|
89
|
+
reuse. With it, the hash is enforced against the authenticated block header, which no evidence author
|
|
90
|
+
can choose. The receipt's claimed attested interval must also equal the interval the evidence actually
|
|
91
|
+
attests; otherwise a receipt can point at a real proofpack covering a different interval and still read
|
|
92
|
+
as verified.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from avm_mesh import create_receipt, verify_receipt
|
|
96
|
+
|
|
97
|
+
receipt = create_receipt(bundle, "ipfs://.../proofpack.json")
|
|
98
|
+
report = verify_receipt(receipt, resolve=my_storage.read, expected_chain="mainnet-v1.0")
|
|
99
|
+
assert report.trust_root == "stake_attested"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`resolve` is caller-supplied, so storage is agnostic: a file, an HTTP endpoint, a chain.
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## Post-quantum dependency
|
|
107
|
+
|
|
108
|
+
Falcon-1024 is verified with `avm_proofpack.consensus.falcon` (stdlib-only). `pqcrypto` 1.0.0 removed
|
|
109
|
+
Falcon entirely when it migrated off the now-archived PQClean, so `pip install pqcrypto` no longer
|
|
110
|
+
provides a Falcon verifier. Why, what replaces it, and what remains unproven:
|
|
111
|
+
**`docs/PQC-FALCON.md`**.
|
|
112
|
+
|
|
113
|
+
## The framework, end to end
|
|
114
|
+
|
|
115
|
+
| Component | Role |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `produce` | settler side: store evidence, then seal a receipt |
|
|
118
|
+
| `service` | **witness**: serves receipts and content-addressed proofpacks |
|
|
119
|
+
| `receipt` | digest + pointer, three enforced bindings |
|
|
120
|
+
| `attestation` | LEVEL 3 consensus attestation, anchor-required |
|
|
121
|
+
| `sealed` | wire format the rails already speak |
|
|
122
|
+
| `trust` | one auditable policy holding every pin |
|
|
123
|
+
| `bond` | witness bonds and enforced independence |
|
|
124
|
+
| `a2a` | settlement-evidence extension |
|
|
125
|
+
| `cli` | offline verifier, `avm-mesh verify` / `avm-mesh serve` |
|
|
126
|
+
|
|
127
|
+
A deployment needs two things to be safe: an **anchor** it obtained itself, and a **policy** that pins
|
|
128
|
+
chain, genesis hash, anchor and witness set in one document. Everything else is mechanism.
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
# a witness publishes evidence
|
|
132
|
+
avm-mesh serve --root ./store --port 8080 --witness-id w1 --operator-id op-a --escrow-txid ESC
|
|
133
|
+
|
|
134
|
+
# a merchant verifies a receipt against its own policy
|
|
135
|
+
avm-mesh verify receipt.json --evidence ./proofpacks \
|
|
136
|
+
--genesis wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= \
|
|
137
|
+
--voters-commitment <b64> --ln-proven-weight <n>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## LEVEL 3: consensus attestation for the rails receipt
|
|
141
|
+
|
|
142
|
+
The AlgoVoi Payment Rails receipt already has two levels:
|
|
143
|
+
|
|
144
|
+
| Level | Checks |
|
|
145
|
+
|---|---|
|
|
146
|
+
| 1 | `sha256(canonical_bytes) == content_hash` |
|
|
147
|
+
| 2 | the **operator's** Falcon-1024 signature over that hash |
|
|
148
|
+
|
|
149
|
+
LEVEL 2 proves *"AlgoVoi issued this receipt"*. It cannot prove the settlement happened as described,
|
|
150
|
+
because the issuer could sign anything. **LEVEL 3** closes that: evidence that the transaction was
|
|
151
|
+
final, verified against chain consensus, with **no operator key in the trust base**.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from avm_mesh import attest, verify_attestation
|
|
155
|
+
|
|
156
|
+
# `anchor` is YOUR trust anchor: a TrustedVoters the caller supplies.
|
|
157
|
+
receipt["consensus_attestation"] = attest(bundle, "ipfs://.../proofpack.json",
|
|
158
|
+
trusted_voters=anchor).to_dict()
|
|
159
|
+
|
|
160
|
+
verify_attestation(receipt["consensus_attestation"], resolve=my_storage.read,
|
|
161
|
+
trusted_voters=anchor, # must match the recorded anchor
|
|
162
|
+
expected_genesis_hash_b64=ALGORAND_GENESIS_B64)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### The anchor is mandatory, and that is the point
|
|
166
|
+
|
|
167
|
+
The signing set for a state proof is committed by the voters commitment published **one interval
|
|
168
|
+
earlier**. A single bundle cannot authenticate that commitment — it carries it only as an unverified
|
|
169
|
+
`previous_state_proof_message`, which the bundle's own author chooses. Deriving trust from it means a
|
|
170
|
+
bundle can name itself as the sole signer and mint a valid-looking `stake_attested` tier.
|
|
171
|
+
|
|
172
|
+
So the anchor comes from **you**: a pinned genesis bootstrap, a chain walk
|
|
173
|
+
(`avm_proofpack.consensus.chain`), or a node you already trust at a known round. Never from the bundle
|
|
174
|
+
you are verifying. `trusted_voters` is required by `attest()` and by `verify_attestation()`, which also
|
|
175
|
+
checks the anchor against the one the attestation recorded — passing an attestation its own anchor
|
|
176
|
+
would verify nothing.
|
|
177
|
+
|
|
178
|
+
The scheme string is `avm-stateproof-falcon1024-v2`. **v1 attestations are refused by name**, because
|
|
179
|
+
their semantics were forgeable and must not be accepted as equivalent.
|
|
180
|
+
|
|
181
|
+
Because the attestation lives inside the receipt's canonical bytes, it is covered by LEVEL 1 and
|
|
182
|
+
LEVEL 2 — it cannot be swapped or stripped without breaking the content hash.
|
|
183
|
+
|
|
184
|
+
**`attest()` refuses rather than downgrades.** If the evidence does not reach `stake_attested`, no
|
|
185
|
+
attestation is emitted at all; shipping a weaker block would let a receipt *look* consensus-backed
|
|
186
|
+
while carrying an operator's word.
|
|
187
|
+
|
|
188
|
+
**Pin the genesis hash.** Without `expected_genesis_hash_b64` the attestation is self-referential —
|
|
189
|
+
it states its own anchor and then agrees with itself, which is no stronger than LEVEL 2.
|
|
190
|
+
|
|
191
|
+
**Only Algorand and Voi.** `ConsensusAttestation` rejects any other chain at construction. Hedera,
|
|
192
|
+
Stellar, Base, Solana and Tempo have no state proofs, and a receipt for them stays honestly at
|
|
193
|
+
LEVEL 2.
|
|
194
|
+
|
|
195
|
+
## Seal it and verify it from the command line
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
python -m avm_mesh verify receipt.json --evidence ./proofpacks \
|
|
199
|
+
--genesis wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= \
|
|
200
|
+
--voters-commitment <trusted-voters-commitment-b64> \
|
|
201
|
+
--ln-proven-weight <proven-weight>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
integrity: PASS
|
|
206
|
+
signature: None
|
|
207
|
+
consensus: stake_attested
|
|
208
|
+
trust root: stake_attested
|
|
209
|
+
attested: J6FPFF4CKP4XUWUEDLEENV7EV5YDKFZHZJC6UYYWBDVBJLAT7IIA @ round 66457600
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The anchor flags are required whenever the receipt carries a consensus attestation: supply the
|
|
213
|
+
`TrustedVoters` commitment and proven weight you independently trust. A sealed receipt with a LEVEL 3
|
|
214
|
+
block verifies against that anchor and fails closed without it — never from the bundle's own word. The
|
|
215
|
+
genesis pin is likewise enforced against the *authenticated* state-proof header, not the attestation's
|
|
216
|
+
self-declared field.
|
|
217
|
+
|
|
218
|
+
Exit `0` verified, `2` failed, `3` usage. `--json` for machine output. A real receipt is **945 bytes**
|
|
219
|
+
against an 854 KB proofpack; the proof is stored once per attested interval, not per payment.
|
|
220
|
+
|
|
221
|
+
Sealing binds the attestation into the hashed bytes:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from avm_mesh import seal
|
|
225
|
+
receipt = seal({"settlement": {...}, "issuer": "..."}, bundle, "file://proofpack.json")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## A2A: settlement evidence between agents
|
|
229
|
+
|
|
230
|
+
A2A is the transport agents already use, and you are an A2A Partner with TCK standing, so this is the
|
|
231
|
+
route that reaches peers rather than requiring them to adopt an SDK.
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from avm_mesh import a2a
|
|
235
|
+
card = a2a.advertise(my_agent_card) # declare the extension
|
|
236
|
+
artifact = a2a.as_artifact(sealed_receipt) # evidence as an ordinary Artifact
|
|
237
|
+
result = a2a.verify_artifact(artifact, resolve=my_storage.read,
|
|
238
|
+
expected_genesis_hash_b64=GENESIS)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The extension is **optional** (`required: false`), so peers without it still interoperate and simply
|
|
242
|
+
fall back to whatever they trusted before. The URI is the versioning boundary — semantics behind a
|
|
243
|
+
published URI never change; publish a v2 instead.
|
|
244
|
+
|
|
245
|
+
**No evidence is an error, not an empty pass.** An artifact without the extension payload raises rather
|
|
246
|
+
than returning a vacuous success, because "verified nothing and reported OK" is how receivers end up
|
|
247
|
+
believing they checked something.
|
|
248
|
+
|
|
249
|
+
## Witnesses are bonded, and that is the whole point
|
|
250
|
+
|
|
251
|
+
A state proof says what the chain did. A witness is whoever hands you the proof. Without a bond a
|
|
252
|
+
witness can withhold, delay or selectively serve evidence **at zero cost** — and "decentralised
|
|
253
|
+
because node operators run it" becomes a centralised service with a bigger logo.
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from avm_mesh.bond import witnesses
|
|
257
|
+
|
|
258
|
+
ws = witnesses(w("w1", "op-a"), w("w2", "op-b"), w("w3", "op-c"))
|
|
259
|
+
quorum = ws.require_corroboration("mainnet-v1.0", threshold=2)
|
|
260
|
+
ws.slashable_stake("mainnet-v1.0") # what one lie could actually cost
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Two claims are kept strictly apart:
|
|
264
|
+
|
|
265
|
+
| Claim | Meaning |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `bond_microalgo` + `escrow_txid` | economic stake that can be slashed — verifiable on-chain |
|
|
268
|
+
| `operator_id` | who runs the witness — **not** a trust source, only an independence input |
|
|
269
|
+
|
|
270
|
+
**Independence is enforced, not assumed.** `independent_corroborators()` counts at most one witness per
|
|
271
|
+
operator *and* per escrow, because:
|
|
272
|
+
|
|
273
|
+
- two endpoints of one operator are one witness wearing two hats — presenting them as a quorum is the
|
|
274
|
+
oldest trick in the attestation playbook;
|
|
275
|
+
- two witnesses pointing at one escrow is one bond counted twice, the cheapest possible fake.
|
|
276
|
+
|
|
277
|
+
`require_corroboration()` **refuses** rather than returning a short list. A caller that cannot reach the
|
|
278
|
+
threshold must not be handed fewer corroborators and left to interpret that as success.
|
|
279
|
+
|
|
280
|
+
The receipt's `corroboration` field is checked against bonds when you ask it to be:
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
verify_receipt(receipt, resolve=storage.read, witness_set=ws, corroboration_threshold=2)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
It refuses on four distinct failures — **unknown** witness (no bond record), **unbonded** (absent,
|
|
287
|
+
inactive or too small), **duplicated** (same operator or same escrow), **insufficient** (fewer
|
|
288
|
+
independent witnesses than the threshold). Omitting these arguments keeps the old behaviour: the claim
|
|
289
|
+
is recorded, and the caller should describe it as recorded rather than established.
|
|
290
|
+
|
|
291
|
+
An unverifiable bond is not a bond: a witness record without an `escrow_txid` is rejected at
|
|
292
|
+
construction, as is one with no `operator_id`.
|
|
293
|
+
|
|
294
|
+
## Trust tiers are per chain, on purpose
|
|
295
|
+
|
|
296
|
+
Only **Algorand** and **Voi** have Falcon state proofs. Hedera, Stellar, Base, Solana and Tempo do not.
|
|
297
|
+
Claiming parity across seven chains would be the same dishonesty as a bridge calling itself
|
|
298
|
+
trustless:
|
|
299
|
+
|
|
300
|
+
| Tier | Means |
|
|
301
|
+
|---|---|
|
|
302
|
+
| `stake_attested` | offline-verified against a Falcon-signed state proof |
|
|
303
|
+
| `corroborated` | several independent observers agree, no consensus proof |
|
|
304
|
+
| `structural` | internally consistent, no external anchor |
|
|
305
|
+
| `unattested` | no proof material present |
|
|
306
|
+
|
|
307
|
+
*"Here are the two chains where you get cryptographic proof and the five where you get someone's
|
|
308
|
+
word"* is a stronger offer than claiming all seven are equal, because the merchant can check it.
|
|
309
|
+
|
|
310
|
+
## Status
|
|
311
|
+
|
|
312
|
+
`0.2.0` — producer path, witness service, trust policy, receipts, LEVEL 3 attestation, A2A extension.
|
|
313
|
+
**85 tests**, validated in a VM2 clean-room.
|
|
314
|
+
|
|
315
|
+
**Dependency:** requires `avm-proofpack>=0.4.1` and is verified against **0.4.1 @ `02d2516`**, where the
|
|
316
|
+
`stake_attested` tier became anchor-required. `avm-mesh` 0.1.x followed that fix: LEVEL 3 no longer
|
|
317
|
+
authenticates from a bundle's own `previous_message`.
|
|
318
|
+
|
|
319
|
+
Not yet built: wiring LEVEL 3 into the rails bundle's own verifier (it currently lives here), the A2A
|
|
320
|
+
settlement extension, and witness bonding.
|
|
321
|
+
|
|
322
|
+
## Honest limits
|
|
323
|
+
|
|
324
|
+
- **A receipt is not a proof of delivery.** It proves a transaction was final and what it did. Whether
|
|
325
|
+
goods shipped is not an on-chain fact.
|
|
326
|
+
- **Corroboration is a capture-time property.** It cannot be established after the fact, and two
|
|
327
|
+
endpoints run by one operator are not independent witnesses. It is recorded, not counted.
|
|
328
|
+
- **Capture is time-sensitive.** Nodes retain roughly two state-proof intervals (~512 rounds, ~20
|
|
329
|
+
minutes). Miss that window and the bundle falls back to `unattested`.
|
|
330
|
+
- **Falcon verification is only as good as the genesis hash** you pin. Ship it in your application,
|
|
331
|
+
not fetched over the wire.
|
|
332
|
+
|
|
333
|
+
Apache-2.0.
|
avm_mesh-0.4.2/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# avm-mesh
|
|
2
|
+
|
|
3
|
+
Settlement receipts for the Algorand/Voi mesh.
|
|
4
|
+
|
|
5
|
+
A bridge is a separate system bolted onto two chains: its own validator set, its own token, its own
|
|
6
|
+
contracts, its own hosting, and therefore its own attack surface. `avm-mesh` takes the opposite
|
|
7
|
+
position — **the node set is the interop layer**. A node on the source chain produces evidence for free
|
|
8
|
+
because it already holds the full block. A node on the destination chain verifies that evidence because
|
|
9
|
+
it *cannot* see those blocks. Nothing new needs to be trusted; the asymmetry is the protocol.
|
|
10
|
+
|
|
11
|
+
## The problem with what exists
|
|
12
|
+
|
|
13
|
+
Every other cross-chain message answers "who says this happened?" with "us". Wormhole says the
|
|
14
|
+
Guardians, Axelar says its validator set, LayerZero says its DVNs. You are asked to trust a new
|
|
15
|
+
federation that did not previously exist, and to run it you must be online, because verification is a
|
|
16
|
+
query against someone else's indexer.
|
|
17
|
+
|
|
18
|
+
`avm-proofpack` answers with a Falcon-1024 state proof — Algorand and Voi sign their consensus state
|
|
19
|
+
with a NIST-selected post-quantum scheme, and a state proof verifies offline against the genesis hash.
|
|
20
|
+
No indexer, no RPC, no federation, and PQC by construction rather than by roadmap.
|
|
21
|
+
|
|
22
|
+
## What a receipt is
|
|
23
|
+
|
|
24
|
+
A settlement receipt is what actually moves between meshes. It is deliberately tiny:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"version": 1,
|
|
29
|
+
"chain": "mainnet-v1.0",
|
|
30
|
+
"txid": "J6FPFF4CKP4XUWUEDLEENV7EV5YDKFZHZJC6UYYWBDVBJLAT7IIA",
|
|
31
|
+
"round": 66457600,
|
|
32
|
+
"trust_root": "stake_attested",
|
|
33
|
+
"pointer": {
|
|
34
|
+
"uri": "ipfs://.../proofpack.json",
|
|
35
|
+
"digest": "9f2c...",
|
|
36
|
+
"first_attested_round": 66457345,
|
|
37
|
+
"last_attested_round": 66457600
|
|
38
|
+
},
|
|
39
|
+
"corroboration": []
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The proofpack itself is 428–834 KB, so it is **stored once per attested interval**, never per
|
|
44
|
+
transaction. The receipt commits to it by digest and points at it.
|
|
45
|
+
|
|
46
|
+
### Three properties, each enforced rather than documented
|
|
47
|
+
|
|
48
|
+
1. **Substitution.** The receipt commits to the bundle by SHA-256 over canonical JSON. Swapping the
|
|
49
|
+
bundle behind the pointer changes the digest and the receipt is refused.
|
|
50
|
+
2. **Upgrade.** The receipt carries the tier the bundle *earned*. The verifier re-derives that tier and
|
|
51
|
+
refuses a mismatch **in either direction**, so a structural bundle cannot be advertised as
|
|
52
|
+
`stake_attested`, and an honest verifier cannot quietly accept a weaker claim than the evidence
|
|
53
|
+
supports.
|
|
54
|
+
3. **Identity.** Chain, txid and round are taken from verification, not from the caller. A perfectly
|
|
55
|
+
valid bundle for a different transaction is not evidence for this one.
|
|
56
|
+
|
|
57
|
+
Pass `expected_genesis_hash_b64` to pin the chain itself:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
verify_receipt(receipt, resolve=storage.read, trusted_voters=anchor,
|
|
61
|
+
expected_genesis_hash_b64="wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Without it, `receipt.chain` is only checked against the bundle's genesis **id** — a string a fork can
|
|
65
|
+
reuse. With it, the hash is enforced against the authenticated block header, which no evidence author
|
|
66
|
+
can choose. The receipt's claimed attested interval must also equal the interval the evidence actually
|
|
67
|
+
attests; otherwise a receipt can point at a real proofpack covering a different interval and still read
|
|
68
|
+
as verified.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from avm_mesh import create_receipt, verify_receipt
|
|
72
|
+
|
|
73
|
+
receipt = create_receipt(bundle, "ipfs://.../proofpack.json")
|
|
74
|
+
report = verify_receipt(receipt, resolve=my_storage.read, expected_chain="mainnet-v1.0")
|
|
75
|
+
assert report.trust_root == "stake_attested"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`resolve` is caller-supplied, so storage is agnostic: a file, an HTTP endpoint, a chain.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Post-quantum dependency
|
|
83
|
+
|
|
84
|
+
Falcon-1024 is verified with `avm_proofpack.consensus.falcon` (stdlib-only). `pqcrypto` 1.0.0 removed
|
|
85
|
+
Falcon entirely when it migrated off the now-archived PQClean, so `pip install pqcrypto` no longer
|
|
86
|
+
provides a Falcon verifier. Why, what replaces it, and what remains unproven:
|
|
87
|
+
**`docs/PQC-FALCON.md`**.
|
|
88
|
+
|
|
89
|
+
## The framework, end to end
|
|
90
|
+
|
|
91
|
+
| Component | Role |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `produce` | settler side: store evidence, then seal a receipt |
|
|
94
|
+
| `service` | **witness**: serves receipts and content-addressed proofpacks |
|
|
95
|
+
| `receipt` | digest + pointer, three enforced bindings |
|
|
96
|
+
| `attestation` | LEVEL 3 consensus attestation, anchor-required |
|
|
97
|
+
| `sealed` | wire format the rails already speak |
|
|
98
|
+
| `trust` | one auditable policy holding every pin |
|
|
99
|
+
| `bond` | witness bonds and enforced independence |
|
|
100
|
+
| `a2a` | settlement-evidence extension |
|
|
101
|
+
| `cli` | offline verifier, `avm-mesh verify` / `avm-mesh serve` |
|
|
102
|
+
|
|
103
|
+
A deployment needs two things to be safe: an **anchor** it obtained itself, and a **policy** that pins
|
|
104
|
+
chain, genesis hash, anchor and witness set in one document. Everything else is mechanism.
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
# a witness publishes evidence
|
|
108
|
+
avm-mesh serve --root ./store --port 8080 --witness-id w1 --operator-id op-a --escrow-txid ESC
|
|
109
|
+
|
|
110
|
+
# a merchant verifies a receipt against its own policy
|
|
111
|
+
avm-mesh verify receipt.json --evidence ./proofpacks \
|
|
112
|
+
--genesis wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= \
|
|
113
|
+
--voters-commitment <b64> --ln-proven-weight <n>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## LEVEL 3: consensus attestation for the rails receipt
|
|
117
|
+
|
|
118
|
+
The AlgoVoi Payment Rails receipt already has two levels:
|
|
119
|
+
|
|
120
|
+
| Level | Checks |
|
|
121
|
+
|---|---|
|
|
122
|
+
| 1 | `sha256(canonical_bytes) == content_hash` |
|
|
123
|
+
| 2 | the **operator's** Falcon-1024 signature over that hash |
|
|
124
|
+
|
|
125
|
+
LEVEL 2 proves *"AlgoVoi issued this receipt"*. It cannot prove the settlement happened as described,
|
|
126
|
+
because the issuer could sign anything. **LEVEL 3** closes that: evidence that the transaction was
|
|
127
|
+
final, verified against chain consensus, with **no operator key in the trust base**.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from avm_mesh import attest, verify_attestation
|
|
131
|
+
|
|
132
|
+
# `anchor` is YOUR trust anchor: a TrustedVoters the caller supplies.
|
|
133
|
+
receipt["consensus_attestation"] = attest(bundle, "ipfs://.../proofpack.json",
|
|
134
|
+
trusted_voters=anchor).to_dict()
|
|
135
|
+
|
|
136
|
+
verify_attestation(receipt["consensus_attestation"], resolve=my_storage.read,
|
|
137
|
+
trusted_voters=anchor, # must match the recorded anchor
|
|
138
|
+
expected_genesis_hash_b64=ALGORAND_GENESIS_B64)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### The anchor is mandatory, and that is the point
|
|
142
|
+
|
|
143
|
+
The signing set for a state proof is committed by the voters commitment published **one interval
|
|
144
|
+
earlier**. A single bundle cannot authenticate that commitment — it carries it only as an unverified
|
|
145
|
+
`previous_state_proof_message`, which the bundle's own author chooses. Deriving trust from it means a
|
|
146
|
+
bundle can name itself as the sole signer and mint a valid-looking `stake_attested` tier.
|
|
147
|
+
|
|
148
|
+
So the anchor comes from **you**: a pinned genesis bootstrap, a chain walk
|
|
149
|
+
(`avm_proofpack.consensus.chain`), or a node you already trust at a known round. Never from the bundle
|
|
150
|
+
you are verifying. `trusted_voters` is required by `attest()` and by `verify_attestation()`, which also
|
|
151
|
+
checks the anchor against the one the attestation recorded — passing an attestation its own anchor
|
|
152
|
+
would verify nothing.
|
|
153
|
+
|
|
154
|
+
The scheme string is `avm-stateproof-falcon1024-v2`. **v1 attestations are refused by name**, because
|
|
155
|
+
their semantics were forgeable and must not be accepted as equivalent.
|
|
156
|
+
|
|
157
|
+
Because the attestation lives inside the receipt's canonical bytes, it is covered by LEVEL 1 and
|
|
158
|
+
LEVEL 2 — it cannot be swapped or stripped without breaking the content hash.
|
|
159
|
+
|
|
160
|
+
**`attest()` refuses rather than downgrades.** If the evidence does not reach `stake_attested`, no
|
|
161
|
+
attestation is emitted at all; shipping a weaker block would let a receipt *look* consensus-backed
|
|
162
|
+
while carrying an operator's word.
|
|
163
|
+
|
|
164
|
+
**Pin the genesis hash.** Without `expected_genesis_hash_b64` the attestation is self-referential —
|
|
165
|
+
it states its own anchor and then agrees with itself, which is no stronger than LEVEL 2.
|
|
166
|
+
|
|
167
|
+
**Only Algorand and Voi.** `ConsensusAttestation` rejects any other chain at construction. Hedera,
|
|
168
|
+
Stellar, Base, Solana and Tempo have no state proofs, and a receipt for them stays honestly at
|
|
169
|
+
LEVEL 2.
|
|
170
|
+
|
|
171
|
+
## Seal it and verify it from the command line
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
python -m avm_mesh verify receipt.json --evidence ./proofpacks \
|
|
175
|
+
--genesis wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8= \
|
|
176
|
+
--voters-commitment <trusted-voters-commitment-b64> \
|
|
177
|
+
--ln-proven-weight <proven-weight>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
integrity: PASS
|
|
182
|
+
signature: None
|
|
183
|
+
consensus: stake_attested
|
|
184
|
+
trust root: stake_attested
|
|
185
|
+
attested: J6FPFF4CKP4XUWUEDLEENV7EV5YDKFZHZJC6UYYWBDVBJLAT7IIA @ round 66457600
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The anchor flags are required whenever the receipt carries a consensus attestation: supply the
|
|
189
|
+
`TrustedVoters` commitment and proven weight you independently trust. A sealed receipt with a LEVEL 3
|
|
190
|
+
block verifies against that anchor and fails closed without it — never from the bundle's own word. The
|
|
191
|
+
genesis pin is likewise enforced against the *authenticated* state-proof header, not the attestation's
|
|
192
|
+
self-declared field.
|
|
193
|
+
|
|
194
|
+
Exit `0` verified, `2` failed, `3` usage. `--json` for machine output. A real receipt is **945 bytes**
|
|
195
|
+
against an 854 KB proofpack; the proof is stored once per attested interval, not per payment.
|
|
196
|
+
|
|
197
|
+
Sealing binds the attestation into the hashed bytes:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from avm_mesh import seal
|
|
201
|
+
receipt = seal({"settlement": {...}, "issuer": "..."}, bundle, "file://proofpack.json")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## A2A: settlement evidence between agents
|
|
205
|
+
|
|
206
|
+
A2A is the transport agents already use, and you are an A2A Partner with TCK standing, so this is the
|
|
207
|
+
route that reaches peers rather than requiring them to adopt an SDK.
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from avm_mesh import a2a
|
|
211
|
+
card = a2a.advertise(my_agent_card) # declare the extension
|
|
212
|
+
artifact = a2a.as_artifact(sealed_receipt) # evidence as an ordinary Artifact
|
|
213
|
+
result = a2a.verify_artifact(artifact, resolve=my_storage.read,
|
|
214
|
+
expected_genesis_hash_b64=GENESIS)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The extension is **optional** (`required: false`), so peers without it still interoperate and simply
|
|
218
|
+
fall back to whatever they trusted before. The URI is the versioning boundary — semantics behind a
|
|
219
|
+
published URI never change; publish a v2 instead.
|
|
220
|
+
|
|
221
|
+
**No evidence is an error, not an empty pass.** An artifact without the extension payload raises rather
|
|
222
|
+
than returning a vacuous success, because "verified nothing and reported OK" is how receivers end up
|
|
223
|
+
believing they checked something.
|
|
224
|
+
|
|
225
|
+
## Witnesses are bonded, and that is the whole point
|
|
226
|
+
|
|
227
|
+
A state proof says what the chain did. A witness is whoever hands you the proof. Without a bond a
|
|
228
|
+
witness can withhold, delay or selectively serve evidence **at zero cost** — and "decentralised
|
|
229
|
+
because node operators run it" becomes a centralised service with a bigger logo.
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from avm_mesh.bond import witnesses
|
|
233
|
+
|
|
234
|
+
ws = witnesses(w("w1", "op-a"), w("w2", "op-b"), w("w3", "op-c"))
|
|
235
|
+
quorum = ws.require_corroboration("mainnet-v1.0", threshold=2)
|
|
236
|
+
ws.slashable_stake("mainnet-v1.0") # what one lie could actually cost
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Two claims are kept strictly apart:
|
|
240
|
+
|
|
241
|
+
| Claim | Meaning |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `bond_microalgo` + `escrow_txid` | economic stake that can be slashed — verifiable on-chain |
|
|
244
|
+
| `operator_id` | who runs the witness — **not** a trust source, only an independence input |
|
|
245
|
+
|
|
246
|
+
**Independence is enforced, not assumed.** `independent_corroborators()` counts at most one witness per
|
|
247
|
+
operator *and* per escrow, because:
|
|
248
|
+
|
|
249
|
+
- two endpoints of one operator are one witness wearing two hats — presenting them as a quorum is the
|
|
250
|
+
oldest trick in the attestation playbook;
|
|
251
|
+
- two witnesses pointing at one escrow is one bond counted twice, the cheapest possible fake.
|
|
252
|
+
|
|
253
|
+
`require_corroboration()` **refuses** rather than returning a short list. A caller that cannot reach the
|
|
254
|
+
threshold must not be handed fewer corroborators and left to interpret that as success.
|
|
255
|
+
|
|
256
|
+
The receipt's `corroboration` field is checked against bonds when you ask it to be:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
verify_receipt(receipt, resolve=storage.read, witness_set=ws, corroboration_threshold=2)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
It refuses on four distinct failures — **unknown** witness (no bond record), **unbonded** (absent,
|
|
263
|
+
inactive or too small), **duplicated** (same operator or same escrow), **insufficient** (fewer
|
|
264
|
+
independent witnesses than the threshold). Omitting these arguments keeps the old behaviour: the claim
|
|
265
|
+
is recorded, and the caller should describe it as recorded rather than established.
|
|
266
|
+
|
|
267
|
+
An unverifiable bond is not a bond: a witness record without an `escrow_txid` is rejected at
|
|
268
|
+
construction, as is one with no `operator_id`.
|
|
269
|
+
|
|
270
|
+
## Trust tiers are per chain, on purpose
|
|
271
|
+
|
|
272
|
+
Only **Algorand** and **Voi** have Falcon state proofs. Hedera, Stellar, Base, Solana and Tempo do not.
|
|
273
|
+
Claiming parity across seven chains would be the same dishonesty as a bridge calling itself
|
|
274
|
+
trustless:
|
|
275
|
+
|
|
276
|
+
| Tier | Means |
|
|
277
|
+
|---|---|
|
|
278
|
+
| `stake_attested` | offline-verified against a Falcon-signed state proof |
|
|
279
|
+
| `corroborated` | several independent observers agree, no consensus proof |
|
|
280
|
+
| `structural` | internally consistent, no external anchor |
|
|
281
|
+
| `unattested` | no proof material present |
|
|
282
|
+
|
|
283
|
+
*"Here are the two chains where you get cryptographic proof and the five where you get someone's
|
|
284
|
+
word"* is a stronger offer than claiming all seven are equal, because the merchant can check it.
|
|
285
|
+
|
|
286
|
+
## Status
|
|
287
|
+
|
|
288
|
+
`0.2.0` — producer path, witness service, trust policy, receipts, LEVEL 3 attestation, A2A extension.
|
|
289
|
+
**85 tests**, validated in a VM2 clean-room.
|
|
290
|
+
|
|
291
|
+
**Dependency:** requires `avm-proofpack>=0.4.1` and is verified against **0.4.1 @ `02d2516`**, where the
|
|
292
|
+
`stake_attested` tier became anchor-required. `avm-mesh` 0.1.x followed that fix: LEVEL 3 no longer
|
|
293
|
+
authenticates from a bundle's own `previous_message`.
|
|
294
|
+
|
|
295
|
+
Not yet built: wiring LEVEL 3 into the rails bundle's own verifier (it currently lives here), the A2A
|
|
296
|
+
settlement extension, and witness bonding.
|
|
297
|
+
|
|
298
|
+
## Honest limits
|
|
299
|
+
|
|
300
|
+
- **A receipt is not a proof of delivery.** It proves a transaction was final and what it did. Whether
|
|
301
|
+
goods shipped is not an on-chain fact.
|
|
302
|
+
- **Corroboration is a capture-time property.** It cannot be established after the fact, and two
|
|
303
|
+
endpoints run by one operator are not independent witnesses. It is recorded, not counted.
|
|
304
|
+
- **Capture is time-sensitive.** Nodes retain roughly two state-proof intervals (~512 rounds, ~20
|
|
305
|
+
minutes). Miss that window and the bundle falls back to `unattested`.
|
|
306
|
+
- **Falcon verification is only as good as the genesis hash** you pin. Ship it in your application,
|
|
307
|
+
not fetched over the wire.
|
|
308
|
+
|
|
309
|
+
Apache-2.0.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "avm-mesh"
|
|
7
|
+
version = "0.4.2"
|
|
8
|
+
description = "Settlement receipts for the AVM mesh: digest-and-pointer evidence that resolves to a verifiable proofpack"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
authors = [{name = "AlgoVoi", email = "chopmob@gmail.com"}]
|
|
13
|
+
keywords = ["algorand", "voi", "avm", "state-proof", "settlement", "a2a", "x402", "falcon"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Topic :: Security :: Cryptography",
|
|
21
|
+
"Topic :: Office/Business :: Financial",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
]
|
|
24
|
+
dependencies = ["avm-proofpack>=0.4.1", "avm-effectproof>=0.1.1"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/chopmob-cloud/avm-mesh"
|
|
28
|
+
Repository = "https://github.com/chopmob-cloud/avm-mesh"
|
|
29
|
+
Issues = "https://github.com/chopmob-cloud/avm-mesh/issues"
|
|
30
|
+
Changelog = "https://github.com/chopmob-cloud/avm-mesh/blob/main/CHANGELOG.md"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
avm_mesh-0.4.2/setup.cfg
ADDED