UNPKG

@nori-zk/ethprocessor

Version:

zkApp for verifying SP1 Helios Nori proof and storing latest execution state root on Mina

158 lines 8.09 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import 'dotenv/config'; import { Field, PrivateKey, SmartContract, State, method, state, Poseidon, UInt64, PublicKey, Permissions, Provable, Struct, } from 'o1js'; import { Logger } from '@nori-zk/proof-conversion'; import { EthProof } from './EthVerifier.js'; import { Bytes32, Bytes32FieldPair } from './types.js'; const logger = new Logger('EthProcessor'); let adminPrivateKeyBase58 = process.env.ADMIN_PRIVATE_KEY; if (!adminPrivateKeyBase58) { logger.warn('ADMIN_PRIVATE_KEY not set, using random key'); adminPrivateKeyBase58 = PrivateKey.random().toBase58(); } export const adminPrivateKey = PrivateKey.fromBase58(adminPrivateKeyBase58); export const adminPublicKey = adminPrivateKey.toPublicKey(); export class EthProofType extends EthProof { } class VerificationKey extends Struct({ data: String, hash: Field, }) { } class DeployArgsWithStoreHash extends Struct({ verificationKey: VerificationKey, storeHash: Bytes32FieldPair, }) { } class DeployArgsWithoutStoreHash extends Struct({ verificationKey: VerificationKey, }) { } export class EthProcessor extends SmartContract { constructor() { super(...arguments); this.admin = State(); this.verifiedStateRoot = State(); // todo make PackedString this.latestHead = State(); this.latestHeliusStoreInputHashHighByte = State(); this.latestHeliusStoreInputHashLowerBytes = State(); this.latestVerifiedContractDepositsRootHighByte = State(); this.latestVerifiedContractDepositsRootLowerBytes = State(); } //todo // events = { 'executionStateRoot-set': Bytes32.provable };//todo change type, if events even possible init() { super.init(); this.admin.set(adminPublicKey); this.latestHead.set(UInt64.from(0)); this.verifiedStateRoot.set(Field(1)); this.account.permissions.set({ ...Permissions.default(), }); } async deploy(args) { // Could we deploy with a proof? const { verificationKey } = args; super.deploy({ verificationKey }); if ('storeHash' in args) { this.latestHeliusStoreInputHashHighByte.set(args.storeHash.highByteField); this.latestHeliusStoreInputHashLowerBytes.set(args.storeHash.lowerBytesField); } //this.verifiedStateRoot.set(Field(2)); // Need to prove this otherwise its bootstrapped in an invalid state } // @method async init() { // this.account.provedState.getAndRequireEquals(); // this.account.provedState.get().assertFalse(); // super.init(); // } async update(ethProof) { const proofHead = ethProof.publicInput.outputSlot; const executionStateRoot = ethProof.publicInput.executionStateRoot; const currentSlot = this.latestHead.getAndRequireEquals(); const newStoreHash = Bytes32FieldPair.fromBytes32(ethProof.publicInput.outputStoreHash); Provable.asProver(() => { Provable.log('Proof input store hash values were:'); Provable.log(ethProof.publicInput.outputStoreHash.bytes[0].value); Provable.log(ethProof.publicInput.outputStoreHash.bytes .slice(1, 33) .map((b) => b.value)); Provable.log('Public outputs created:', newStoreHash.highByteField, newStoreHash.lowerBytesField); }); Provable.asProver(() => { Provable.log('Current slot', currentSlot); }); const prevStoreHash = Bytes32FieldPair.fromBytes32(ethProof.publicInput.inputStoreHash); // Verification of the previous store hash higher byte. prevStoreHash.highByteField.assertEquals(this.latestHeliusStoreInputHashHighByte.getAndRequireEquals(), "The latest transition proofs' input helios store hash higher byte, must match the contracts' helios store hash higher byte."); Provable.asProver(() => { Provable.log('ethProof.prevStoreHashHighByteField vs this.latestHeliusStoreInputHashHighByte', prevStoreHash.highByteField.toString(), this.latestHeliusStoreInputHashHighByte.get().toString()); }); // Verification of previous store hash lower bytes. prevStoreHash.lowerBytesField.assertEquals(this.latestHeliusStoreInputHashLowerBytes.getAndRequireEquals(), "The latest transition proofs' input helios store hash lower bytes, must match the contracts' helios store hash lower bytes."); Provable.asProver(() => { Provable.log('ethProof.prevStoreHashLowerBytesField vs this.latestHeliusStoreInputHashLowerBytes', prevStoreHash.lowerBytesField.toString(), this.latestHeliusStoreInputHashLowerBytes.get().toString()); }); // Verification of slot progress. Moved to the bottom to allow us to test hash mismatches do indeed yield validation errors. proofHead.assertGreaterThan(currentSlot, 'Proof head must be greater than current head.'); // Verification that next sync commitee is non zero (could brick the bridge head otherwise) let nextSyncCommitteeZeroAcc = new Field(0); for (let i = 0; i < 32; i++) { nextSyncCommitteeZeroAcc = nextSyncCommitteeZeroAcc.add(ethProof.publicInput.nextSyncCommitteeHash.bytes[i].value); } nextSyncCommitteeZeroAcc.assertNotEquals(new Field(0)); // Verify transition proof. ethProof.verify(); // Pack the verifiedContractDepositsRoot into a pair of fields const verifiedContractDepositsRoot = Bytes32FieldPair.fromBytes32(ethProof.publicInput.verifiedContractDepositsRoot); // Update contract values this.latestHead.set(proofHead); this.verifiedStateRoot.set(Poseidon.hashPacked(Bytes32.provable, executionStateRoot)); this.latestHeliusStoreInputHashHighByte.set(newStoreHash.highByteField); this.latestHeliusStoreInputHashLowerBytes.set(newStoreHash.lowerBytesField); this.latestVerifiedContractDepositsRootHighByte.set(verifiedContractDepositsRoot.highByteField); this.latestVerifiedContractDepositsRootLowerBytes.set(verifiedContractDepositsRoot.lowerBytesField); } } __decorate([ state(PublicKey), __metadata("design:type", Object) ], EthProcessor.prototype, "admin", void 0); __decorate([ state(Field), __metadata("design:type", Object) ], EthProcessor.prototype, "verifiedStateRoot", void 0); __decorate([ state(UInt64), __metadata("design:type", Object) ], EthProcessor.prototype, "latestHead", void 0); __decorate([ state(Field), __metadata("design:type", Object) ], EthProcessor.prototype, "latestHeliusStoreInputHashHighByte", void 0); __decorate([ state(Field), __metadata("design:type", Object) ], EthProcessor.prototype, "latestHeliusStoreInputHashLowerBytes", void 0); __decorate([ state(Field), __metadata("design:type", Object) ], EthProcessor.prototype, "latestVerifiedContractDepositsRootHighByte", void 0); __decorate([ state(Field), __metadata("design:type", Object) ], EthProcessor.prototype, "latestVerifiedContractDepositsRootLowerBytes", void 0); __decorate([ method, __metadata("design:type", Function), __metadata("design:paramtypes", [EthProofType]), __metadata("design:returntype", Promise) ], EthProcessor.prototype, "update", null); //# sourceMappingURL=EthProcessor.js.map