ZkProgram Proofs as Inputs
ZkProgram proofs can be used as inputs to zkApp methods, enabling powerful composition patterns. This allows you to verify complex computations off-chain and use the verified results in your smart contracts.
Basic ZkProgram Integration
Define a ZkProgram and use its proofs in a smart contract:
export const BasicMathProgram = ZkProgram({
name: 'BasicMathProgram',
publicInput: Field,
publicOutput: Field,
methods: {
square: {
privateInputs: [],
async method(input: Field) {
return input.mul(input);
},
},
multiply: {
privateInputs: [Field],
async method(publicInput: Field, privateInput: Field) {
return publicInput.mul(privateInput);
},
},
},
});
export class BasicMathProof extends ZkProgram.Proof(BasicMathProgram) {}
export class ProofVerifierContract extends SmartContract {
@state(Field) verifiedResult = State<Field>();
@state(UInt32) proofCount = State<UInt32>();
init() {
super.init();
this.verifiedResult.set(Field(0));
this.proofCount.set(UInt32.from(0));
}
@method async verifySquareProof(proof: BasicMathProof) {
// Verify the proof is valid
proof.verify();
// Ensure it's a square operation proof
proof.publicOutput.assertEquals(proof.publicInput.mul(proof.publicInput));
// Store the verified result
this.verifiedResult.set(proof.publicOutput);
// Increment proof count
const currentCount = this.proofCount.getAndRequireEquals();
this.proofCount.set(currentCount.add(1));
}
@method async verifyAndAccumulate(proof: BasicMathProof, multiplier: Field) {
// Verify the proof
proof.verify();
// Use the proven result in further computation
const provenResult = proof.publicOutput;
const newResult = provenResult.mul(multiplier);
// Accumulate with existing state
const currentResult = this.verifiedResult.getAndRequireEquals();
this.verifiedResult.set(currentResult.add(newResult));
}
}
Recursive Proofs
Build recursive computations using self-referencing proofs:
export const RecursiveProgram = ZkProgram({
name: 'RecursiveProgram',
publicInput: Field,
publicOutput: Field,
methods: {
baseCase: {
privateInputs: [],
async method(input: Field) {
return input.add(1);
},
},
recursiveCase: {
privateInputs: [SelfProof],
async method(input: Field, previousProof: SelfProof) {
// Verify the previous proof
previousProof.verify();
// Ensure continuity: current input should be previous output + 1
previousProof.publicOutput.add(1).assertEquals(input);
// Continue the sequence
return input.add(1);
},
},
},
});
export class RecursiveProof extends ZkProgram.Proof(RecursiveProgram) {}
export class RecursiveContract extends SmartContract {
@state(Field) finalValue = State<Field>();
@state(UInt32) chainLength = State<UInt32>();
init() {
super.init();
this.finalValue.set(Field(0));
this.chainLength.set(UInt32.from(0));
}
@method async acceptRecursiveProof(proof: RecursiveProof, expectedLength: UInt32) {
// Verify the proof
proof.verify();
// Store the final value from the proof chain
this.finalValue.set(proof.publicOutput);
this.chainLength.set(expectedLength);
}
}
This enables scalable computation, state transitions, and proof compression for arbitrary-length computations.
API Reference
For detailed information about ZkProgram and proofs, see: