@nori-zk/ethprocessor
Version:
zkApp for verifying SP1 Helios Nori proof and storing latest execution state root on Mina
158 lines • 8.09 kB
JavaScript
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