Skip to content

Commit

Permalink
Merge pull request #361 from o1-labs/feature/zkapp-b-test
Browse files Browse the repository at this point in the history
  • Loading branch information
Trivo25 authored Oct 24, 2022
2 parents 6cb138e + 0888294 commit f40e461
Show file tree
Hide file tree
Showing 11 changed files with 1,324 additions and 63 deletions.
9 changes: 7 additions & 2 deletions src/examples/zkapps/voting/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import {
ElectionPreconditions,
} from './preconditions.js';

let Local = Mina.LocalBlockchain();
let Local = Mina.LocalBlockchain({
proofsEnabled: false,
});
Mina.setActiveInstance(Local);

let feePayer = Local.testAccounts[0].privateKey;
Expand Down Expand Up @@ -52,7 +54,7 @@ let params: VotingAppParams = {
voterKey,
candidateKey,
votingKey,
doProofs: false,
doProofs: true,
};

let contracts = await VotingApp(params);
Expand Down Expand Up @@ -87,6 +89,7 @@ try {
);
});
await tx.send();

let m: Member = Member.empty();
// lets register three voters
tx = await Mina.transaction(feePayer, () => {
Expand Down Expand Up @@ -200,6 +203,7 @@ try {

if (params.doProofs) await tx.prove();
await tx.send();

tx = await Mina.transaction(feePayer, () => {
// creating and registering 1 new candidate
let m = registerMember(
Expand All @@ -223,6 +227,7 @@ try {

if (params.doProofs) await tx.prove();
await tx.send();

/*
since the voting contact calls the candidate membership contract via invoking candidateRegister,
the membership contract will then emit one event per new member
Expand Down
169 changes: 169 additions & 0 deletions src/examples/zkapps/voting/deployContracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import {
DeployArgs,
Experimental,
Field,
Permissions,
Mina,
AccountUpdate,
PrivateKey,
SmartContract,
} from 'snarkyjs';
import { VotingAppParams } from './factory.js';

import { Membership_ } from './membership.js';

import { Voting_ } from './voting.js';

class InvalidContract extends SmartContract {
deploy(args: DeployArgs) {
super.deploy(args);
this.setPermissions({
...Permissions.default(),
editState: Permissions.none(),
editSequenceState: Permissions.none(),
});
}
}

/**
* Function used to deploy a set of contracts for a given set of preconditions
* @param feePayer the private key used to pay the fees
* @param contracts A set of contracts to deploy
* @param params A set of preconditions and parameters
* @param voterRoot the initial root of the voter store
* @param candidateRoot the initial root of the voter store
* @param votesRoot the initial root of the votes store
*/
export async function deployContracts(
contracts: {
voterContract: Membership_;
candidateContract: Membership_;
voting: Voting_;
},
params: VotingAppParams,
voterRoot: Field,
candidateRoot: Field,
votesRoot: Field,
proofsEnabled: boolean = false
): Promise<{
voterContract: Membership_;
candidateContract: Membership_;
voting: Voting_;
Local: any;
feePayer: PrivateKey;
}> {
let Local = Mina.LocalBlockchain({
proofsEnabled,
});
Mina.setActiveInstance(Local);

let feePayer = Local.testAccounts[0].privateKey;
let { voterContract, candidateContract, voting } = contracts;

console.log('deploying set of 3 contracts');
try {
let tx = await Mina.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer, {
initialBalance: Mina.accountCreationFee().add(
Mina.accountCreationFee()
),
});

voting.deploy({ zkappKey: params.votingKey });
voting.committedVotes.set(votesRoot);
voting.accumulatedVotes.set(Experimental.Reducer.initialActionsHash);

candidateContract.deploy({ zkappKey: params.candidateKey });
candidateContract.committedMembers.set(candidateRoot);
candidateContract.accumulatedMembers.set(
Experimental.Reducer.initialActionsHash
);

voterContract.deploy({ zkappKey: params.voterKey });
voterContract.committedMembers.set(voterRoot);
voterContract.accumulatedMembers.set(
Experimental.Reducer.initialActionsHash
);
});
await tx.send();
} catch (err: any) {
throw Error(err);
}

console.log('successfully deployed contracts');
return { voterContract, candidateContract, voting, feePayer, Local };
}

/**
* Function used to deploy a set of **invalid** membership contracts for a given set of preconditions
* @param feePayer the private key used to pay the fees
* @param contracts A set of contracts to deploy
* @param params A set of preconditions and parameters
* @param voterRoot the initial root of the voter store
* @param candidateRoot the initial root of the voter store
* @param votesRoot the initial root of the votes store
*/
export async function deployInvalidContracts(
contracts: {
voterContract: Membership_;
candidateContract: Membership_;
voting: Voting_;
},
params: VotingAppParams,
voterRoot: Field,
candidateRoot: Field,
votesRoot: Field
): Promise<{
voterContract: Membership_;
candidateContract: Membership_;
voting: Voting_;
Local: any;
feePayer: PrivateKey;
}> {
let Local = Mina.LocalBlockchain({
proofsEnabled: false,
});
Mina.setActiveInstance(Local);

let feePayer = Local.testAccounts[0].privateKey;
let { voterContract, candidateContract, voting } = contracts;

console.log('deploying set of 3 contracts');
try {
let tx = await Mina.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer, {
initialBalance: Mina.accountCreationFee().add(
Mina.accountCreationFee()
),
});

voting.deploy({ zkappKey: params.votingKey });
voting.committedVotes.set(votesRoot);
voting.accumulatedVotes.set(Experimental.Reducer.initialActionsHash);

// invalid contracts

let invalidCandidateContract = new InvalidContract(
params.candidateKey.toPublicKey()
);

invalidCandidateContract.deploy({ zkappKey: params.candidateKey });

candidateContract = invalidCandidateContract as Membership_;

let invalidVoterContract = new InvalidContract(
params.voterKey.toPublicKey()
);

invalidVoterContract.deploy({ zkappKey: params.voterKey });

voterContract = invalidVoterContract as Membership_;
});
await tx.send();
} catch (err: any) {
throw Error(err);
}

console.log('successfully deployed contracts');
return { voterContract, candidateContract, voting, feePayer, Local };
}
33 changes: 33 additions & 0 deletions src/examples/zkapps/voting/dummyContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
Field,
SmartContract,
state,
State,
method,
DeployArgs,
Permissions,
} from 'snarkyjs';

