@crisp-e3/sdk 0.0.1-test
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +165 -0
- package/README.md +18 -0
- package/dist/src/ERC20Votes.json +847 -0
- package/dist/src/constants.d.ts +18 -0
- package/dist/src/constants.js +23 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +12 -0
- package/dist/src/signature.d.ts +8 -0
- package/dist/src/signature.js +36 -0
- package/dist/src/state.d.ts +12 -0
- package/dist/src/state.js +48 -0
- package/dist/src/token.d.ts +23 -0
- package/dist/src/token.js +95 -0
- package/dist/src/types.d.ts +193 -0
- package/dist/src/types.js +16 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +71 -0
- package/dist/src/vote.d.ts +71 -0
- package/dist/src/vote.js +222 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BFVParams } from './types';
|
|
2
|
+
export declare const CRISP_SERVER_TOKEN_TREE_ENDPOINT = "state/token-holders";
|
|
3
|
+
export declare const CRISP_SERVER_STATE_LITE_ENDPOINT = "state/lite";
|
|
4
|
+
/**
|
|
5
|
+
* This is the maximum value for a vote (Yes or No). This is 2^28
|
|
6
|
+
* The minimum degree that BFV should use is 56 (to accommodate both Yes and No votes)
|
|
7
|
+
* If you change this value, make sure to update the circuit too.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAXIMUM_VOTE_VALUE = 268435456n;
|
|
10
|
+
/**
|
|
11
|
+
* Default BFV parameters for the CRISP ZK inputs generator.
|
|
12
|
+
* These are the parameters used for the default testing purposes only.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_BFV_PARAMS: BFVParams;
|
|
15
|
+
/**
|
|
16
|
+
* Mock message for masking signature
|
|
17
|
+
*/
|
|
18
|
+
export declare const MESSAGE = "Vote for round 0";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
import { ZKInputsGenerator } from '@crisp-e3/zk-inputs';
|
|
7
|
+
export const CRISP_SERVER_TOKEN_TREE_ENDPOINT = 'state/token-holders';
|
|
8
|
+
export const CRISP_SERVER_STATE_LITE_ENDPOINT = 'state/lite';
|
|
9
|
+
/**
|
|
10
|
+
* This is the maximum value for a vote (Yes or No). This is 2^28
|
|
11
|
+
* The minimum degree that BFV should use is 56 (to accommodate both Yes and No votes)
|
|
12
|
+
* If you change this value, make sure to update the circuit too.
|
|
13
|
+
*/
|
|
14
|
+
export const MAXIMUM_VOTE_VALUE = 268435456n;
|
|
15
|
+
/**
|
|
16
|
+
* Default BFV parameters for the CRISP ZK inputs generator.
|
|
17
|
+
* These are the parameters used for the default testing purposes only.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_BFV_PARAMS = ZKInputsGenerator.withDefaults().getBFVParams();
|
|
20
|
+
/**
|
|
21
|
+
* Mock message for masking signature
|
|
22
|
+
*/
|
|
23
|
+
export const MESSAGE = 'Vote for round 0';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './token';
|
|
2
|
+
export * from './state';
|
|
3
|
+
export * from './constants';
|
|
4
|
+
export * from './utils';
|
|
5
|
+
export * from './vote';
|
|
6
|
+
export * from './signature';
|
|
7
|
+
export { VotingMode } from './types';
|
|
8
|
+
export type { IRoundDetails, IRoundDetailsResponse, ITokenDetails, IMerkleProof, IVote, CRISPCircuitInputs, NoirSignatureInputs, } from './types';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
export * from './token';
|
|
7
|
+
export * from './state';
|
|
8
|
+
export * from './constants';
|
|
9
|
+
export * from './utils';
|
|
10
|
+
export * from './vote';
|
|
11
|
+
export * from './signature';
|
|
12
|
+
export { VotingMode } from './types';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NoirSignatureInputs } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Given a message and its signed version, extract the signature components
|
|
4
|
+
* @param message The original message
|
|
5
|
+
* @param signedMessage The signed message (signature)
|
|
6
|
+
* @returns The extracted signature components
|
|
7
|
+
*/
|
|
8
|
+
export declare const extractSignature: (message: string, signedMessage: `0x${string}`) => Promise<NoirSignatureInputs>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
import { hashMessage, hexToBytes, recoverPublicKey } from 'viem';
|
|
7
|
+
/**
|
|
8
|
+
* Given a message and its signed version, extract the signature components
|
|
9
|
+
* @param message The original message
|
|
10
|
+
* @param signedMessage The signed message (signature)
|
|
11
|
+
* @returns The extracted signature components
|
|
12
|
+
*/
|
|
13
|
+
export const extractSignature = async (message, signedMessage) => {
|
|
14
|
+
const messageHash = hashMessage(message);
|
|
15
|
+
const messageBytes = hexToBytes(messageHash);
|
|
16
|
+
const publicKey = await recoverPublicKey({
|
|
17
|
+
hash: messageHash,
|
|
18
|
+
signature: signedMessage,
|
|
19
|
+
});
|
|
20
|
+
const publicKeyBytes = hexToBytes(publicKey);
|
|
21
|
+
const publicKeyX = publicKeyBytes.slice(1, 33);
|
|
22
|
+
const publicKeyY = publicKeyBytes.slice(33, 65);
|
|
23
|
+
// Extract r and s from signature (remove v)
|
|
24
|
+
const sigBytes = hexToBytes(signedMessage);
|
|
25
|
+
const r = sigBytes.slice(0, 32); // First 32 bytes
|
|
26
|
+
const s = sigBytes.slice(32, 64); // Next 32 bytes
|
|
27
|
+
const signatureBytes = new Uint8Array(64);
|
|
28
|
+
signatureBytes.set(r, 0);
|
|
29
|
+
signatureBytes.set(s, 32);
|
|
30
|
+
return {
|
|
31
|
+
hashed_message: messageBytes,
|
|
32
|
+
pub_key_x: publicKeyX,
|
|
33
|
+
pub_key_y: publicKeyY,
|
|
34
|
+
signature: signatureBytes,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IRoundDetails, ITokenDetails } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Get the details of a specific round
|
|
4
|
+
*/
|
|
5
|
+
export declare const getRoundDetails: (serverUrl: string, e3Id: number) => Promise<IRoundDetails>;
|
|
6
|
+
/**
|
|
7
|
+
* Get the token address, balance threshold and snapshot block for a specific round
|
|
8
|
+
* @param serverUrl - The base URL of the CRISP server
|
|
9
|
+
* @param e3Id - The e3Id of the round
|
|
10
|
+
* @returns The token address, balance threshold and snapshot block
|
|
11
|
+
*/
|
|
12
|
+
export declare const getRoundTokenDetails: (serverUrl: string, e3Id: number) => Promise<ITokenDetails>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
import { CRISP_SERVER_STATE_LITE_ENDPOINT } from './constants';
|
|
7
|
+
/**
|
|
8
|
+
* Get the details of a specific round
|
|
9
|
+
*/
|
|
10
|
+
export const getRoundDetails = async (serverUrl, e3Id) => {
|
|
11
|
+
const response = await fetch(`${serverUrl}/${CRISP_SERVER_STATE_LITE_ENDPOINT}`, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({ round_id: e3Id }),
|
|
17
|
+
});
|
|
18
|
+
const data = (await response.json());
|
|
19
|
+
return {
|
|
20
|
+
e3Id: BigInt(data.id),
|
|
21
|
+
tokenAddress: data.token_address,
|
|
22
|
+
balanceThreshold: BigInt(data.balance_threshold),
|
|
23
|
+
chainId: BigInt(data.chain_id),
|
|
24
|
+
enclaveAddress: data.enclave_address,
|
|
25
|
+
status: data.status,
|
|
26
|
+
voteCount: BigInt(data.vote_count),
|
|
27
|
+
startTime: BigInt(data.start_time),
|
|
28
|
+
duration: BigInt(data.duration),
|
|
29
|
+
expiration: BigInt(data.expiration),
|
|
30
|
+
startBlock: BigInt(data.start_block),
|
|
31
|
+
committeePublicKey: data.committee_public_key,
|
|
32
|
+
emojis: data.emojis,
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Get the token address, balance threshold and snapshot block for a specific round
|
|
37
|
+
* @param serverUrl - The base URL of the CRISP server
|
|
38
|
+
* @param e3Id - The e3Id of the round
|
|
39
|
+
* @returns The token address, balance threshold and snapshot block
|
|
40
|
+
*/
|
|
41
|
+
export const getRoundTokenDetails = async (serverUrl, e3Id) => {
|
|
42
|
+
const roundDetails = await getRoundDetails(serverUrl, e3Id);
|
|
43
|
+
return {
|
|
44
|
+
tokenAddress: roundDetails.tokenAddress,
|
|
45
|
+
threshold: roundDetails.balanceThreshold,
|
|
46
|
+
snapshotBlock: roundDetails.startBlock,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the merkle tree data from the CRISP server
|
|
3
|
+
* @param serverUrl - The base URL of the CRISP server
|
|
4
|
+
* @param e3Id - The e3Id of the round
|
|
5
|
+
*/
|
|
6
|
+
export declare const getTreeData: (serverUrl: string, e3Id: number) => Promise<bigint[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Get the token balance at a specific block for a given address
|
|
9
|
+
* @param voterAddress - The address of the voter
|
|
10
|
+
* @param tokenAddress - The address of the token contract
|
|
11
|
+
* @param snapshotBlock - The block number at which to get the balance
|
|
12
|
+
* @param chainId - The chain ID of the network
|
|
13
|
+
* @returns The token balance as a bigint
|
|
14
|
+
*/
|
|
15
|
+
export declare const getBalanceAt: (voterAddress: string, tokenAddress: string, snapshotBlock: number, chainId: number) => Promise<bigint>;
|
|
16
|
+
/**
|
|
17
|
+
* Get the total supply of a ERC20Votes Token at a specific block
|
|
18
|
+
* @param tokenAddress The token address to query
|
|
19
|
+
* @param snapshotBlock The block number at which to get the total supply
|
|
20
|
+
* @param chainId The chain ID of the network
|
|
21
|
+
* @returns The total supply as a bigint
|
|
22
|
+
*/
|
|
23
|
+
export declare const getTotalSupplyAt: (tokenAddress: string, snapshotBlock: number, chainId: number) => Promise<bigint>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
import { CRISP_SERVER_TOKEN_TREE_ENDPOINT } from './constants';
|
|
7
|
+
import ERC20Votes from './ERC20Votes.json';
|
|
8
|
+
import { createPublicClient, http } from 'viem';
|
|
9
|
+
import { localhost, sepolia } from 'viem/chains';
|
|
10
|
+
/**
|
|
11
|
+
* Get the merkle tree data from the CRISP server
|
|
12
|
+
* @param serverUrl - The base URL of the CRISP server
|
|
13
|
+
* @param e3Id - The e3Id of the round
|
|
14
|
+
*/
|
|
15
|
+
export const getTreeData = async (serverUrl, e3Id) => {
|
|
16
|
+
const response = await fetch(`${serverUrl}/${CRISP_SERVER_TOKEN_TREE_ENDPOINT}`, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify({ round_id: e3Id }),
|
|
22
|
+
});
|
|
23
|
+
const hashes = (await response.json());
|
|
24
|
+
// Convert hex strings to BigInts
|
|
25
|
+
return hashes.map((hash) => {
|
|
26
|
+
// Ensure the hash is treated as a hex string
|
|
27
|
+
if (!hash.startsWith('0x')) {
|
|
28
|
+
return BigInt('0x' + hash);
|
|
29
|
+
}
|
|
30
|
+
return BigInt(hash);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Get the token balance at a specific block for a given address
|
|
35
|
+
* @param voterAddress - The address of the voter
|
|
36
|
+
* @param tokenAddress - The address of the token contract
|
|
37
|
+
* @param snapshotBlock - The block number at which to get the balance
|
|
38
|
+
* @param chainId - The chain ID of the network
|
|
39
|
+
* @returns The token balance as a bigint
|
|
40
|
+
*/
|
|
41
|
+
export const getBalanceAt = async (voterAddress, tokenAddress, snapshotBlock, chainId) => {
|
|
42
|
+
let chain;
|
|
43
|
+
switch (chainId) {
|
|
44
|
+
case 11155111:
|
|
45
|
+
chain = sepolia;
|
|
46
|
+
break;
|
|
47
|
+
case 31337:
|
|
48
|
+
chain = localhost;
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
throw new Error('Unsupported chainId');
|
|
52
|
+
}
|
|
53
|
+
const publicClient = createPublicClient({
|
|
54
|
+
transport: http(),
|
|
55
|
+
chain,
|
|
56
|
+
});
|
|
57
|
+
const balance = (await publicClient.readContract({
|
|
58
|
+
address: tokenAddress,
|
|
59
|
+
abi: ERC20Votes.abi,
|
|
60
|
+
functionName: 'getPastVotes',
|
|
61
|
+
args: [voterAddress, BigInt(snapshotBlock)],
|
|
62
|
+
}));
|
|
63
|
+
return balance;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Get the total supply of a ERC20Votes Token at a specific block
|
|
67
|
+
* @param tokenAddress The token address to query
|
|
68
|
+
* @param snapshotBlock The block number at which to get the total supply
|
|
69
|
+
* @param chainId The chain ID of the network
|
|
70
|
+
* @returns The total supply as a bigint
|
|
71
|
+
*/
|
|
72
|
+
export const getTotalSupplyAt = async (tokenAddress, snapshotBlock, chainId) => {
|
|
73
|
+
let chain;
|
|
74
|
+
switch (chainId) {
|
|
75
|
+
case 11155111:
|
|
76
|
+
chain = sepolia;
|
|
77
|
+
break;
|
|
78
|
+
case 31337:
|
|
79
|
+
chain = localhost;
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
throw new Error('Unsupported chainId');
|
|
83
|
+
}
|
|
84
|
+
const publicClient = createPublicClient({
|
|
85
|
+
transport: http(),
|
|
86
|
+
chain,
|
|
87
|
+
});
|
|
88
|
+
const totalSupply = (await publicClient.readContract({
|
|
89
|
+
address: tokenAddress,
|
|
90
|
+
abi: ERC20Votes.abi,
|
|
91
|
+
functionName: 'getPastTotalSupply',
|
|
92
|
+
args: [BigInt(snapshotBlock)],
|
|
93
|
+
}));
|
|
94
|
+
return totalSupply;
|
|
95
|
+
};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { LeanIMTMerkleProof } from '@zk-kit/lean-imt';
|
|
2
|
+
/**
|
|
3
|
+
* Interface representing the details of a specific round returned by the CRISP server
|
|
4
|
+
*/
|
|
5
|
+
export interface IRoundDetailsResponse {
|
|
6
|
+
id: string;
|
|
7
|
+
chain_id: string;
|
|
8
|
+
enclave_address: string;
|
|
9
|
+
status: string;
|
|
10
|
+
vote_count: string;
|
|
11
|
+
start_time: string;
|
|
12
|
+
duration: string;
|
|
13
|
+
expiration: string;
|
|
14
|
+
start_block: string;
|
|
15
|
+
committee_public_key: string[];
|
|
16
|
+
emojis: [string, string];
|
|
17
|
+
token_address: string;
|
|
18
|
+
balance_threshold: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Interface representing the details of a specific round in a more convenient format
|
|
22
|
+
*/
|
|
23
|
+
export interface IRoundDetails {
|
|
24
|
+
e3Id: bigint;
|
|
25
|
+
chainId: bigint;
|
|
26
|
+
enclaveAddress: string;
|
|
27
|
+
status: string;
|
|
28
|
+
voteCount: bigint;
|
|
29
|
+
startTime: bigint;
|
|
30
|
+
duration: bigint;
|
|
31
|
+
expiration: bigint;
|
|
32
|
+
startBlock: bigint;
|
|
33
|
+
committeePublicKey: string[];
|
|
34
|
+
emojis: [string, string];
|
|
35
|
+
tokenAddress: string;
|
|
36
|
+
balanceThreshold: bigint;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Interface representing the token details required for participation in a round
|
|
40
|
+
*/
|
|
41
|
+
export interface ITokenDetails {
|
|
42
|
+
tokenAddress: string;
|
|
43
|
+
threshold: bigint;
|
|
44
|
+
snapshotBlock: bigint;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Interface representing a Merkle proof
|
|
48
|
+
*/
|
|
49
|
+
export interface IMerkleProof {
|
|
50
|
+
leaf: bigint;
|
|
51
|
+
index: number;
|
|
52
|
+
proof: LeanIMTMerkleProof<bigint>;
|
|
53
|
+
length: number;
|
|
54
|
+
indices: number[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Enum representing the voting modes
|
|
58
|
+
*/
|
|
59
|
+
export declare enum VotingMode {
|
|
60
|
+
/**
|
|
61
|
+
* Governance voting requires to spend all credits on one option
|
|
62
|
+
they cannot be split
|
|
63
|
+
*/
|
|
64
|
+
GOVERNANCE = "GOVERNANCE"
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Interface representing a vote with power for 'yes' and 'no'
|
|
68
|
+
*/
|
|
69
|
+
export interface IVote {
|
|
70
|
+
/**
|
|
71
|
+
* The voting power for 'yes' votes
|
|
72
|
+
*/
|
|
73
|
+
yes: bigint;
|
|
74
|
+
/**
|
|
75
|
+
* The voting power for 'no' votes
|
|
76
|
+
*/
|
|
77
|
+
no: bigint;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Interface representing a vector with coefficients
|
|
81
|
+
*/
|
|
82
|
+
export interface Polynomial {
|
|
83
|
+
coefficients: string[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Interface representing cryptographic parameters
|
|
87
|
+
*/
|
|
88
|
+
export interface GrecoCryptographicParams {
|
|
89
|
+
q_mod_t: string;
|
|
90
|
+
qis: string[];
|
|
91
|
+
k0is: string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Interface representing Greco bounds
|
|
95
|
+
*/
|
|
96
|
+
export interface GrecoBoundParams {
|
|
97
|
+
e_bound: string;
|
|
98
|
+
u_bound: string;
|
|
99
|
+
k1_low_bound: string;
|
|
100
|
+
k1_up_bound: string;
|
|
101
|
+
p1_bounds: string[];
|
|
102
|
+
p2_bounds: string[];
|
|
103
|
+
pk_bounds: string[];
|
|
104
|
+
r1_low_bounds: string[];
|
|
105
|
+
r1_up_bounds: string[];
|
|
106
|
+
r2_bounds: string[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Interface representing Greco parameters
|
|
110
|
+
*/
|
|
111
|
+
export interface GrecoParams {
|
|
112
|
+
crypto: GrecoCryptographicParams;
|
|
113
|
+
bounds: GrecoBoundParams;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The inputs required for the CRISP circuit
|
|
117
|
+
*/
|
|
118
|
+
export interface CRISPCircuitInputs {
|
|
119
|
+
prev_ct0is: Polynomial[];
|
|
120
|
+
prev_ct1is: Polynomial[];
|
|
121
|
+
sum_ct0is: Polynomial[];
|
|
122
|
+
sum_ct1is: Polynomial[];
|
|
123
|
+
sum_r0is: Polynomial[];
|
|
124
|
+
sum_r1is: Polynomial[];
|
|
125
|
+
params: GrecoParams;
|
|
126
|
+
ct0is: Polynomial[];
|
|
127
|
+
ct1is: Polynomial[];
|
|
128
|
+
pk0is: Polynomial[];
|
|
129
|
+
pk1is: Polynomial[];
|
|
130
|
+
r1is: Polynomial[];
|
|
131
|
+
r2is: Polynomial[];
|
|
132
|
+
p1is: Polynomial[];
|
|
133
|
+
p2is: Polynomial[];
|
|
134
|
+
u: Polynomial;
|
|
135
|
+
e0: Polynomial;
|
|
136
|
+
e1: Polynomial;
|
|
137
|
+
k1: Polynomial;
|
|
138
|
+
public_key_x: string[];
|
|
139
|
+
public_key_y: string[];
|
|
140
|
+
signature: string[];
|
|
141
|
+
hashed_message: string[];
|
|
142
|
+
merkle_root: string;
|
|
143
|
+
merkle_proof_length: string;
|
|
144
|
+
merkle_proof_indices: string[];
|
|
145
|
+
merkle_proof_siblings: string[];
|
|
146
|
+
slot_address: string;
|
|
147
|
+
balance: string;
|
|
148
|
+
is_first_vote: boolean;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Interface representing the BFV parameters
|
|
152
|
+
*/
|
|
153
|
+
export interface BFVParams {
|
|
154
|
+
degree: number;
|
|
155
|
+
plaintextModulus: bigint;
|
|
156
|
+
moduli: BigInt64Array;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Interface representing the inputs for Noir signature verification
|
|
160
|
+
*/
|
|
161
|
+
export interface NoirSignatureInputs {
|
|
162
|
+
/**
|
|
163
|
+
* X coordinate of the public key
|
|
164
|
+
*/
|
|
165
|
+
pub_key_x: Uint8Array;
|
|
166
|
+
/**
|
|
167
|
+
* Y coordinate of the public key
|
|
168
|
+
*/
|
|
169
|
+
pub_key_y: Uint8Array;
|
|
170
|
+
/**
|
|
171
|
+
* The signature to verify
|
|
172
|
+
*/
|
|
173
|
+
signature: Uint8Array;
|
|
174
|
+
/**
|
|
175
|
+
* The hashed message that was signed
|
|
176
|
+
*/
|
|
177
|
+
hashed_message: Uint8Array;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Parameters for encryptVoteAndGenerateCRISPInputs function
|
|
181
|
+
*/
|
|
182
|
+
export interface EncryptVoteAndGenerateCRISPInputsParams {
|
|
183
|
+
encodedVote: string[];
|
|
184
|
+
publicKey: Uint8Array;
|
|
185
|
+
previousCiphertext: Uint8Array;
|
|
186
|
+
signature: `0x${string}`;
|
|
187
|
+
message: string;
|
|
188
|
+
merkleData: IMerkleProof;
|
|
189
|
+
balance: bigint;
|
|
190
|
+
bfvParams?: BFVParams;
|
|
191
|
+
slotAddress: string;
|
|
192
|
+
isFirstVote: boolean;
|
|
193
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
/**
|
|
7
|
+
* Enum representing the voting modes
|
|
8
|
+
*/
|
|
9
|
+
export var VotingMode;
|
|
10
|
+
(function (VotingMode) {
|
|
11
|
+
/**
|
|
12
|
+
* Governance voting requires to spend all credits on one option
|
|
13
|
+
they cannot be split
|
|
14
|
+
*/
|
|
15
|
+
VotingMode["GOVERNANCE"] = "GOVERNANCE";
|
|
16
|
+
})(VotingMode || (VotingMode = {}));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { LeanIMT } from '@zk-kit/lean-imt';
|
|
2
|
+
import type { IMerkleProof } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Hash a leaf node for the Merkle tree
|
|
5
|
+
* @param address The voter's address
|
|
6
|
+
* @param balance The voter's balance
|
|
7
|
+
* @returns The hashed leaf as a bigint
|
|
8
|
+
*/
|
|
9
|
+
export declare const hashLeaf: (address: string, balance: string) => bigint;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a new LeanIMT with the leaves provided
|
|
12
|
+
* @param leaves The leaves of the Merkle tree
|
|
13
|
+
* @returns the generated Merkle tree
|
|
14
|
+
*/
|
|
15
|
+
export declare const generateMerkleTree: (leaves: bigint[]) => LeanIMT;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a Merkle proof for a given address to prove inclusion in the voters' list
|
|
18
|
+
* @param threshold The minimum balance required to be eligible
|
|
19
|
+
* @param balance The voter's balance
|
|
20
|
+
* @param address The voter's address
|
|
21
|
+
* @param leaves The leaves of the Merkle tree
|
|
22
|
+
* @param maxDepth The maximum depth of the Merkle tree
|
|
23
|
+
*/
|
|
24
|
+
export declare const generateMerkleProof: (threshold: bigint, balance: bigint, address: string, leaves: bigint[], maxDepth: number) => IMerkleProof;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a number to its binary representation
|
|
27
|
+
* @param number The number to convert to binary
|
|
28
|
+
* @returns The binary representation of the number as a string
|
|
29
|
+
*/
|
|
30
|
+
export declare const toBinary: (number: bigint) => string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-3.0-only
|
|
2
|
+
//
|
|
3
|
+
// This file is provided WITHOUT ANY WARRANTY;
|
|
4
|
+
// without even the implied warranty of MERCHANTABILITY
|
|
5
|
+
// or FITNESS FOR A PARTICULAR PURPOSE.
|
|
6
|
+
import { poseidon2 } from 'poseidon-lite';
|
|
7
|
+
import { LeanIMT } from '@zk-kit/lean-imt';
|
|
8
|
+
/**
|
|
9
|
+
* Hash a leaf node for the Merkle tree
|
|
10
|
+
* @param address The voter's address
|
|
11
|
+
* @param balance The voter's balance
|
|
12
|
+
* @returns The hashed leaf as a bigint
|
|
13
|
+
*/
|
|
14
|
+
export const hashLeaf = (address, balance) => {
|
|
15
|
+
return poseidon2([address, balance]);
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Generate a new LeanIMT with the leaves provided
|
|
19
|
+
* @param leaves The leaves of the Merkle tree
|
|
20
|
+
* @returns the generated Merkle tree
|
|
21
|
+
*/
|
|
22
|
+
export const generateMerkleTree = (leaves) => {
|
|
23
|
+
return new LeanIMT((a, b) => poseidon2([a, b]), leaves);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Generate a Merkle proof for a given address to prove inclusion in the voters' list
|
|
27
|
+
* @param threshold The minimum balance required to be eligible
|
|
28
|
+
* @param balance The voter's balance
|
|
29
|
+
* @param address The voter's address
|
|
30
|
+
* @param leaves The leaves of the Merkle tree
|
|
31
|
+
* @param maxDepth The maximum depth of the Merkle tree
|
|
32
|
+
*/
|
|
33
|
+
export const generateMerkleProof = (threshold, balance, address, leaves, maxDepth) => {
|
|
34
|
+
if (balance < threshold) {
|
|
35
|
+
throw new Error('Balance is below the threshold');
|
|
36
|
+
}
|
|
37
|
+
const leaf = hashLeaf(address, balance.toString());
|
|
38
|
+
const index = leaves.findIndex((l) => l === leaf);
|
|
39
|
+
if (index === -1) {
|
|
40
|
+
throw new Error('Leaf not found in the tree');
|
|
41
|
+
}
|
|
42
|
+
const tree = generateMerkleTree(leaves);
|
|
43
|
+
const proof = tree.generateProof(index);
|
|
44
|
+
// Pad siblings with zeros
|
|
45
|
+
const paddedSiblings = [...proof.siblings, ...Array(maxDepth - proof.siblings.length).fill(0n)];
|
|
46
|
+
// Pad indices with zeros
|
|
47
|
+
const indices = proof.siblings.map((_, i) => Number((BigInt(proof.index) >> BigInt(i)) & 1n));
|
|
48
|
+
const paddedIndices = [...indices, ...Array(maxDepth - indices.length).fill(0)];
|
|
49
|
+
return {
|
|
50
|
+
leaf,
|
|
51
|
+
index,
|
|
52
|
+
proof: {
|
|
53
|
+
...proof,
|
|
54
|
+
siblings: paddedSiblings,
|
|
55
|
+
},
|
|
56
|
+
// Original length before padding
|
|
57
|
+
length: proof.siblings.length,
|
|
58
|
+
indices: paddedIndices,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Convert a number to its binary representation
|
|
63
|
+
* @param number The number to convert to binary
|
|
64
|
+
* @returns The binary representation of the number as a string
|
|
65
|
+
*/
|
|
66
|
+
export const toBinary = (number) => {
|
|
67
|
+
if (number < 0) {
|
|
68
|
+
throw new Error('Value cannot be negative');
|
|
69
|
+
}
|
|
70
|
+
return number.toString(2);
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BFVParams, type CRISPCircuitInputs, type EncryptVoteAndGenerateCRISPInputsParams, type IVote, VotingMode } from './types';
|
|
2
|
+
import { type ProofData } from '@aztec/bb.js';
|
|
3
|
+
/**
|
|
4
|
+
* This utility function calculates the first valid index for vote options
|
|
5
|
+
* based on the total voting power and degree.
|
|
6
|
+
* @dev This is needed to calculate the decoded plaintext
|
|
7
|
+
* @dev Also, we will need to check in the circuit that anything within these indices is
|
|
8
|
+
* either 0 or 1.
|
|
9
|
+
* @param totalVotingPower The maximum vote amount (if a single voter had all of the power)
|
|
10
|
+
* @param degree The degree of the polynomial
|
|
11
|
+
*/
|
|
12
|
+
export declare const calculateValidIndicesForPlaintext: (totalVotingPower: bigint, degree: number) => {
|
|
13
|
+
yesIndex: number;
|
|
14
|
+
noIndex: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Encode a vote based on the voting mode
|
|
18
|
+
* @param vote The vote to encode
|
|
19
|
+
* @param votingMode The voting mode to use for encoding
|
|
20
|
+
* @param votingPower The voting power of the voter
|
|
21
|
+
* @param bfvParams The BFV parameters to use for encoding
|
|
22
|
+
* @returns The encoded vote as a string
|
|
23
|
+
*/
|
|
24
|
+
export declare const encodeVote: (vote: IVote, votingMode: VotingMode, votingPower: bigint, bfvParams?: BFVParams) => string[];
|
|
25
|
+
/**
|
|
26
|
+
* Given an encoded tally, decode it into its decimal representation
|
|
27
|
+
* @param tally The encoded tally to decode
|
|
28
|
+
* @param votingMode The voting mode
|
|
29
|
+
*/
|
|
30
|
+
export declare const decodeTally: (tally: string[], votingMode: VotingMode) => IVote;
|
|
31
|
+
/**
|
|
32
|
+
* Validate whether a vote is valid for a given voting mode
|
|
33
|
+
* @param votingMode The voting mode to validate against
|
|
34
|
+
* @param vote The vote to validate
|
|
35
|
+
* @param votingPower The voting power of the voter
|
|
36
|
+
*/
|
|
37
|
+
export declare const validateVote: (votingMode: VotingMode, vote: IVote, votingPower: bigint) => void;
|
|
38
|
+
/**
|
|
39
|
+
* This is a wrapper around enclave-e3/sdk encryption functions as CRISP circuit will require some more
|
|
40
|
+
* input values which generic Greco do not need.
|
|
41
|
+
* @param encodedVote The encoded vote as string array
|
|
42
|
+
* @param publicKey The public key to use for encryption
|
|
43
|
+
* @param previousCiphertext The previous ciphertext to use for addition operation
|
|
44
|
+
* @param bfvParams The BFV parameters to use for encryption
|
|
45
|
+
* @param merkleData The merkle proof data
|
|
46
|
+
* @param message The message that was signed
|
|
47
|
+
* @param signature The signature of the message
|
|
48
|
+
* @param balance The voter's balance
|
|
49
|
+
* @param slotAddress The voter's slot address
|
|
50
|
+
* @param isFirstVote Whether this is the first vote for this slot
|
|
51
|
+
* @returns The CRISP circuit inputs
|
|
52
|
+
*/
|
|
53
|
+
export declare const encryptVoteAndGenerateCRISPInputs: ({ encodedVote, publicKey, previousCiphertext, bfvParams, merkleData, message, signature, balance, slotAddress, isFirstVote, }: EncryptVoteAndGenerateCRISPInputsParams) => Promise<CRISPCircuitInputs>;
|
|
54
|
+
/**
|
|
55
|
+
* A function to generate the data required to mask a vote
|
|
56
|
+
* @param voter The voter's address
|
|
57
|
+
* @param publicKey The voter's public key
|
|
58
|
+
* @param previousCiphertext The previous ciphertext
|
|
59
|
+
* @param bfvParams The BFV parameters
|
|
60
|
+
* @param merkleRoot The merkle root of the census tree
|
|
61
|
+
* @param slotAddress The voter's slot address
|
|
62
|
+
* @param isFirstVote Whether this is the first vote for this slot
|
|
63
|
+
* @returns The CRISP circuit inputs for a mask vote
|
|
64
|
+
*/
|
|
65
|
+
export declare const generateMaskVote: (publicKey: Uint8Array, previousCiphertext: Uint8Array, bfvParams: BFVParams | undefined, merkleRoot: bigint, slotAddress: string, isFirstVote: boolean) => Promise<CRISPCircuitInputs>;
|
|
66
|
+
export declare const generateProof: (crispInputs: CRISPCircuitInputs) => Promise<ProofData>;
|
|
67
|
+
export declare const generateProofWithReturnValue: (crispInputs: CRISPCircuitInputs) => Promise<{
|
|
68
|
+
returnValue: unknown;
|
|
69
|
+
proof: ProofData;
|
|
70
|
+
}>;
|
|
71
|
+
export declare const verifyProof: (proof: ProofData) => Promise<boolean>;
|