Skip to content

Commit 4bf9fb5

Browse files
committed
Refresh v1-comparison page with TLS proofs, plugin system, and FHE sections
1 parent 594f546 commit 4bf9fb5

2 files changed

Lines changed: 127 additions & 22 deletions

File tree

book/docs/concepts/fhe-encryption.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ exists as an opaque handle — a reference to encrypted data stored by the FHE c
2121
arithmetic and comparisons on the encrypted values, but only addresses granted permission by the consuming contract can
2222
decrypt and read the plaintext.
2323

24-
This is different from regular encryption (like the [encrypted channel plugin](/plugins#encrypted-channel)). Regular
25-
encryption requires decrypting before doing anything useful. FHE lets the contract use the data while it's still
26-
encrypted — a lending protocol can check `is price < liquidation threshold` and get a boolean result without either
27-
value ever being revealed.
24+
This is different from regular encryption (like the [encrypted channel plugin](/docs/plugins)). Regular encryption
25+
requires decrypting before doing anything useful. FHE lets the contract use the data while it's still encrypted — a
26+
lending protocol can check `is price < liquidation threshold` and get a boolean result without either value ever being
27+
revealed.
2828

2929
## How it works
3030

31-
FHE encryption is implemented as an Airnode [plugin](/plugins) that intercepts the ABI-encoded response in the
31+
FHE encryption is implemented as an Airnode [plugin](/docs/plugins) that intercepts the ABI-encoded response in the
3232
`onBeforeSign` hook:
3333

3434
1. Airnode calls the upstream API and ABI-encodes the response as usual.

book/docs/v1-comparison.md

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ rules. Two independent operators serving the same API with the same config produ
7373

7474
**Why:** Specification-bound IDs enable cross-operator comparability without a registry. A quorum verifier can confirm
7575
that multiple airnodes signed data for the same endpoint ID — meaning they all committed to calling the same API the
76-
same way. When TLS proofs mature, the endpoint ID can be verified against the proven HTTP request on-chain.
76+
same way. TLS proofs extend this: the endpoint ID can be cross-checked against the proven HTTP request that backs the
77+
response.
7778

7879
## Signature format
7980

@@ -82,7 +83,126 @@ v1 signed `keccak256(requestId, timestamp, airnodeAddress, data)` where the requ
8283

8384
v2 signs `keccak256(encodePacked(endpointId, timestamp, data))` where the endpoint ID is derived from the API spec. The
8485
endpoint ID, timestamp, and data are separate top-level fields — not nested inside another hash — so on-chain contracts
85-
and future TLS proof verifiers can inspect each field independently.
86+
and TLS proof verifiers can inspect each field independently.
87+
88+
## TLS proofs for data provenance
89+
90+
v1 had no way to prove that signed data actually came from the claimed upstream API. The EIP-191 signature only proved
91+
_who_ signed — not _where the data came from_. A compromised or dishonest operator could fabricate responses and sign
92+
them.
93+
94+
v2 integrates [TLS proofs](/docs/concepts/proofs) via [Reclaim Protocol](https://reclaimprotocol.org/). When enabled, an
95+
independent attestor participates in the upstream TLS session over MPC-TLS and signs a claim that the response actually
96+
came from the declared HTTPS endpoint and matched the configured `responseMatches` patterns. Airnode attaches the proof
97+
to the response alongside the signature.
98+
99+
```yaml
100+
settings:
101+
proof:
102+
type: reclaim
103+
gatewayUrl: http://localhost:5177/v1/prove
104+
105+
apis:
106+
- name: CoinGecko
107+
# ...
108+
endpoints:
109+
- name: coinPrice
110+
# ...
111+
responseMatches:
112+
- type: regex
113+
value: '"usd":\s*(?<price>[\d.]+)'
114+
```
115+
116+
Proof generation is **non-fatal** — if the gateway is unavailable, Airnode still returns the signed response without the
117+
`proof` field and logs a warning. Consumers that require provenance simply reject responses that lack a `proof`.
118+
119+
**Why:** Signatures answer "who endorsed this data." TLS proofs answer "did this data really come from the API." Pairing
120+
them turns an airnode from a trusted relay into a verifiable relay — the operator can no longer forge upstream responses
121+
undetected.
122+
123+
## A real plugin system
124+
125+
v1 had no plugin mechanism — custom behaviour meant forking the node. Every custom auth check, metric, or response
126+
transform bled into a maintenance burden the operator carried alone.
127+
128+
v2 exposes a [plugin system](/docs/plugins) with six hooks that fire at well-defined points in the request pipeline:
129+
130+
| Hook | Type | When it fires |
131+
| ----------------- | ----------- | -------------------------------------- |
132+
| `onHttpRequest` | Mutation | After endpoint resolution, before auth |
133+
| `onBeforeApiCall` | Mutation | Before the upstream API call |
134+
| `onAfterApiCall` | Mutation | After the upstream API responds |
135+
| `onBeforeSign` | Mutation | After encoding, before signing |
136+
| `onResponseSent` | Observation | After the signed response is sent |
137+
| `onError` | Observation | When an error occurs at any stage |
138+
139+
Plugins are ordinary modules loaded from a path in config, with per-request time budgets enforced by the runtime.
140+
Mutation hooks that fail or time out **drop** the request (fail-closed — no data leaks past a broken security plugin);
141+
observation hooks are fire-and-forget.
142+
143+
The pipeline is powerful enough that several headline v2 capabilities are built as plugins rather than core features:
144+
145+
- **`fhe-encrypt`** — encrypts the signed payload for on-chain confidential compute (see below)
146+
- **`encrypted-channel`** — ECIES-encrypts responses end-to-end to a requester's ephemeral key
147+
- **`heartbeat`**, **`logger`**, **`slack-alerts`** — operational observability
148+
149+
**Why:** Airnode operators have wildly different needs — custom authorization, bespoke upstream protocols, private
150+
metrics, paid-data gating. A stable hook surface lets those live alongside the core node instead of forking it, and
151+
keeps the core small enough to audit.
152+
153+
## FHE encryption for confidential on-chain data
154+
155+
v1 had no notion of confidential data. Every signed value was public the moment it landed on-chain — visible in calldata
156+
before inclusion (enabling front-running) and readable from storage afterward (making it impossible to sell exclusive
157+
data or keep valuations private).
158+
159+
v2 ships an [FHE encryption plugin](/docs/concepts/fhe-encryption) built on [Zama's fhEVM](https://docs.zama.ai/fhevm).
160+
The plugin intercepts the ABI-encoded response in the `onBeforeSign` hook, encrypts it with the target chain's FHE
161+
public key, and packs the resulting `(einput, inputProof)` pair as the new `data` field. Airnode signs the ciphertext,
162+
so the signature proves the encrypted data is authentic without ever revealing plaintext.
163+
164+
```
165+
API response → ABI encode → [fhe-encrypt plugin] → sign(ciphertext) → return to client
166+
167+
encrypt with chain's FHE public key
168+
pack (einput, inputProof) into data field
169+
```
170+
171+
Because FHE is homomorphic, the callback contract can compute directly on the ciphertext —
172+
`TFHE.gt(price, liquidationThreshold)` returns an encrypted boolean without either value ever becoming public.
173+
Per-handle on-chain ACLs determine who is allowed to decrypt.
174+
175+
**Why:** Public oracle data leaks value. Searchers front-run price updates, premium data leaks to non-payers the instant
176+
it's consumed, and confidential valuations can't be delivered at all. FHE lets contracts use oracle data while it stays
177+
encrypted — enabling MEV-protected feeds, paid-data access control, sealed auctions, and confidential RWA pricing on the
178+
same signing and verification path as any other Airnode response. The existing `AirnodeVerifier` contract works
179+
unchanged.
180+
181+
## Response caching
182+
183+
v1 had no response cache. Every request hit the upstream API, which was wasteful for endpoints with long-lived data
184+
(e.g. daily FX rates) and couldn't absorb bursts without rate-limiting the origin.
185+
186+
v2 has an in-memory response cache with configurable TTL, keyed by `(endpointId, sorted parameters)`. Cache config is
187+
set per-API and can be overridden per-endpoint:
188+
189+
```yaml
190+
apis:
191+
- name: CoinGecko
192+
cache:
193+
maxAge: 30000 # 30 seconds
194+
endpoints:
195+
- name: coinPrice
196+
# inherits the 30s cache
197+
- name: realtimeTicker
198+
cache:
199+
maxAge: 1000 # override to 1 second
200+
```
201+
202+
Entries are bounded (10,000 entries by default) and swept on a periodic timer. No external cache server is required.
203+
204+
**Why:** Long-running processes can hold state — caching is free in this model and valuable in practice. Most oracle
205+
endpoints are called far more often than their underlying data changes.
86206

87207
## Other improvements
88208

@@ -97,21 +217,6 @@ per endpoint (any-of semantics).
97217
v2 endpoints support three modes: `sync` (default request-response), `async` (return 202, poll for result), and `stream`
98218
(Server-Sent Events). v1 only supported synchronous request-response.
99219

100-
### Plugin system
101-
102-
v2 has a plugin system with six hooks (`onHttpRequest`, `onBeforeApiCall`, `onAfterApiCall`, `onBeforeSign`,
103-
`onResponseSent`, `onError`) and per-request time budgets. v1 had no plugin mechanism — custom logic required forking
104-
the node.
105-
106-
### Caching
107-
108-
v2 caches responses in memory with configurable TTL.
109-
110-
### Solidity contract
111-
112-
v2 uses a single minimal Solidity contract (AirnodeVerifier) instead of v1's 30+ contracts. The test suite includes
113-
unit, invariant (stateful fuzz), and symbolic (Halmos) tests.
114-
115220
### Language and runtime
116221

117222
v1 was a TypeScript monorepo with 10+ packages, Hardhat for testing, and ethers.js for chain interaction. v2 is a single

0 commit comments

Comments
 (0)