export class DummyContract extends SmartContract {
@state(Field) sum = State<Field>();

deploy(args: DeployArgs) {
super.deploy(args);
this.setPermissions({
...Permissions.default(),
editState: Permissions.proofOrSignature(),
editSequenceState: Permissions.proofOrSignature(),
setPermissions: Permissions.proofOrSignature(),
setVerificationKey: Permissions.proofOrSignature(),
incrementNonce: Permissions.proofOrSignature(),
});
this.sum.set(Field.zero);
}

/**
* Method used to add two variables together.
*/
@method add(x: Field, y: Field) {
this.sum.set(x.add(y));
}
}
12 changes: 12 additions & 0 deletions src/examples/zkapps/voting/election_preconditions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CircuitValue, prop, UInt32 } from 'snarkyjs';

export default class ElectionPreconditions extends CircuitValue {
@prop startElection: UInt32;
@prop endElection: UInt32;

constructor(startElection: UInt32, endElection: UInt32) {
super();
this.startElection = startElection;
this.endElection = endElection;
}
}
17 changes: 11 additions & 6 deletions src/examples/zkapps/voting/membership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export async function Membership(
participantPreconditions = params.participantPreconditions;

let contract = new Membership_(params.contractAddress);

params.doProofs = true;
if (params.doProofs) {
await Membership_.compile();
}
Expand Down Expand Up @@ -60,8 +62,11 @@ export class Membership_ extends SmartContract {
super.deploy(args);
this.setPermissions({
...Permissions.default(),
editState: Permissions.none(), // TODO: fix permissions
editSequenceState: Permissions.none(), // TODO: fix permissions
editState: Permissions.proofOrSignature(),
editSequenceState: Permissions.proofOrSignature(),
setPermissions: Permissions.proofOrSignature(),
setVerificationKey: Permissions.proofOrSignature(),
incrementNonce: Permissions.proofOrSignature(),
});
}

Expand All @@ -77,20 +82,20 @@ export class Membership_ extends SmartContract {
// since we need to keep this contract "generic", we always assert within a range
// even tho voters cant have a maximum balance, only candidates
// but for a voter we simply use UInt64.MAXINT() as the maximum

let accountUpdate = Experimental.createChildAccountUpdate(
this.self,
member.publicKey
);

accountUpdate.account.balance.assertEquals(
accountUpdate.account.balance.get()
);

let balance = accountUpdate.account.balance.get();

balance
.gte(participantPreconditions.minMina)
.and(balance.lte(participantPreconditions.maxMina))
.assertTrue();
balance.assertGte(participantPreconditions.minMina);
balance.assertLte(participantPreconditions.maxMina);

let accumulatedMembers = this.accumulatedMembers.get();
this.accumulatedMembers.assertEquals(accumulatedMembers);
Expand Down
18 changes: 18 additions & 0 deletions src/examples/zkapps/voting/participant_preconditions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CircuitValue, prop, UInt64 } from 'snarkyjs';

export default class ParticipantPreconditions extends CircuitValue {
@prop minMinaVote: UInt64;
@prop minMinaCandidate: UInt64;
@prop maxMinaCandidate: UInt64;

constructor(
minMinaVote: UInt64,
minMinaCandidate: UInt64,
maxMinaCandidate: UInt64
) {
super();
this.minMinaVote = minMinaVote;
this.minMinaCandidate = minMinaCandidate;
this.maxMinaCandidate = maxMinaCandidate;
}
}
35 changes: 21 additions & 14 deletions src/examples/zkapps/voting/run.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Experimental, Mina, PrivateKey, PublicKey } from 'snarkyjs';
import {
Experimental,
Mina,
PrivateKey,
PublicKey,
UInt32,
UInt64,
} from 'snarkyjs';
import { VotingApp, VotingAppParams } from './factory.js';
import {
ElectionPreconditions,
Expand All @@ -9,9 +16,6 @@ import { OffchainStorage } from './off_chain_storage.js';
import { Member } from './member.js';
import { testSet } from './test.js';

let Local = Mina.LocalBlockchain();
Mina.setActiveInstance(Local);

console.log('Running Voting script...');

// I really hope this factory pattern works with SnarkyJS' contracts
Expand All @@ -21,12 +25,21 @@ console.log('Running Voting script...');
// ! the VotingApp() factory returns a set of compiled contract instances

// dummy set to demonstrate how the script will function
console.log('Starting run for set 1...');
console.log('Starting set 1...');

let params_set1: VotingAppParams = {
candidatePreconditions: ParticipantPreconditions.default,
voterPreconditions: ParticipantPreconditions.default,
electionPreconditions: ElectionPreconditions.default,
candidatePreconditions: new ParticipantPreconditions(
UInt64.from(10),
UInt64.from(5000)
),
voterPreconditions: new ParticipantPreconditions(
UInt64.from(10),
UInt64.from(50)
),
electionPreconditions: new ElectionPreconditions(
UInt32.from(5),
UInt32.from(15)
),
voterKey: PrivateKey.random(),
candidateKey: PrivateKey.random(),
votingKey: PrivateKey.random(),
Expand All @@ -46,9 +59,3 @@ console.log('Testing set 1...');
await testSet(contracts_set1, params_set1, storage_set1);

// ..

// do our thing before we create another set
// sets need to be created and used in series,
// parallel creation of sets doesnt work with the current "factory" pattern

// ..
Loading

0 comments on commit f40e461

Please sign in to comment.