igneum/tools/reference-apps/oracle/README.md

3.3 KiB

Igneum Devnet 3 state oracle on Sepolia

A contract on Ethereum Sepolia that stores proven Devnet 3 state roots and lets other contracts read a proven Devnet 3 balance or storage slot. Certificates are read from the shared verifier (IIgneumCertificateVerifier); a StubCertificateVerifier stands in until the real one lands (setVerifier, deployer only, swaps it).

Addresses, transaction hashes, blocks and gas are in deployment.json (every write is appended under writes).

What the oracle checks on chain

  1. Every header in the path is hashed with keyed BLAKE2b-256 ("BlockHash") through the EIP-152 precompile and parsed out of the same bytes. Each next header must list the previous hash among its level-0 parents. The last hash must be the checkpoint the verifier holds for the given certificate index.
  2. The carrier's coinbase transaction is hashed ("TransactionHash") and the merkle path ("MerkleBranchHash", a missing right child is 32 zero bytes) must reach the carrier's hash_merkle_root, parsed from the header bytes.
  3. The coinbase payload is walked from the transaction bytes (so it is bound to the hash); the nested sections IGNS || IGNP || IGNF are taken from the end; the chosen 586-byte segment record gives the statement's number, block hash and post_root (number must equal the record's last, block hash must equal the record's block, chain id must be Devnet 3's 0x116f). The root is stored under the number with the certificate index.
  4. provenBalance, provenAccount and provenStorage verify eth_getProof account and storage proofs against the stored post_root with the contract's own RLP and keccak-keyed Merkle Patricia trie walk, and revert with a reason on any mismatch.

Not checked on chain in this slice (also stated by trust()): the aggregator's BLS signature over the segment record and the ZK proof behind it. Also not checked: the segment record's version field (the layout is fixed either way).

Files

  • contracts/Blake2b.sol: keyed BLAKE2b-256 over the precompile at 0x09.
  • contracts/Mpt.sol: RLP reading, MPT proofs, account and storage decoding.
  • contracts/IIgneumCertificateVerifier.sol, contracts/StubCertificateVerifier.sol, contracts/IgneumStateOracle.sol.
  • compile.mjs: solc-js (via IR, optimizer 200, cancun) to artifacts/*.json.
  • deploy.mjs: EIP-1559 deploy of the stub and the oracle, writes deployment.json (--force, --oracle-only).
  • demo.mjs: submits the certificate and the state root for fixtures/dn3-balance.json (or the synthetic vector when the fixture is absent) and prints the Sepolia-read balance beside the fixture's.
  • test.mjs: the vectors and negatives through eth_call. Part A is the receipt fixture (real headers, a real merkle path); part B is a seeded synthetic vector (segment record, coinbase, three-header path, account and storage proofs); part C is the balance fixture when it exists.
  • lib.mjs: the byte layouts, a JS mirror of the BLAKE2b driver (checked against @noble/hashes), the trie builder.

Run

node compile.mjs
node deploy.mjs
node test.mjs
node demo.mjs

The deployer key is read from ~/.config/igneum/sepolia-deployer. Sepolia's gas schedule is repriced (a plain transfer estimates 12,000 gas), so the numbers in deployment.json are what this network charges.