UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

132 lines (109 loc) 3.5 kB
import { Field, state, State, method, SmartContract, Mina, AccountUpdate, ZkappPublicInput, SelfProof, verify, Empty, } from 'o1js'; class TrivialZkapp extends SmartContract { @method async proveSomething(hasToBe1: Field) { hasToBe1.assertEquals(1); } } class TrivialProof extends TrivialZkapp.Proof() {} class NotSoSimpleZkapp extends SmartContract { @state(Field) x = State<Field>(); @method async initialize(proof: TrivialProof) { proof.verify(); this.x.set(Field(1)); } @method async update( y: Field, oldProof: SelfProof<ZkappPublicInput, Empty>, trivialProof: TrivialProof ) { oldProof.verify(); trivialProof.verify(); let x = this.x.get(); this.x.requireEquals(x); this.x.set(x.add(y)); } } let Local = await Mina.LocalBlockchain(); Mina.setActiveInstance(Local); let [feePayer] = Local.testAccounts; const [trivialContractAccount, notSoSimpleContractAccount] = Mina.TestPublicKey.random(2); // compile and prove trivial zkapp console.log('compile (trivial zkapp)'); let { verificationKey: trivialVerificationKey } = await TrivialZkapp.compile(); // TODO: should we have a simpler API for zkapp proving without // submitting transactions? or is this an irrelevant use case? // would also improve the return type -- `Proof` instead of `(Proof | undefined)[]` console.log('prove (trivial zkapp)'); let [trivialProof] = await Mina.transaction(feePayer, async () => { await new TrivialZkapp(notSoSimpleContractAccount).proveSomething(Field(1)); }) .prove() .proofs(); trivialProof = await testJsonRoundtripAndVerify(TrivialProof, trivialProof, trivialVerificationKey); console.log('compile'); let { verificationKey } = await NotSoSimpleZkapp.compile(); let zkapp = new NotSoSimpleZkapp(trivialContractAccount); console.log('deploy'); await Mina.transaction(feePayer, async () => { AccountUpdate.fundNewAccount(feePayer); await zkapp.deploy(); }) .prove() .sign([feePayer.key, trivialContractAccount.key]) .send(); console.log('initialize'); let tx = await Mina.transaction(feePayer, async () => { await zkapp.initialize(trivialProof!); }) .prove() .sign([feePayer.key]); let [proof] = tx.proofs; await tx.send(); proof = await testJsonRoundtripAndVerify(NotSoSimpleZkapp.Proof(), proof, verificationKey); console.log('initial state: ' + zkapp.x.get()); console.log('update'); tx = await Mina.transaction(feePayer, async () => { await zkapp.update(Field(3), proof!, trivialProof!); }) .prove() .sign([feePayer.key]); [proof] = tx.proofs; await tx.send(); proof = await testJsonRoundtripAndVerify(NotSoSimpleZkapp.Proof(), proof, verificationKey); console.log('state 2: ' + zkapp.x.get()); console.log('update'); tx = await Mina.transaction(feePayer, async () => { await zkapp.update(Field(3), proof!, trivialProof!); }) .prove() .sign([feePayer.key]); [proof] = tx.proofs; await tx.send(); proof = await testJsonRoundtripAndVerify(NotSoSimpleZkapp.Proof(), proof, verificationKey); console.log('final state: ' + zkapp.x.get()); async function testJsonRoundtripAndVerify( Proof: any, proof: any, verificationKey: { data: string } ): Promise<any> { let jsonProof = proof.toJSON(); console.log( 'json proof:', JSON.stringify({ ...jsonProof, proof: jsonProof.proof.slice(0, 10) + '..' }) ); let ok = await verify(jsonProof, verificationKey.data); if (!ok) throw Error('proof cannot be verified'); return Proof.fromJSON(jsonProof); }