1
+ import { ethers , BigNumber } from 'ethers'
2
+ import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree"
3
+ import { poseidon } from "circomlibjs" // v0.0.8
4
+
5
+ import { ZqField } from 'ffjavascript'
6
+ const SNARK_FIELD_SIZE = BigInt ( '21888242871839275222246405745257275088548364400416034343698204186575808495617' )
7
+
8
+ // Creates the finite field
9
+ const Fq = new ZqField ( SNARK_FIELD_SIZE )
10
+
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ const snarkjs = require ( 'snarkjs' ) ;
13
+
14
+ const logger = {
15
+ info : ( ...args ) => console . log ( ...args ) ,
16
+ debug : ( ...args ) => console . log ( ...args ) ,
17
+ error : ( ...args ) => console . error ( ...args ) ,
18
+ }
19
+
20
+ /**
21
+ * Recovers secret from two shares
22
+ * @param x1 signal hash of first message
23
+ * @param x2 signal hash of second message
24
+ * @param y1 yshare of first message
25
+ * @param y2 yshare of second message
26
+ * @returns identity secret
27
+ */
28
+ function shamirRecovery ( x1 : bigint , x2 : bigint , y1 : bigint , y2 : bigint ) : bigint {
29
+ const slope = Fq . div ( Fq . sub ( y2 , y1 ) , Fq . sub ( x2 , x1 ) )
30
+ const privateKey = Fq . sub ( y1 , Fq . mul ( slope , x1 ) )
31
+
32
+ return Fq . normalize ( privateKey )
33
+ }
34
+
35
+ function hash ( message : any ) : bigint {
36
+ message = BigNumber . from ( message ) . toTwos ( 256 ) . toHexString ( )
37
+ message = ethers . utils . zeroPad ( message , 32 )
38
+ return BigInt ( ethers . utils . keccak256 ( message ) ) >> BigInt ( 8 )
39
+ }
40
+
41
+ function hashNullifier ( message : any ) : bigint {
42
+ return BigInt ( ethers . utils . keccak256 ( message ) ) >> BigInt ( 8 )
43
+ }
44
+
45
+ async function prove ( signals , wasm , wtns , r1cs , zkey_final , vKey ) {
46
+ console . log ( 'calculate' )
47
+ await snarkjs . wtns . calculate ( signals , wasm , wtns ) ;
48
+
49
+ console . log ( 'check' )
50
+ await snarkjs . wtns . check ( r1cs , wtns , logger ) ;
51
+
52
+ console . log ( 'prove' )
53
+ const { proof, publicSignals } = await snarkjs . groth16 . prove ( zkey_final , wtns ) ;
54
+
55
+ const verified = await snarkjs . groth16 . verify ( vKey , publicSignals , proof , logger ) ;
56
+ console . log ( 'zk proof validity' , verified ) ;
57
+ return {
58
+ proof,
59
+ x : publicSignals [ 3 ] ,
60
+ y : publicSignals [ 0 ]
61
+ }
62
+ }
63
+
64
+ ( async ( ) => {
65
+ try {
66
+ // @ts -ignore
67
+ const r1csBuffer = await remix . call ( 'fileManager' , 'readFile' , 'circuits/.bin/rln.r1cs' , true ) ;
68
+ // @ts -ignore
69
+ const r1cs = new Uint8Array ( r1csBuffer ) ;
70
+ // @ts -ignore
71
+ const wasmBuffer = await remix . call ( 'fileManager' , 'readFile' , 'circuits/.bin/rln.wasm' , true ) ;
72
+ // @ts -ignore
73
+ const wasm = new Uint8Array ( wasmBuffer ) ;
74
+
75
+ const zkey_final = {
76
+ type : "mem" ,
77
+ data : new Uint8Array ( JSON . parse ( await remix . call ( 'fileManager' , 'readFile' , './zk/build/zk_setup.txt' ) ) )
78
+ }
79
+ const wtns = { type : "mem" } ;
80
+
81
+ const vKey = JSON . parse ( await remix . call ( 'fileManager' , 'readFile' , './zk/build/verification_key.json' ) )
82
+
83
+ // build list of identity commitments
84
+ const secrets = [ ]
85
+ const identityCommitments = [ ]
86
+ const rateCommitments = [ ]
87
+ const userMessageLimit = 0x2
88
+ for ( let k = 0 ; k < 2 ; k ++ ) {
89
+ const identitySecret = BigInt ( ethers . utils . hexlify ( ethers . utils . randomBytes ( 32 ) ) )
90
+ secrets . push ( identitySecret )
91
+
92
+ const identityCommitment = poseidon ( [ identitySecret ] )
93
+ const rateCommitment = poseidon ( [ identityCommitment , userMessageLimit ] )
94
+ identityCommitments . push ( identityCommitment )
95
+ rateCommitments . push ( rateCommitment )
96
+ }
97
+
98
+ let tree
99
+
100
+ try {
101
+ tree = new IncrementalMerkleTree ( poseidon , 20 , BigInt ( 0 ) , 2 , rateCommitments ) // Binary tree.
102
+ } catch ( e ) {
103
+ console . error ( e . message )
104
+ return
105
+ }
106
+
107
+ const merkleproof1 = tree . createProof ( 0 )
108
+
109
+ const appId = 0xa
110
+ const epoch = 0x1
111
+
112
+ const signals1 = {
113
+ identitySecret : secrets [ 0 ] ,
114
+ userMessageLimit,
115
+ messageId : 0x0 ,
116
+ pathElements : merkleproof1 . siblings ,
117
+ identityPathIndex : merkleproof1 . pathIndices ,
118
+ x : 0xabcd , // hash(message)
119
+ externalNullifier : 0xa // hash(epoch, appId)
120
+ }
121
+ const proof1 = await prove ( signals1 , wasm , wtns , r1cs , zkey_final , vKey )
122
+
123
+ const signals2 = {
124
+ identitySecret : secrets [ 0 ] ,
125
+ userMessageLimit,
126
+ messageId : 0x0 ,
127
+ pathElements : merkleproof1 . siblings ,
128
+ identityPathIndex : merkleproof1 . pathIndices ,
129
+ x : 0xabce , // hash(message)
130
+ externalNullifier : 0xa // hash(epoch, appId)
131
+ }
132
+ const proof2 = await prove ( signals2 , wasm , wtns , r1cs , zkey_final , vKey )
133
+
134
+ const secret = shamirRecovery ( BigInt ( proof1 . x ) , BigInt ( proof2 . x ) , BigInt ( proof1 . y ) , BigInt ( proof2 . y ) )
135
+
136
+ console . log ( secret . toString ( 10 ) )
137
+ console . log ( Fq . normalize ( secrets [ 0 ] ) )
138
+ } catch ( e ) {
139
+ console . error ( e . message )
140
+ }
141
+ } ) ( )
0 commit comments