@nori-zk/ethprocessor
Version:
zkApp for verifying SP1 Helios Nori proof and storing latest execution state root on Mina
109 lines • 5.47 kB
JavaScript
import path from 'path';
import { fileURLToPath } from 'url';
import { UInt64, UInt8 } from 'o1js';
import { wordToBytes } from '@nori-zk/proof-conversion';
import { EthVerifier } from './EthVerifier.js';
import { EthProcessor } from './EthProcessor.js';
import { Bytes32 } from './types.js';
import { ethVerifierVkHash } from './integrity/EthVerifier.VKHash.js';
import { ethProcessorVkHash } from './integrity/EthProcessor.VKHash.js';
// DEPRECATED
export function padUInt64To32Bytes(num) {
let unpadded = [];
unpadded = wordToBytes(num.toFields()[0]);
return [...unpadded, ...Array(24).fill(UInt8.from(0))].reverse();
}
// This is explicitly here for validation puposes not supposed to be provable.
function toBigIntFromBytes(bytes) {
let result = 0n;
for (const byte of bytes) {
result = (result << 8n) | BigInt(byte);
}
return result;
}
// This is explicitly here for validation puposes not supposed to be provable.
const MAX_U64 = (1n << 64n) - 1n;
function assertUint64(value) {
if (value < 0n || value > MAX_U64) {
throw new RangeError(`Value out of range for u64: '${value}'.`);
}
}
// Proof decoder
const proofOffsets = {
inputSlot: 0,
inputStoreHash: 8,
outputSlot: 40,
outputStoreHash: 48,
executionStateRoot: 80,
verifiedContractStorageSlotsRoot: 112,
nextSyncCommitteeHash: 144,
};
const proofTotalLength = 176;
export function decodeConsensusMptProof(ethSP1Proof) {
const proofData = new Uint8Array(Buffer.from(ethSP1Proof.public_values.buffer.data));
if (proofData.length !== proofTotalLength) {
throw new Error(`Byte slice must be exactly ${proofTotalLength} bytes, got '${proofData.length}'.`);
}
const inputSlotSlice = proofData.slice(proofOffsets.inputSlot, proofOffsets.inputStoreHash);
const inputSlot = toBigIntFromBytes(inputSlotSlice);
assertUint64(inputSlot);
const inputStoreHashSlice = proofData.slice(proofOffsets.inputStoreHash, proofOffsets.outputSlot);
const outputSlotSlice = proofData.slice(proofOffsets.outputSlot, proofOffsets.outputStoreHash);
const outputSlot = toBigIntFromBytes(outputSlotSlice);
assertUint64(outputSlot);
const outputStoreHashSlice = proofData.slice(proofOffsets.outputStoreHash, proofOffsets.executionStateRoot);
const executionStateRootSlice = proofData.slice(proofOffsets.executionStateRoot, proofOffsets.verifiedContractStorageSlotsRoot);
const verifiedContractStorageSlotsRootSlice = proofData.slice(proofOffsets.verifiedContractStorageSlotsRoot, proofOffsets.nextSyncCommitteeHash);
const nextSyncCommitteeHashSlice = proofData.slice(proofOffsets.nextSyncCommitteeHash, proofTotalLength);
const provables = {
inputSlot: UInt64.from(inputSlot),
inputStoreHash: Bytes32.from(inputStoreHashSlice),
outputSlot: UInt64.from(outputSlot),
outputStoreHash: Bytes32.from(outputStoreHashSlice),
executionStateRoot: Bytes32.from(executionStateRootSlice),
verifiedContractDepositsRoot: Bytes32.from(verifiedContractStorageSlotsRootSlice),
nextSyncCommitteeHash: Bytes32.from(nextSyncCommitteeHashSlice),
};
return provables;
}
// Compile and verify contracts utility
export async function compileAndVerifyContracts(logger) {
try {
logger.log('Compiling EthVerifier contract.');
const ethVerifierVerificationKey = (await EthVerifier.compile())
.verificationKey;
const calculatedEthVerifierVkHash = ethVerifierVerificationKey.hash.toString();
logger.log(`Verifier contract vk hash compiled: '${calculatedEthVerifierVkHash}'.`);
logger.log('Compiling EthProcessor contract.');
const ethProcessorVerificationKey = (await EthProcessor.compile())
.verificationKey;
// console.log(await EthProcessor.analyzeMethods()); // Used for debugging to make sure our contract compiles fully
const calculatedEthProcessorVKHash = ethProcessorVerificationKey.hash.toString();
logger.log(`EthProcessor contract vk hash compiled: '${calculatedEthProcessorVKHash}'.`);
// Validation
logger.log('Verifying computed Vk hashes.');
let disagree = [];
if (calculatedEthVerifierVkHash !== ethVerifierVkHash) {
disagree.push(`Computed ethVerifierVkHash '${calculatedEthVerifierVkHash}' disagrees with the one cached within this repository '${ethVerifierVkHash}'.`);
}
if (calculatedEthProcessorVKHash !== ethProcessorVkHash) {
disagree.push(`Computed ethProcessorVKHash '${calculatedEthProcessorVKHash}' disagrees with the one cached within this repository '${ethProcessorVkHash}'.`);
}
if (disagree.length) {
disagree.push(`Refusing to start. Try clearing your o1js cache directory, typically found at '~/.cache/o1js'. Or do you need to run 'npm run bake-vk-hashes' in the eth-processor repository and commit the change?`);
const errStr = disagree.join('\n');
throw new Error(errStr);
}
logger.log('Contracts compiled.');
return { ethVerifierVerificationKey, ethProcessorVerificationKey };
}
catch (err) {
console.log(err.stack);
logger.error(`Error compiling contracts:\n${String(err)}`);
throw err;
}
}
// Root dir
const __filename = fileURLToPath(import.meta.url);
export const rootDir = path.dirname(__filename);
//# sourceMappingURL=utils.js.map