Verifying proofs
Willow proofs can be verified without any request to Willow beyond fetching the proof. Two verifiers are available: a browser page, and a command-line verifier that runs on a local machine. Both perform the same steps, which are listed below so that an independent verifier can also be written.
Trust assumptions
Section titled “Trust assumptions”A log proof relies on three assumptions:
- One block hash. It is confirmed with a node of the chain selected by the verifying party, or the browser page verifies it against Ethereum. That verification relies on Ethereum’s sync committee and, on an L2, on the chain’s fault proofs or its sequencer.
- The soundness of SP1, the proof system with which the proofs are produced.
- The proving programs, whose verifying keys the verifier pins.
No other dependency on Willow’s server exists: its eth_getLogs response and
all data sent with the proof are verified. A transaction or receipt proof
relies on one block hash, Keccak-256, the chain’s signature scheme, and the
verifier’s code (see
Transaction and receipt proofs).
In the browser
Section titled “In the browser”Open api.willow.tech/verify, select
Logs, the chain, and a filter, and press Verify. The page supports all
seven chains. It downloads the SP1 proofs and verifies each one in the browser,
verifies the range proofs and the links between parts, verifies the last
block, and compares Willow’s eth_getLogs response with the proven logs.
The page verifies the block independently. On Ethereum, it follows the validators’ signatures from a checkpoint built into the page. On the other six chains, it reads the chain’s claim on Ethereum, which settles once the challenge period ends, and verifies newer blocks by the sequencer’s signature. If Willow cannot provide that data, the page queries public nodes and displays their response in gold, never as verified.
Under Advanced:
- Willow RPC endpoint. By default it ends in
public, which requires no key and verifies blocks Willow has already proven. With an API key in its place, Willow also proves new blocks on request, and those proofs are charged to the key. - Ethereum node, through which Ethereum is read instead of public nodes.
- Another provider for comparison. Its response is also compared with the proven one. It must allow browser requests.
- The program keys the page pins. Replace them only if Willow announces that a new program is in use.
On the command line
Section titled “On the command line”willow-logs-verify performs the same steps on a local machine, on all seven
chains, without relying on the page’s code. It also verifies transaction and
receipt proofs. It is available on request from
[email protected].
Verification steps
Section titled “Verification steps”For a log proof, the verifier:
- Calls
willow_getLogsProofwith explicit block numbers. - Fetches each part’s SP1 proof with
willow_getProofand verifies it against the pinned program keys. - Verifies that the parts cover the entire range without gaps, and that each part’s parent hash equals the previous part’s last block hash.
- For segment parts, determines from the filter itself which index ranges must be proven, then verifies each range proof against the segment’s committed index. The server therefore cannot determine what counts as complete. For block and leaf parts, verifies that each block’s entries hash to the proven root.
- Applies the filter to the proven entries. The result is the proven list of logs.
- Confirms the last block’s hash with a node of the chain selected by the verifying party (the browser page verifies it against Ethereum instead).
- Compares an
eth_getLogsresponse with the proven logs on the six proven fields, matched by block number and log index.
Transaction and receipt proofs
Section titled “Transaction and receipt proofs”In the browser. On api.willow.tech/verify, select Transaction or Block and the chain, and enter a transaction hash or a block. The page verifies the proof in the browser, verifies the block against Ethereum as described above, and compares Willow’s own responses with the proven ones, field by field. It lists the proven fields and identifies the fields the proof does not cover.
On the command line:
willow-logs-verify --rpc https://api.willow.tech/v1/rpc/1/{YOUR_KEY} \ --receipt 0x4560d377ceb62f875b054d83a86caabe34e7f681271f2d204ac692a472cf987c \ --compare-rpc https://your-provider.example/rpc \ --anchor-rpc https://your-node.example/rpc--tx and --receipt take a transaction hash or <block>:<index>, and
--block-receipts takes a block number, a block hash, or latest. The
verifier:
- Calls
willow_getTransactionProof,willow_getReceiptProof, orwillow_getBlockReceiptsProofwith the given hash or position. - Verifies that the header hashes to
blockHash, and that the block and transaction are those requested. - Verifies every item against the header’s roots, either by walking each item proof from the root or by reconstructing both roots from a whole block’s items.
- Computes the standard response from the proven bytes.
- Confirms the block hash with the specified node (
--anchor-rpc). - Compares another endpoint’s response (
--compare-rpc) with the proven one, field by field.
Output for the request above, compared with a public Ethereum node:
{ "block": { "canonical": true, "hash": "0xdb98a28f352acb72751ec8012c5a617acfb2d7469aadb9bc4690db6b6dd8d53f", "number": "0x18e8f2a" }, "chain": "ethereum", "chainId": 1, "compare": { "eth_getTransactionReceipt": { "differ": [], "matched": 94, "omitted": [], "unproven": [] } }, "method": "willow_getReceiptProof", "proofBytes": 12981, "transactionHash": "0x4560d377ceb62f875b054d83a86caabe34e7f681271f2d204ac692a472cf987c", "transactionIndex": "0x0", "unproven": [], "verified": true, "verifySeconds": 0.010043125}unproven: the fields of the response that this proof does not cover.compare:matchedcounts the proven values that the other endpoint reports identically.differlists every value it reports differently, and every proven field it omits.unprovenlists the fields of its response that this proof does not cover, andomittedthe proven fields that some nodes do not report:blockTimestamp(omitted, or0x0on logs from Robinhood Chain’s own RPC), and a deposit’snonceanddepositReceiptVersionon some OP Mainnet nodes.
The verifier exits with status 1 if the proof fails verification, a value differs, or the block is not on the specified node’s chain.