Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/CRISP/circuits/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fn main(
} else {
// check if vote == 0.
let is_vote_zero = check_coefficient_zero(k1);
assert(is_vote_zero);
assert(is_vote_zero == true);

// need to check if slot is empty (no previous ciphertext).
// If so, (ct0is, ct1is) should be returned.
Expand Down
96 changes: 52 additions & 44 deletions examples/CRISP/circuits/src/utils.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,44 @@

use polynomial::Polynomial;

// Check that all valid coefficients are either 0 or 1 and that the vote is <= balance
// Check that all valid coefficients are either 0 or q_mod_t and that the vote is <= balance
pub fn check_coefficient_values_with_balance<let D: u32>(
k1: Polynomial<D>,
q_mod_t: Field,
balance: Field,
) {
// This value would allow to fit
// 268435456 for yes and 268435456 for no
// which would fit the supply of most tokens really (imagining one user just holds all tokens)
// This value allows to fit 268,435,455 for yes and 268,435,455 for no
let HALF_LARGEST_MINIMUM_DEGREE = 28;
let HALF_D = D / 2;
let START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE;
let START_INDEX_N = D - HALF_LARGEST_MINIMUM_DEGREE;

// After reversal, bits that were at END of first half are now at START of second half
let END_INDEX_Y = HALF_D + HALF_LARGEST_MINIMUM_DEGREE;

// Bits that were at END of second half are now at START of first half
let END_INDEX_N = HALF_LARGEST_MINIMUM_DEGREE;

let mut sum_yes: u64 = 0;
let mut sum_no: u64 = 0;

// Loop through all coefficients in the space where we could have a vote
// yes part
for i in START_INDEX_Y..HALF_D {
let coeff = k1.coefficients[i];
// Yes part - process from END to START (reverse order)
for i in 0..HALF_LARGEST_MINIMUM_DEGREE {
// Access indices backwards: from (END_INDEX_Y - 1) down to START_INDEX_Y
let idx = END_INDEX_Y - 1 - i;
let coeff = k1.coefficients[idx];
assert(0 == coeff * (q_mod_t - coeff));

// evaluate using Horner's method
sum_yes = sum_yes + sum_yes + coeff as u64;
let bit: u64 = if coeff == 0 { 0 } else { 1 };
sum_yes = sum_yes * 2 + bit;
}

// no part
for i in START_INDEX_N..D {
let coeff = k1.coefficients[i];
// No part - process from END to START (reverse order)
for i in 0..HALF_LARGEST_MINIMUM_DEGREE {
let idx = END_INDEX_N - 1 - i;
let coeff = k1.coefficients[idx];
assert(0 == coeff * (q_mod_t - coeff));

// evaluate using Horner's method
sum_no = sum_no + sum_no + coeff as u64;
let bit: u64 = if coeff == 0 { 0 } else { 1 };
sum_no = sum_no * 2 + bit;
}

if (sum_yes != 0) {
Expand All @@ -53,28 +57,25 @@ pub fn check_coefficient_values_with_balance<let D: u32>(
}
}

// Check that the polynomial has 0 for all relevant coefficients
pub fn check_coefficient_zero<let D: u32>(k1: Polynomial<D>) -> bool {
// This value would allow to fit
// 268435456 for yes and 268435456 for no
// which would fit the supply of most tokens really (imagining one user just holds all tokens)
let HALF_LARGEST_MINIMUM_DEGREE = 28;
let HALF_D = D / 2;
let START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE;
let START_INDEX_N = D - HALF_LARGEST_MINIMUM_DEGREE;

let START_INDEX_Y = HALF_D;
let END_INDEX_Y = HALF_D + HALF_LARGEST_MINIMUM_DEGREE;

let START_INDEX_N = 0;
let END_INDEX_N = HALF_LARGEST_MINIMUM_DEGREE;

let mut res = true;

// Loop through all coefficients in the space where we could have a vote
// yes part
for i in START_INDEX_Y..HALF_D {
for i in START_INDEX_Y..END_INDEX_Y {
if k1.coefficients[i] != 0 {
res = false;
}
}

// no part
for i in START_INDEX_N..D {
for i in START_INDEX_N..END_INDEX_N {
if k1.coefficients[i] != 0 {
res = false;
}
Expand All @@ -88,9 +89,10 @@ fn test_check_vote_is_within_balance_pass() {
let pol = Polynomial {
coefficients: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// YES votes: bits at indices 50-52 (START of YES window)
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};

Expand All @@ -101,10 +103,12 @@ fn test_check_vote_is_within_balance_pass() {
fn test_check_vote_is_within_balance_yes_and_no_not_zero() {
let pol = Polynomial {
coefficients: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
// NO votes: index 0
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// YES votes: indices 50-52
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};

Expand All @@ -116,9 +120,10 @@ fn test_check_vote_is_within_balance_fail() {
let pol = Polynomial {
coefficients: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// YES votes: indices 50-52 (binary 111 = 7, exceeds balance of 1)
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};

Expand All @@ -129,9 +134,10 @@ fn test_check_vote_is_within_balance_fail() {
fn test_check_coefficient_values_pass() {
let pol = Polynomial {
coefficients: [
// NO vote: index 0 (binary 1 = 1)
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};
Expand All @@ -143,10 +149,11 @@ fn test_check_coefficient_values_pass() {
fn test_check_coefficient_values_fail() {
let pol = Polynomial {
coefficients: [
// Invalid coefficient value 2 (not 0 or 1)
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};

Expand All @@ -158,9 +165,10 @@ fn test_check_coefficient_zero_fail() {
let pol = Polynomial {
coefficients: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Non-zero at index 50 (YES section)
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};

Expand Down
6 changes: 3 additions & 3 deletions examples/CRISP/packages/crisp-sdk/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export const CRISP_SERVER_STATE_LITE_ENDPOINT = 'state/lite'
* Half the minimum degree needed to support the maxium vote value
* If you change MAXIMUM_VOTE_VALUE, make sure to update this value too.
*/
export const HALF_LARGEST_MINIMUM_DEGREE = 28;
export const HALF_LARGEST_MINIMUM_DEGREE = 28

/**
* This is the maximum value for a vote (Yes or No). This is 2^28
* This is the maximum value for a vote (Yes or No). This is 2^28 - 1
* The minimum degree that BFV should use is 56 (to accommodate both Yes and No votes)
*/
export const MAXIMUM_VOTE_VALUE = BigInt(Math.pow(2, HALF_LARGEST_MINIMUM_DEGREE))
export const MAXIMUM_VOTE_VALUE = BigInt(Math.pow(2, HALF_LARGEST_MINIMUM_DEGREE) - 1)

/**
* Default BFV parameters for the CRISP ZK inputs generator.
Expand Down
40 changes: 25 additions & 15 deletions examples/CRISP/packages/crisp-sdk/src/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,37 +97,37 @@ export const encodeVote = (vote: IVote, votingMode: VotingMode, votingPower: big
export const decodeTally = (tally: string[], votingMode: VotingMode): IVote => {
switch (votingMode) {
case VotingMode.GOVERNANCE:
const HALF_D = tally.length / 2;
const START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE;
const START_INDEX_N = tally.length - HALF_LARGEST_MINIMUM_DEGREE;
const HALF_D = tally.length / 2
const START_INDEX_Y = HALF_D - HALF_LARGEST_MINIMUM_DEGREE
const START_INDEX_N = tally.length - HALF_LARGEST_MINIMUM_DEGREE

// Extract only the relevant parts of the tally
const yesBinary = tally.slice(START_INDEX_Y, HALF_D);
const noBinary = tally.slice(START_INDEX_N, tally.length);
const yesBinary = tally.slice(START_INDEX_Y, HALF_D)
const noBinary = tally.slice(START_INDEX_N, tally.length)

let yes = 0n;
let no = 0n;
let yes = 0n
let no = 0n

// Convert yes votes (from START_INDEX_Y to HALF_D)
for (let i = 0; i < yesBinary.length; i += 1) {
const weight = 2n ** BigInt(yesBinary.length - 1 - i);
yes += BigInt(yesBinary[i]) * weight;
const weight = 2n ** BigInt(yesBinary.length - 1 - i)
yes += BigInt(yesBinary[i]) * weight
}

// Convert no votes (from START_INDEX_N to D)
for (let i = 0; i < noBinary.length; i += 1) {
const weight = 2n ** BigInt(noBinary.length - 1 - i);
no += BigInt(noBinary[i]) * weight;
const weight = 2n ** BigInt(noBinary.length - 1 - i)
no += BigInt(noBinary[i]) * weight
}

return {
yes,
no,
};
}
default:
throw new Error('Unsupported voting mode');
throw new Error('Unsupported voting mode')
}
};
}

/**
* Validate whether a vote is valid for a given voting mode
Expand Down Expand Up @@ -271,7 +271,9 @@ export const generateProof = async (crispInputs: CRISPCircuitInputs): Promise<Pr
return proof
}

export const generateProofWithReturnValue = async (crispInputs: CRISPCircuitInputs): Promise<{ returnValue: unknown, proof:ProofData }> => {
export const generateProofWithReturnValue = async (
crispInputs: CRISPCircuitInputs,
): Promise<{ returnValue: unknown; proof: ProofData }> => {
const noir = new Noir(circuit as CompiledCircuit)
const backend = new UltraHonkBackend((circuit as CompiledCircuit).bytecode)

Expand All @@ -281,6 +283,14 @@ export const generateProofWithReturnValue = async (crispInputs: CRISPCircuitInpu
return { returnValue, proof }
}

export const getCircuitOutputValue = async (crispInputs: CRISPCircuitInputs): Promise<{ returnValue: unknown }> => {
const noir = new Noir(circuit as CompiledCircuit)

const { returnValue } = await noir.execute(crispInputs as any)

return { returnValue }
}

export const verifyProof = async (proof: ProofData): Promise<boolean> => {
const backend = new UltraHonkBackend((circuit as CompiledCircuit).bytecode)

Expand Down
46 changes: 23 additions & 23 deletions examples/CRISP/packages/crisp-sdk/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,31 @@
// or FITNESS FOR A PARTICULAR PURPOSE.

export function normalizeCoefficient(coeff: string): string {
if (coeff.startsWith('0x')) {
return BigInt(coeff).toString();
}
return coeff.toString();
if (coeff.startsWith('0x')) {
return BigInt(coeff).toString()
}
return coeff.toString()
}

export function compareCoefficientsArrays(arr1: any[], arr2: any[]): boolean {
if (arr1.length !== arr2.length) return false;

for (let i = 0; i < arr1.length; i++) {
if (!arr1[i] || !arr2[i]) return false;

const coeff1 = arr1[i].coefficients;
const coeff2 = arr2[i].coefficients;

if (!coeff1 || !coeff2) return false;
if (coeff1.length !== coeff2.length) return false;

for (let k = 0; k < coeff1.length; k++) {
const normalized1 = normalizeCoefficient(coeff1[k]);
const normalized2 = normalizeCoefficient(coeff2[k]);

if (normalized1 !== normalized2) return false;
}
if (arr1.length !== arr2.length) return false

for (let i = 0; i < arr1.length; i++) {
if (!arr1[i] || !arr2[i]) return false

const coeff1 = arr1[i].coefficients
const coeff2 = arr2[i].coefficients

if (!coeff1 || !coeff2) return false
if (coeff1.length !== coeff2.length) return false

for (let k = 0; k < coeff1.length; k++) {
const normalized1 = normalizeCoefficient(coeff1[k])
const normalized2 = normalizeCoefficient(coeff2[k])

if (normalized1 !== normalized2) return false
}

return true;
}

return true
}
Loading
Loading