Skip to content

Commit 94bb7da

Browse files
committed
docs: add trustless sessions concept page
1 parent 0bf5d17 commit 94bb7da

1 file changed

Lines changed: 373 additions & 0 deletions

File tree

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
---
2+
title: Trustless Sessions (Future)
3+
description: Fraud proof architecture for trustless escrow session verification
4+
---
5+
6+
# Trustless Sessions
7+
8+
<Note>
9+
This document describes a **future enhancement** to the escrow system. The current implementation uses a trusted facilitator model. Trustless verification is planned for a future release.
10+
</Note>
11+
12+
## Current Trust Model
13+
14+
The current escrow system has a **trusted facilitator** model:
15+
16+
| Component | Trust Level | Notes |
17+
| --------- | ----------- | ----- |
18+
| Deposit authorization | Trustless | ERC-3009 signature verified on-chain |
19+
| Off-chain debits | Trusted | Facilitator tracks in database |
20+
| Capture amounts | Trusted | Facilitator decides what to capture |
21+
| Fund recovery | Trustless | Smart contract enforces reclaim after expiry |
22+
23+
**Key assumption:** Users trust the facilitator to honestly report API consumption.
24+
25+
### What's Trustless Today
26+
27+
- **Deposits**: User signs ERC-3009 authorization - cryptographically verified on-chain
28+
- **Reclaims**: Smart contract enforces reclaim after `authorizationExpiry` - no facilitator involvement needed
29+
- **Bounded exposure**: Only deposited amount at risk, never entire wallet
30+
31+
### What Requires Trust
32+
33+
- **Usage tracking**: Facilitator maintains authoritative ledger of session debits
34+
- **Capture amounts**: Facilitator decides how much to capture - no cryptographic proof submitted
35+
- **Billing accuracy**: No on-chain verification that captures match actual consumption
36+
37+
## Trustless Architecture (Planned)
38+
39+
### Overview
40+
41+
The trustless model replaces facilitator trust with **cryptographic verification**:
42+
43+
```
44+
1. SESSION CREATION (unchanged)
45+
User signs ERC-3009 (one-time)
46+
Facilitator creates session, returns sessionToken
47+
48+
2. SESSION USAGE (unchanged UX)
49+
Client sends session token per request
50+
Facilitator returns CUMULATIVE receipt:
51+
{ sessionId, nonce, cumulativeAmount, timestamp, facilitatorSig }
52+
Client stores only latest receipt (can re-fetch if needed)
53+
54+
3. FACILITATOR COMMITS CAPTURE
55+
Builds merkle tree of ALL issued receipts
56+
Publishes: (merkleRoot, captureAmount) for sessionId
57+
Stakes collateral (10% of capture value)
58+
CRITICAL: Cannot forge receipts after commitment
59+
60+
4. CHALLENGE WINDOW (3-7 days)
61+
User disputes if captureAmount > their latest receipt's cumulative
62+
Provides their highest-nonce receipt as proof
63+
Facilitator can counter with higher-nonce receipt if user is lying
64+
BUT: Facilitator must prove receipt exists in committed merkle tree
65+
Contract compares nonces: highest nonce wins (if merkle proof valid)
66+
67+
5. SETTLEMENT
68+
After window: funds released to receiver
69+
Collateral returned to facilitator
70+
```
71+
72+
### Cumulative Receipts
73+
74+
For each API request, the **facilitator signs a cumulative receipt** showing the running total spent. This prevents users from selectively discarding receipts.
75+
76+
```typescript
77+
interface CumulativeReceipt {
78+
sessionId: bytes32; // Links to on-chain escrow (and original ERC-3009)
79+
nonce: uint256; // Monotonically increasing per session
80+
cumulativeAmount: uint256; // TOTAL spent so far (not just this request)
81+
timestamp: uint256; // Unix timestamp
82+
facilitatorSignature: bytes; // EIP-712 signature over all above fields
83+
}
84+
```
85+
86+
The `facilitatorSignature` is the key binding:
87+
- Proves facilitator acknowledged this exact (sessionId, nonce, amount) tuple
88+
- Cannot be forged by user (only facilitator has signing key)
89+
- `sessionId` links to on-chain escrow → links to original ERC-3009 authorization
90+
91+
**Example sequence:**
92+
```
93+
Request 1: { nonce: 1, cumulativeAmount: $0.05 }
94+
Request 2: { nonce: 2, cumulativeAmount: $0.10 }
95+
Request 3: { nonce: 3, cumulativeAmount: $0.15 }
96+
...
97+
Request 100: { nonce: 100, cumulativeAmount: $5.00 }
98+
```
99+
100+
**Why cumulative?** Users can only dispute using their **highest nonce receipt**. If the latest receipt shows `$5.00 cumulative`, they cannot claim less was consumed. No selective discarding possible.
101+
102+
<Note>
103+
Receipts are returned in the `PAYMENT-RESPONSE` header. Clients only need to store their **latest receipt** as proof.
104+
</Note>
105+
106+
### Receipt Re-Fetching
107+
108+
If a receipt response is lost (network failure), clients can re-fetch any past receipt:
109+
110+
```
111+
GET /api/receipts/:sessionId/:nonce
112+
Authorization: Bearer <sessionToken>
113+
114+
Response: { receipt: CumulativeReceipt }
115+
```
116+
117+
<Warning>
118+
Clients should sync their receipts before the challenge window opens. After capture is committed, the facilitator may not be required to serve old receipts.
119+
</Warning>
120+
121+
### Why This Prevents Attacks
122+
123+
<AccordionGroup>
124+
<Accordion title="User claims missing receipts" icon="user-xmark">
125+
**Attack:** "I only received 80 receipts, not 100"
126+
127+
**Defense:** Cumulative model means only the **highest nonce receipt** matters.
128+
If user's latest receipt shows `{ nonce: 100, cumulative: $5.00 }`, the cumulative amount proves total consumption.
129+
</Accordion>
130+
131+
<Accordion title="User discards high-value receipts" icon="trash">
132+
**Attack:** User discards receipt showing $5.00, disputes with earlier $3.00 receipt
133+
134+
**Defense:** Facilitator counter-disputes with the higher-nonce receipt:
135+
- User provides: `{ nonce: 60, cumulative: $3.00 }`
136+
- Facilitator counters: `{ nonce: 100, cumulative: $5.00 }` with valid signature
137+
- Contract verifies: facilitator's nonce > user's nonce → dispute rejected
138+
</Accordion>
139+
140+
<Accordion title="Facilitator over-captures" icon="user-secret">
141+
**Attack:** Facilitator captures $10.00 but only issued receipts for $5.00
142+
143+
**Defense:** User disputes with their highest receipt:
144+
- User provides: `{ nonce: 100, cumulative: $5.00 }` with valid facilitator signature
145+
- Facilitator cannot counter (never issued higher receipt)
146+
- Contract verifies: $5.00 < $10.00 captured → facilitator slashed
147+
</Accordion>
148+
149+
<Accordion title="Facilitator doesn't return receipts" icon="ban">
150+
**Attack:** Facilitator processes requests but doesn't return receipts
151+
152+
**Defense:** Without a signed receipt, the charge effectively didn't happen.
153+
- User's latest receipt shows actual acknowledged consumption
154+
- Facilitator can only capture up to what they signed for
155+
- Incentivizes facilitator to always return receipts
156+
</Accordion>
157+
158+
<Accordion title="Network drops receipts" icon="wifi">
159+
**Attack:** Network failure causes user to miss receipts
160+
161+
**Defense:** Receipt re-fetch endpoint allows recovery:
162+
- User calls `GET /api/receipts/:sessionId/:nonce` for any past receipt
163+
- User syncs all receipts before challenge window
164+
- User's responsibility to ensure they have receipts before disputing
165+
</Accordion>
166+
167+
<Accordion title="Facilitator inflates receipts (LIMITATION)" icon="triangle-exclamation">
168+
**Attack:** Facilitator adds fake receipts to merkle tree before committing
169+
170+
**Status:** NOT FULLY PREVENTED - this is a known limitation.
171+
172+
**Why it's hard:** Facilitator controls the merkle tree. They can add fake receipts before committing. Without per-request user acknowledgments, we cannot prove delivery.
173+
174+
**Mitigations (not cryptographic):**
175+
- User's exposure bounded by deposit (maxAmount)
176+
- Reputation damage deters fraud
177+
- User can stop using facilitator and reclaim remaining balance
178+
- Facilitator cannot capture MORE than tree total (still enforced)
179+
</Accordion>
180+
</AccordionGroup>
181+
182+
### Merkle Commitment
183+
184+
Facilitator batches receipts into a merkle tree. **This is critical for preventing receipt forgery** - once committed, the facilitator cannot create fake receipts.
185+
186+
| Property | Value |
187+
| -------- | ----- |
188+
| Leaf format | `keccak256(abi.encode(receipt))` |
189+
| Tree type | Binary merkle tree |
190+
| Commitment | `commitCapture(sessionId, merkleRoot, captureAmount)` |
191+
| Collateral | 10% of batch value |
192+
| Purpose | Prevents facilitator from forging receipts after commitment |
193+
194+
### Challenge Window
195+
196+
<AccordionGroup>
197+
<Accordion title="How disputes work" icon="gavel">
198+
1. Facilitator commits `captureAmount = $50` with collateral
199+
2. User sees capture, checks their latest receipt: `{ nonce: 80, cumulative: $30 }`
200+
3. User submits `disputeOverCapture(sessionId, userReceipt)`
201+
4. Contract verifies: facilitator signature valid, $30 < $50
202+
5. Dispute opens - facilitator has 24-48h to counter
203+
6. If no valid counter → `resolveDispute()` slashes facilitator, refunds user $20
204+
205+
**Counter-dispute flow:**
206+
- Facilitator submits `{ nonce: 100, cumulative: $50 }` receipt
207+
- Contract verifies: signature valid, nonce 100 > 80
208+
- User's dispute rejected (they had a higher receipt)
209+
</Accordion>
210+
<Accordion title="What can be disputed" icon="shield-exclamation">
211+
- **Over-capture**: User's highest receipt shows less than captured amount
212+
- **Invalid signature**: Facilitator signature on receipt doesn't verify
213+
</Accordion>
214+
<Accordion title="Window parameters" icon="clock">
215+
| Parameter | Value | Rationale |
216+
| --------- | ----- | --------- |
217+
| Duration | 3-7 days | Balance security vs UX |
218+
| Collateral | 10% of batch | Economic deterrent |
219+
| Slash | 100% of disputed amount | Full user refund |
220+
</Accordion>
221+
</AccordionGroup>
222+
223+
### Smart Contract Interface
224+
225+
```solidity
226+
interface ITrustlessEscrow {
227+
// Facilitator commits capture with merkle root of all receipts
228+
function commitCapture(
229+
bytes32 sessionId,
230+
bytes32 merkleRoot, // Root of all issued receipts
231+
uint256 captureAmount
232+
) external payable; // msg.value = collateral stake (10% of captureAmount)
233+
234+
// User disputes: "My receipt shows less than you captured"
235+
function disputeOverCapture(
236+
bytes32 sessionId,
237+
CumulativeReceipt calldata userReceipt // User's highest nonce receipt
238+
) external;
239+
// Contract verifies:
240+
// 1. facilitatorSignature is valid
241+
// 2. userReceipt.cumulativeAmount < captureAmount
242+
// If both true → dispute opened, facilitator must counter
243+
244+
// Facilitator counters with higher nonce receipt + merkle proof
245+
function counterDispute(
246+
bytes32 sessionId,
247+
CumulativeReceipt calldata facilitatorReceipt,
248+
bytes32[] calldata merkleProof // Proves receipt was in committed tree
249+
) external;
250+
// Contract verifies:
251+
// 1. facilitatorSignature is valid
252+
// 2. Receipt exists in committed merkle tree (prevents forgery!)
253+
// 3. facilitatorReceipt.nonce > userReceipt.nonce
254+
// If all true → dispute rejected (user was lying)
255+
256+
// After challenge window with no successful counter
257+
function resolveDispute(bytes32 sessionId) external;
258+
// Slashes facilitator, refunds user the difference
259+
260+
// After challenge window with no disputes
261+
function finalize(bytes32 sessionId) external;
262+
// Releases funds to receiver, returns collateral
263+
}
264+
265+
struct CumulativeReceipt {
266+
bytes32 sessionId;
267+
uint256 nonce;
268+
uint256 cumulativeAmount;
269+
uint256 timestamp;
270+
bytes facilitatorSignature; // EIP-712 over (sessionId, nonce, cumulativeAmount, timestamp)
271+
}
272+
```
273+
274+
**Verification logic:**
275+
```solidity
276+
function verifyReceipt(CumulativeReceipt calldata receipt) internal view returns (bool) {
277+
bytes32 structHash = keccak256(abi.encode(
278+
RECEIPT_TYPEHASH,
279+
receipt.sessionId,
280+
receipt.nonce,
281+
receipt.cumulativeAmount,
282+
receipt.timestamp
283+
));
284+
bytes32 digest = _hashTypedDataV4(structHash);
285+
address signer = ECDSA.recover(digest, receipt.facilitatorSignature);
286+
return signer == facilitatorAddress;
287+
}
288+
```
289+
290+
### Security Properties
291+
292+
| Property | Current | Trustless | Notes |
293+
| -------- | ------- | --------- | ----- |
294+
| Over-capture (capture > receipts) | Unverifiable | **Trustless** | User can prove with receipts |
295+
| Receipt inflation (fake receipts) | Trusted | **Trusted** | Can't prove delivery without acks |
296+
| Fraud detection | None | Challenge window | For provable over-capture |
297+
| Economic security | None | Collateral slashing | For provable fraud |
298+
| User recourse | Only reclaim | Dispute + refund | For over-capture only |
299+
| Client signing | Once | Once | Preserves UX |
300+
| Bounded exposure | maxAmount | maxAmount | Limits damage from inflation |
301+
302+
<Warning>
303+
**Limitation:** Facilitator can inflate receipts (add fake ones to merkle tree) because we cannot prove delivery without per-request user signatures. The protection is:
304+
- Facilitator cannot capture MORE than what's in their committed tree
305+
- User's exposure is bounded by their deposit (maxAmount)
306+
- Reputation damage from fraud is a deterrent
307+
</Warning>
308+
309+
## Trade-offs
310+
311+
### Advantages
312+
313+
- Users can cryptographically prove over-capture fraud
314+
- Economic incentive against fraud (collateral at risk)
315+
- Preserves "sign once" UX (no per-request client signatures)
316+
- Bounded exposure limits damage from any fraud
317+
318+
### Disadvantages
319+
320+
- 3-7 day finality delay (receiver waits for funds)
321+
- Client must store receipts locally (storage overhead)
322+
- Requires collateral capital from facilitator
323+
- More complex implementation
324+
- Higher gas costs (commitment transactions)
325+
- Facilitator must sign each response (compute overhead)
326+
- **Receipt inflation still trusted** - facilitator can add fake receipts to tree
327+
328+
## Migration Path
329+
330+
<Steps>
331+
<Step title="Phase 1: Trusted (Current)">
332+
Current model with trusted facilitator. Faster UX, simpler implementation. Trust assumptions clearly documented.
333+
</Step>
334+
<Step title="Phase 2: Hybrid (Opt-in)">
335+
Signed receipts as optional feature. Users who want trustless can enable. Default remains trusted for faster UX.
336+
</Step>
337+
<Step title="Phase 3: Full Trustless">
338+
Signed receipts mandatory. Dispute contract deployed. Watchtower service for monitoring. Deprecate trusted-only mode.
339+
</Step>
340+
</Steps>
341+
342+
## Open Questions
343+
344+
<AccordionGroup>
345+
<Accordion title="Challenge window duration" icon="clock">
346+
**3 days vs 7 days?**
347+
- Shorter = better UX for receivers (faster access to funds)
348+
- Longer = more time to detect and dispute fraud
349+
</Accordion>
350+
<Accordion title="Collateral source" icon="wallet">
351+
**Facilitator-funded vs protocol treasury?**
352+
- Facilitator stake = direct incentive alignment
353+
- Treasury = requires governance, less direct incentive
354+
</Accordion>
355+
<Accordion title="Watchtower service" icon="tower-observation">
356+
**Who monitors for fraud?**
357+
- Self-monitoring (users watch own sessions)
358+
- Decentralized watchtower network
359+
- Agentokratia-operated service
360+
</Accordion>
361+
<Accordion title="Gas sponsorship" icon="gas-pump">
362+
**Who pays for dispute transactions?**
363+
- User pays (may disincentivize small disputes)
364+
- Protocol subsidizes (encourages fraud detection)
365+
</Accordion>
366+
</AccordionGroup>
367+
368+
## References
369+
370+
- [Optimistic Rollups - ethereum.org](https://ethereum.org/developers/docs/scaling/optimistic-rollups/)
371+
- [Arbitrum Fraud Proofs](https://docs.arbitrum.io/how-arbitrum-works/fraud-proofs)
372+
- [Merkle Trees in Blockchains](https://www.alchemy.com/docs/merkle-trees-in-blockchains)
373+
- [State Channels - ethereum.org](https://ethereum.org/developers/docs/scaling/state-channels/)

0 commit comments

Comments
 (0)