/
deploy.ts
356 lines (333 loc) · 12.4 KB
/
deploy.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import { ethers } from 'ethers'
import { Circuit, Prover, CircuitConfig } from '@unirep/circuits'
import { PoseidonT3 } from 'poseidon-solidity'
import GlobalFactory from 'global-factory'
import { Unirep, Unirep__factory as UnirepFactory } from '../typechain'
import {
compileVerifier,
createVerifierName,
linkLibrary,
tryPath,
} from './utils'
/**
* The current supported verifier helpers.
*/
const VerifierHelpers = {
epochKey: Circuit.epochKey,
epochKeyLite: Circuit.epochKeyLite,
reputation: Circuit.reputation,
}
/**
* Create the verififier helper contract name.
* Capitalize the first character and add `VerifierHelper` at the end.
* @param circuitName The name of the circuit
*/
const createVerifierHelperName = (circuitName: Circuit): string => {
const verifierName = Object.keys(VerifierHelpers).find(
(key) => VerifierHelpers[key] == circuitName
)
if (verifierName === undefined) {
throw new Error('Invalid verifier helper circuit')
}
return `${
verifierName.charAt(0).toUpperCase() + verifierName.slice(1)
}VerifierHelper`
}
/**
* Try a function several times.
* @param fn The function will be executed.
* @param maxRetry The maximum number of trying functions.
*/
export const retryAsNeeded = async (fn: any, maxRetry = 10) => {
let retryCount = 0
let backoff = 1000
for (;;) {
try {
return await fn()
} catch (err) {
if (++retryCount > maxRetry) throw err
backoff *= 2
console.log(`Failed, waiting ${backoff}ms`)
await new Promise((r) => setTimeout(r, backoff))
}
}
}
/**
* @param deployer A signer or an ethereum wallet
* @param circuitName Name of the circuit, which can be chosen from `Circuit`
* @param prover The prover which provides `vkey` of the circuit
* @returns The deployed verifier smart contract
*/
export const deployVerifier = async (
deployer: ethers.Signer,
circuitName: Circuit | string,
prover?: Prover
): Promise<ethers.Contract> => {
const contractName = createVerifierName(circuitName)
console.log(`Deploying ${contractName}`)
let artifacts: any
if (prover) {
const vkey = await prover.getVKey(circuitName)
artifacts = await compileVerifier(contractName, vkey)
} else {
const verifierPath = `contracts/verifiers/${contractName}.sol/${contractName}.json`
artifacts = tryPath(verifierPath)
}
const { bytecode, abi } = artifacts
const _verifierFactory = new ethers.ContractFactory(abi, bytecode, deployer)
const verifierFactory = await GlobalFactory(_verifierFactory)
const verifierContract = await retryAsNeeded(() => verifierFactory.deploy())
return verifierContract
}
/**
* @param deployer A signer or an ethereum wallet
* @param prover The prover which provides `vkey` of the circuit
* @returns All deployed verifier smart contracts
*/
export const deployVerifiers = async (
deployer: ethers.Signer,
prover?: Prover
): Promise<{ [circuit: string]: string }> => {
let verifiers = {}
for (const circuit in Circuit) {
const verifierContract = await deployVerifier(deployer, circuit, prover)
verifiers[circuit] = verifierContract.address
}
return verifiers
}
/**
* @param deployer A signer or an ethereum wallet
* @param prover The prover which provides `vkey` of the circuit
* @returns All deployed verifier helper contracts
*/
export const deployVerifierHelpers = async (
unirepAddress: string,
deployer: ethers.Signer,
prover?: Prover
): Promise<{ [circuit: string]: ethers.Contract }> => {
let verifierHelpers = {}
for (const verifierHelper in VerifierHelpers) {
const verifierContract = await deployVerifierHelper(
unirepAddress,
deployer,
VerifierHelpers[verifierHelper],
prover
)
verifierHelpers[verifierHelper] = verifierContract
}
return verifierHelpers
}
/**
* @param deployer A signer or an ethereum wallet
* @param circuitName Name of the circuit, which can be chosen from `Circuit`
* @param prover The prover which provides `vkey` of the circuit
* @returns The deployed verifier helper contracts
*/
export const deployVerifierHelper = async (
unirepAddress: string,
deployer: ethers.Signer,
circuitName: Circuit,
prover?: Prover
): Promise<ethers.Contract> => {
const verifier = await deployVerifier(deployer, circuitName, prover)
const contractName = createVerifierHelperName(circuitName)
console.log(`Deploying ${contractName}`)
let artifacts
if (prover) {
const vkey = await prover.getVKey(contractName)
artifacts = await compileVerifier(contractName, vkey)
} else {
const verifierPath = `contracts/verifierHelpers/${contractName}.sol/${contractName}.json`
artifacts = tryPath(verifierPath)
}
const { bytecode, abi } = artifacts
const _helperFactory = new ethers.ContractFactory(abi, bytecode, deployer)
const helperFactory = await GlobalFactory(_helperFactory)
const helperContract = await retryAsNeeded(() =>
helperFactory.deploy(unirepAddress, verifier.address)
)
await helperContract.deployed()
return helperContract
}
/**
* Deploy the unirep contract and verifier contracts with given `deployer` and settings
* @param deployer A signer who will deploy the contracts
* @param settings The settings that the deployer can define. See [`CircuitConfig`](https://developer.unirep.io/docs/circuits-api/circuit-config)
* @param prover The prover which provides `vkey` of the circuit
* @returns The Unirep smart contract
* @example
* ```ts
* import { ethers } from 'ethers'
* import { Unirep } from '@unirep/contracts'
* import { deployUnirep } from '@unirep/contracts/deploy'
* const privateKey = 'YOUR/PRIVATE/KEY'
* const provider = 'YOUR/ETH/PROVIDER'
* const deployer = new ethers.Wallet(privateKey, provider);
* const unirepContract: Unirep = await deployUnirep(deployer)
* ```
*
* :::caution
* The default circuit configuration is set in [`CircuitConfig.ts`](https://github.com/Unirep/Unirep/blob/1a3c9c944925ec125a7d7d8bfa9990466389477b/packages/circuits/src/CircuitConfig.ts).<br/>
* Please make sure the `CircuitConfig` matches your [`prover`](circuits-api/interfaces/src.Prover.md).
* If you don't compile circuits on your own, please don't change the `_settings` and `prover`.<br/>
* See the current prover and settings of deployed contracts: [🤝 Testnet Deployment](https://developer.unirep.io/docs/testnet-deployment).
* :::
*/
export const deployUnirep = async (
deployer: ethers.Signer,
settings?: CircuitConfig,
prover?: Prover
): Promise<Unirep> => {
if (!deployer.provider) {
throw new Error('Deployer must have provider')
}
const config = new CircuitConfig({ ...CircuitConfig.default, ...settings })
const {
EPOCH_TREE_DEPTH,
STATE_TREE_DEPTH,
HISTORY_TREE_DEPTH,
NUM_EPOCH_KEY_NONCE_PER_EPOCH,
FIELD_COUNT,
SUM_FIELD_COUNT,
REPL_NONCE_BITS,
REPL_FIELD_BITS,
} = new CircuitConfig(settings)
console.log(
'-----------------------------------------------------------------'
)
console.log(`Epoch tree depth: ${EPOCH_TREE_DEPTH}`)
console.log(`State tree depth: ${STATE_TREE_DEPTH}`)
console.log(`History tree depth: ${HISTORY_TREE_DEPTH}`)
console.log(
`Number of epoch keys per epoch: ${NUM_EPOCH_KEY_NONCE_PER_EPOCH}`
)
console.log(`Total fields per user: ${FIELD_COUNT}`)
console.log(`Sum fields per user: ${SUM_FIELD_COUNT}`)
console.log(`Replacement field nonce bits: ${REPL_NONCE_BITS}`)
console.log(`Replacement field data bits: ${REPL_FIELD_BITS}`)
console.log(
'-----------------------------------------------------------------'
)
console.log(`Make sure these match what you expect!`)
console.log(
'-----------------------------------------------------------------'
)
if ((await deployer.provider.getCode(PoseidonT3.proxyAddress)) === '0x') {
await retryAsNeeded(() =>
deployer.sendTransaction({
to: PoseidonT3.from,
value: PoseidonT3.gas,
})
)
await retryAsNeeded(() =>
deployer.provider?.sendTransaction(PoseidonT3.tx)
)
}
if ((await deployer.provider.getCode(PoseidonT3.address)) === '0x') {
// nothing to do, contract is already deployed
await retryAsNeeded(() =>
deployer.sendTransaction({
to: PoseidonT3.proxyAddress,
data: PoseidonT3.data,
})
)
}
const incPath =
'@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol/IncrementalBinaryTree.json'
const incArtifacts: any = tryPath(incPath)
const _incrementalMerkleTreeFactory = new ethers.ContractFactory(
incArtifacts.abi,
linkLibrary(incArtifacts.bytecode, {
['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: PoseidonT3.address,
}),
deployer
)
const incrementalMerkleTreeFactory = await GlobalFactory(
_incrementalMerkleTreeFactory
)
const incrementalMerkleTreeLib = await retryAsNeeded(() =>
incrementalMerkleTreeFactory.deploy()
)
await incrementalMerkleTreeLib.deployed()
const reusableMerklePath =
'contracts/libraries/ReusableMerkleTree.sol/ReusableMerkleTree.json'
const reusableMerkleArtifacts = tryPath(reusableMerklePath)
const _reusableMerkleFactory = new ethers.ContractFactory(
reusableMerkleArtifacts.abi,
linkLibrary(reusableMerkleArtifacts.bytecode, {
['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: PoseidonT3.address,
}),
deployer
)
const reusableMerkleFactory = await GlobalFactory(_reusableMerkleFactory)
const reusableMerkleContract = await retryAsNeeded(() =>
reusableMerkleFactory.deploy()
)
await reusableMerkleContract.deployed()
const lazyMerklePath =
'contracts/libraries/LazyMerkleTree.sol/LazyMerkleTree.json'
const lazyMerkleArtifacts = tryPath(lazyMerklePath)
const _lazyMerkleFactory = new ethers.ContractFactory(
lazyMerkleArtifacts.abi,
linkLibrary(lazyMerkleArtifacts.bytecode, {
['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: PoseidonT3.address,
}),
deployer
)
const lazyMerkleFactory = await GlobalFactory(_lazyMerkleFactory)
const lazyMerkleContract = await retryAsNeeded(() =>
lazyMerkleFactory.deploy()
)
await lazyMerkleContract.deployed()
const verifiers = await deployVerifiers(deployer, prover)
console.log('Deploying Unirep')
const c: Unirep = await retryAsNeeded(async () =>
(
await GlobalFactory(
new UnirepFactory(
{
['@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol:IncrementalBinaryTree']:
incrementalMerkleTreeLib.address,
['contracts/libraries/ReusableMerkleTree.sol:ReusableMerkleTree']:
reusableMerkleContract.address,
['contracts/libraries/LazyMerkleTree.sol:LazyMerkleTree']:
lazyMerkleContract.address,
['poseidon-solidity/PoseidonT3.sol:PoseidonT3']:
PoseidonT3.address,
},
deployer
)
)
).deploy(
config.contractConfig,
verifiers[Circuit.signup],
verifiers[Circuit.userStateTransition]
)
)
await retryAsNeeded(() => c.deployTransaction?.wait())
// Print out deployment info
console.log(
'-----------------------------------------------------------------'
)
console.log(
'Bytecode size of Unirep:',
Math.floor(UnirepFactory.bytecode.length / 2),
'bytes'
)
if (c.deployTransaction) {
const receipt = await deployer.provider?.getTransactionReceipt(
c.deployTransaction.hash
)
console.log(
'Gas cost of deploying Unirep:',
receipt?.gasUsed.toString()
)
} else {
console.log('Re-using existing Unirep deployment')
}
console.log(`Deployed to: ${c.address}`)
console.log(
'-----------------------------------------------------------------'
)
return c
}