forked from clrfund/monorepo
-
Notifications
You must be signed in to change notification settings - Fork 1
/
maci.ts
140 lines (131 loc) · 3.98 KB
/
maci.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { Contract, BigNumber } from 'ethers'
import { genRandomSalt, IncrementalQuinTree } from 'maci-crypto'
import { Keypair, PubKey, Command, Message } from 'maci-domainobjs'
export class MaciParameters {
stateTreeDepth = 32
messageTreeDepth = 32
voteOptionTreeDepth = 3
tallyBatchSize = 8
messageBatchSize = 8
batchUstVerifier!: string
qvtVerifier!: string
signUpDuration = 7 * 86400
votingDuration = 7 * 86400
constructor(parameters: { [name: string]: any } = {}) {
this.update(parameters)
}
update(parameters: { [name: string]: any }) {
for (const [name, value] of Object.entries(parameters)) {
;(this as any)[name] = value
}
}
values(): any[] {
// To be passed to setMaciParameters()
return [
this.stateTreeDepth,
this.messageTreeDepth,
this.voteOptionTreeDepth,
this.tallyBatchSize,
this.messageBatchSize,
this.batchUstVerifier,
this.qvtVerifier,
this.signUpDuration,
this.votingDuration,
]
}
static async read(maciFactory: Contract): Promise<MaciParameters> {
const { stateTreeDepth, messageTreeDepth, voteOptionTreeDepth } =
await maciFactory.treeDepths()
const { tallyBatchSize, messageBatchSize } = await maciFactory.batchSizes()
const batchUstVerifier = await maciFactory.batchUstVerifier()
const qvtVerifier = await maciFactory.qvtVerifier()
const signUpDuration = (await maciFactory.signUpDuration()).toNumber()
const votingDuration = (await maciFactory.votingDuration()).toNumber()
return new MaciParameters({
stateTreeDepth,
messageTreeDepth,
voteOptionTreeDepth,
tallyBatchSize,
messageBatchSize,
batchUstVerifier,
qvtVerifier,
signUpDuration,
votingDuration,
})
}
}
export function bnSqrt(a: BigNumber): BigNumber {
// Take square root from a big number
// https://stackoverflow.com/a/52468569/1868395
if (a.isZero()) {
return a
}
let x
let x1 = a.div(2)
do {
x = x1
x1 = x.add(a.div(x)).div(2)
} while (!x.eq(x1))
return x
}
export function createMessage(
userStateIndex: number,
userKeypair: Keypair,
newUserKeypair: Keypair | null,
coordinatorPubKey: PubKey,
voteOptionIndex: number | null,
voiceCredits: BigNumber | null,
nonce: number,
salt?: BigInt
): [Message, PubKey] {
const encKeypair = new Keypair()
if (!salt) {
salt = genRandomSalt()
}
const quadraticVoteWeight = voiceCredits ? bnSqrt(voiceCredits) : 0
const command = new Command(
BigInt(userStateIndex),
newUserKeypair ? newUserKeypair.pubKey : userKeypair.pubKey,
BigInt(voteOptionIndex || 0),
BigInt(quadraticVoteWeight),
BigInt(nonce),
BigInt(salt)
)
const signature = command.sign(userKeypair.privKey)
const message = command.encrypt(
signature,
Keypair.genEcdhSharedKey(encKeypair.privKey, coordinatorPubKey)
)
return [message, encKeypair.pubKey]
}
export function getRecipientClaimData(
recipientIndex: number,
recipientTreeDepth: number,
tally: any
): any[] {
// Create proof for tally result
const result = tally.results.tally[recipientIndex]
const resultSalt = tally.results.salt
const resultTree = new IncrementalQuinTree(recipientTreeDepth, BigInt(0))
for (const leaf of tally.results.tally) {
resultTree.insert(leaf)
}
const resultProof = resultTree.genMerklePath(recipientIndex)
// Create proof for total amount of spent voice credits
const spent = tally.totalVoiceCreditsPerVoteOption.tally[recipientIndex]
const spentSalt = tally.totalVoiceCreditsPerVoteOption.salt
const spentTree = new IncrementalQuinTree(recipientTreeDepth, BigInt(0))
for (const leaf of tally.totalVoiceCreditsPerVoteOption.tally) {
spentTree.insert(leaf)
}
const spentProof = spentTree.genMerklePath(recipientIndex)
return [
recipientIndex,
result,
resultProof.pathElements.map((x) => x.map((y) => y.toString())),
resultSalt,
spent,
spentProof.pathElements.map((x) => x.map((y) => y.toString())),
spentSalt,
]
}