|
|
||
|---|---|---|
| .. | ||
| artifacts | ||
| contracts | ||
| .gitignore | ||
| compile.mjs | ||
| demo.mjs | ||
| deploy.mjs | ||
| deployment.json | ||
| lib.mjs | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| real-verifier.mjs | ||
| test.mjs | ||
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
- 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.
- 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.
- The coinbase payload is walked from the transaction bytes (so it is bound to the hash); the nested sections
IGNS || IGNP || IGNFare 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'slast, 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. provenBalance,provenAccountandprovenStorageverify 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) toartifacts/*.json.deploy.mjs: EIP-1559 deploy of the stub and the oracle, writesdeployment.json(--force,--oracle-only).demo.mjs: submits the certificate and the state root forfixtures/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.
Recovery locks (Review B F04, 8 October 2026)
Recovery locks are not accepted by this verifier: a certificate under half of the installed table's weight reverts in submitCertificate (both Sepolia verifiers apply the two-thirds rule only), so every stored root passed the final rule and nothing the oracle answers can read final for a recovery lock. trust() gains this sentence at the next redeploy; the page carries it now.