Skip to content

Commit

Permalink
refactor and add zkapp compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
Ho Nguyen Pham authored and Ho Nguyen Pham committed Oct 28, 2023
1 parent 96a13eb commit ad8ea6e
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 130 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@auxo-dev/dkg-libs",
"version": "0.1.2",
"version": "0.1.3",
"description": "",
"author": "",
"license": "Apache-2.0",
Expand Down
81 changes: 67 additions & 14 deletions src/CommitteeMember.test.ts → src/dkg/Committee.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { Field, Group, Provable, PrivateKey, PublicKey, Scalar } from 'o1js';
import * as Committee from './CommitteeMember';
import {
Field,
Group,
method,
Provable,
PrivateKey,
PublicKey,
Scalar,
SmartContract,
state,
State,
Reducer,
} from 'o1js';
import * as Committee from './Committee.js';
import * as Requestor from './Requestor.js';

describe('CommitteeMember', () => {
describe('Committee', () => {
let T = 3;
let N = 5;
let keyId = Field(0);
let committees: {
privateKey: PrivateKey;
index: number;
Expand Down Expand Up @@ -35,12 +47,6 @@ describe('CommitteeMember', () => {
beforeAll(async () => {
for (let i = 0; i < N; i++) {
let privateKey = PrivateKey.random();
// let committeeMember = {
// publicKey: privateKey.toPublicKey(),
// index: i + 1,
// round1Contribution: undefined,
// round2Contribution: undefined,
// };
let secretPolynomial = Committee.generateRandomPolynomial(T, N);
committees.push({
privateKey: privateKey,
Expand All @@ -59,6 +65,7 @@ describe('CommitteeMember', () => {
);
committees[i].round1Contribution = round1Contribution;
round1Contributions.push(round1Contribution);
Provable.runAndCheck(() => round1Contribution);
}
publicKey = Committee.calculatePublicKey(round1Contributions);
// Provable.log(publicKey);
Expand All @@ -74,22 +81,23 @@ describe('CommitteeMember', () => {
);
committees[i].round2Contribution = round2Contribution;
round2Contributions.push(round2Contribution);
Provable.runAndCheck(() => round2Contribution);
}
// Provable.log(round2Contributions);
// round2Contributions.map((e) => console.log(e.data));
});

it('Should accumulate encryption', async () => {
for (let i = 0; i < plainVectors.length; i++) {
let encryptedVector = Committee.encryptVector(
let encryptedVector = Requestor.generateEncryption(
Committee.calculatePublicKey(round1Contributions),
plainVectors[i]
);
R.push(encryptedVector.R);
M.push(encryptedVector.M);
}

let accumulatedEncryption = Committee.accumulateEncryption(R, M);
let accumulatedEncryption = Requestor.accumulateEncryption(R, M);
sumR = accumulatedEncryption.sumR;
sumM = accumulatedEncryption.sumM;
// Provable.log(sumR, sumM);
Expand All @@ -103,7 +111,15 @@ describe('CommitteeMember', () => {
index == committees[listIndex[i] - 1].index - 1
? prev
: round2Data.push(
curr.data[committees[listIndex[i] - 1].index - 1]
{
c: curr.c.values[
committees[listIndex[i] - 1].index - 1
].toScalar(),
U: curr.U.values[
committees[listIndex[i] - 1].index - 1
],
}
// curr.data[committees[listIndex[i] - 1].index - 1]
),
{}
);
Expand All @@ -113,9 +129,10 @@ describe('CommitteeMember', () => {
round2Data,
sumR
);
Provable.runAndCheck(() => tallyContribution);
committees[listIndex[i] - 1].tallyContribution = tallyContribution;
tallyContributions.push(tallyContribution);
D.push(tallyContribution.D);
D.push(tallyContribution.D.values.slice(0, T));
}
// Provable.log(tallyContributions);
// tallyContributions.map((e) => Provable.log(e.D));
Expand All @@ -132,4 +149,40 @@ describe('CommitteeMember', () => {
expect(resultVector[i].y).toEqual(point.y);
}
});

it('Should be used in Smart Contract', async () => {
class TestRound1Contribution extends SmartContract {
reducer = Reducer({ actionType: Committee.Round1Contribution });
@state(Field) keyId = State<Field>();
@method test(): Field {
return Field(0);
}
}

class TestRound2Contribution extends SmartContract {
reducer = Reducer({ actionType: Committee.Round2Contribution });
@state(Field) keyId = State<Field>();
@method test(): Field {
return Field(0);
}
}

class TestTallyContribution extends SmartContract {
reducer = Reducer({ actionType: Committee.TallyContribution });
@state(Field) keyId = State<Field>();
@method test(): Field {
return Field(0);
}
}

console.log('Compile test round 1...');
await TestRound1Contribution.compile();
console.log('DONE!');
console.log('Compile test round 2...');
await TestRound2Contribution.compile();
console.log('DONE!');
console.log('Compile test tally...');
await TestTallyContribution.compile();
console.log('DONE!');
});
});
124 changes: 36 additions & 88 deletions src/CommitteeMember.ts → src/dkg/Committee.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import {
Bool,
Encryption,
Field,
Group,
MerkleWitness,
Poseidon,
PrivateKey,
Provable,
PublicKey,
Scalar,
Struct,
UInt32,
} from 'o1js';
import * as ElgamalECC from './Elgamal';
import { Group, PrivateKey, PublicKey, Scalar, Struct } from 'o1js';
import * as ElgamalECC from '../utils/Elgamal.js';
import { DynamicArray } from '../utils/DynamicArray.js';
import { CustomScalar } from '../utils/CustomScalar.js';

export {
SecretPolynomial,
Expand All @@ -28,39 +17,41 @@ export {
getTallyContribution,
getLagrangeCoefficient,
getResultVector,
encryptVector,
accumulateEncryption,
};

const GroupDynamicArray = DynamicArray(Group, 32);
const ScalarDynamicArray = DynamicArray(CustomScalar, 32);

type SecretPolynomial = {
a: Scalar[];
C: Group[];
f: Scalar[];
};

type Round1Contribution = {
C: Group[];
};

type Round2Data = {
c: bigint;
c: Scalar;
U: Group;
};

type Round2Contribution = {
data: Round2Data[];
};
class Round1Contribution extends Struct({
C: GroupDynamicArray,
}) {}

type TallyContribution = {
D: Group[];
};
class Round2Contribution extends Struct({
c: ScalarDynamicArray,
U: GroupDynamicArray,
}) {}

class TallyContribution extends Struct({
D: GroupDynamicArray,
}) {}

function calculatePublicKey(
round1Contributions: Round1Contribution[]
): PublicKey {
let result = Group.zero;
for (let i = 0; i < round1Contributions.length; i++) {
result = result.add(round1Contributions[i].C[0]);
result = result.add(round1Contributions[i].C.values[0]);
}
return PublicKey.fromGroup(result);
}
Expand Down Expand Up @@ -88,7 +79,8 @@ function generateRandomPolynomial(T: number, N: number): SecretPolynomial {
}

function getRound1Contribution(secret: SecretPolynomial): Round1Contribution {
return { C: secret.C };
let provableC = GroupDynamicArray.from(secret.C);
return { C: provableC };
}

function getRound2Contribution(
Expand All @@ -97,24 +89,26 @@ function getRound2Contribution(
round1Contributions: Round1Contribution[]
): Round2Contribution {
let data = new Array<Round2Data>(secret.f.length);
let c = new Array<Scalar>(secret.f.length);
let U = new Array<Group>(secret.f.length);
for (let i = 0; i < data.length; i++) {
if (i + 1 == index) {
data[i] = {
U: Group.zero,
c: 0n,
};
c[i] = Scalar.from(0n);
U[i] = Group.zero;
} else {
let encryption = ElgamalECC.encrypt(
secret.f[i].toBigInt(),
PublicKey.fromGroup(round1Contributions[i].C[0])
PublicKey.fromGroup(round1Contributions[i].C.values[0])
);
data[i] = {
U: encryption.U,
c: encryption.c,
};
c[i] = Scalar.from(encryption.c);
U[i] = encryption.U;
}
}
return { data };
let provablec = ScalarDynamicArray.from(
c.map((e) => CustomScalar.fromScalar(e))
);
let provableU = GroupDynamicArray.from(U);
return { c: provablec, U: provableU };
}

function getTallyContribution(
Expand All @@ -126,7 +120,7 @@ function getTallyContribution(
let decryptions: Scalar[] = round2Data.map((data) =>
Scalar.from(
ElgamalECC.decrypt(
data.c,
data.c.toBigInt(),
data.U,
PrivateKey.fromBigInt(secret.a[0].toBigInt())
).m
Expand All @@ -141,7 +135,7 @@ function getTallyContribution(
for (let i = 0; i < R.length; i++) {
D[i] = R[i].scale(ski);
}
return { D };
return { D: GroupDynamicArray.from(D) };
}

function getLagrangeCoefficient(listIndex: number[]): Scalar[] {
Expand Down Expand Up @@ -184,49 +178,3 @@ function getResultVector(
}
return result;
}

function encryptVector(
publicKey: PublicKey,
vector: bigint[]
): {
r: Scalar[];
R: Group[];
M: Group[];
} {
let dimension = vector.length;
let r = new Array<Scalar>(dimension);
let R = new Array<Group>(dimension);
let M = new Array<Group>(dimension);
for (let i = 0; i < dimension; i++) {
let random = Scalar.random();
r[i] = random;
R[i] = Group.generator.scale(random);
M[i] =
vector[i] > 0n
? Group.generator
.scale(Scalar.from(vector[i]))
.add(publicKey.toGroup().scale(random))
: Group.zero.add(publicKey.toGroup().scale(random));
}
return { r, R, M };
}

function accumulateEncryption(
R: Group[][],
M: Group[][]
): { sumR: Group[]; sumM: Group[] } {
let quantity = R.length;
let dimension = R[0].length ?? 0;
let sumR = new Array<Group>(dimension);
let sumM = new Array<Group>(dimension);
sumR.fill(Group.zero);
sumM.fill(Group.zero);

for (let i = 0; i < quantity; i++) {
for (let j = 0; j < dimension; j++) {
sumR[j] = sumR[j].add(R[i][j]);
sumM[j] = sumM[j].add(M[i][j]);
}
}
return { sumR, sumM };
}
Loading

0 comments on commit ad8ea6e

Please sign in to comment.