def test_make_proof(self): wrapper = Mixer(NATIVE_LIB_PATH, VK_PATH, PK_PATH) tree_depth = wrapper.tree_depth n_items = 2 << (tree_depth - 1) tree = MerkleTree(n_items) for n in range(0, 2): tree.append(int(FQ.random())) wallet_address = int(FQ.random()) nullifier_secret = int(FQ.random()) nullifier_hash = mimc_hash([nullifier_secret, nullifier_secret]) leaf_hash = int( get_sha256_hash(to_hex(nullifier_secret), to_hex(wallet_address)), 16) leaf_idx = tree.append(leaf_hash) self.assertEqual(leaf_idx, tree.index(leaf_hash)) # Verify it exists in true leaf_proof = tree.proof(leaf_idx) self.assertTrue(leaf_proof.verify(tree.root)) # Generate proof snark_proof = wrapper.prove( tree.root, wallet_address, nullifier_hash, nullifier_secret, # (index)_2 bits reversed, i.e. [LSB, ... , MSB] leaf_proof.address, leaf_proof.path) self.assertTrue(wrapper.verify(snark_proof))
def test_make_proof(self): n_items = 2 << 28 tree = MerkleTree(n_items) for n in range(0, 2): tree.append(int(FQ.random())) exthash = int(FQ.random()) prehash = int(FQ.random()) secret = int(FQ.random()) msg = int(FQ.random()) leaf_hash = mimc_hash([secret]) sig_msg_hash = mimc_hash([msg]) leaf_idx = tree.append(leaf_hash) self.assertEqual(leaf_idx, tree.index(leaf_hash)) # Verify it exists in true leaf_proof = tree.proof(leaf_idx) self.assertTrue(leaf_proof.verify(tree.root)) self.assertTrue(prehash, sig_msg_hash) # Generate proof wrapper = Ssles(NATIVE_LIB_PATH, VK_PATH, PK_PATH) tree_depth = wrapper.tree_depth snark_proof = wrapper.prove(tree.root, secret, msg, exthash, prehash, leaf_proof.address, leaf_proof.path) self.assertTrue(wrapper.verify(snark_proof)) if __name__ == "__main__": unittest.main()
def test_make_proof(self): n_items = 2<<28 tree = MerkleTree(n_items) for n in range(0, 2): tree.append(int(FQ.random())) exthash = int(FQ.random()) secret = int(FQ.random()) leaf_hash = mimc_hash([secret]) leaf_idx = tree.append(leaf_hash) self.assertEqual(leaf_idx, tree.index(leaf_hash)) # Verify it exists in true leaf_proof = tree.proof(leaf_idx) self.assertTrue(leaf_proof.verify(tree.root)) # Generate proof wrapper = Miximus(NATIVE_LIB_PATH, VK_PATH, PK_PATH) tree_depth = wrapper.tree_depth snark_proof = wrapper.prove( tree.root, secret, exthash, leaf_proof.address, leaf_proof.path) self.assertTrue(wrapper.verify(snark_proof))
def test_update(self): """ Verify that items in the tree can be updated """ tree = MerkleTree(2) tree.append(FQ.random()) tree.append(FQ.random()) proof_0_before = tree.proof(0) proof_1_before = tree.proof(1) root_before = tree.root self.assertTrue(proof_0_before.verify(tree.root)) self.assertTrue(proof_1_before.verify(tree.root)) leaf_0_after = FQ.random() tree.update(0, leaf_0_after) root_after_0 = tree.root proof_0_after = tree.proof(0) self.assertTrue(proof_0_after.verify(tree.root)) self.assertNotEqual(root_before, root_after_0) leaf_1_after = FQ.random() tree.update(1, leaf_1_after) root_after_1 = tree.root proof_1_after = tree.proof(1) self.assertTrue(proof_1_after.verify(tree.root)) self.assertNotEqual(root_before, root_after_1) self.assertNotEqual(root_after_0, root_after_1)
def test_6_recover_y(self): """ There are two y points for every x """ for _ in range(0, 10): p = self._point_r() q = Point.from_x(p.x) self.assertEqual(p.x, q.x) self.assertTrue(p.y in [q.y, -q.y]) # These confirm compatibility across implementations known_test_cases = [ (20616554786359396897066290204264220576319536076538991133935783866206841138898, 10592275084648178561464128859907688344447649297734555224341876545305639835999), (11610117029953798428826613242669939481045605849364609771767823351326159443609, 3722409228507723418678713896319610332389736117851027921973860155000856891140), (21680045038775759642189425577922609025982451102460978847266452551495203884482, 6168854640927408084732268325506202000962285527703379133980054444068219727690), (18879782252170350866370777185563748782908354718484814019474117245310535071541, 2946855428411022359321514310392164228862398839132752152798293872913224129374) ] for x, y in known_test_cases: x, y = FQ(x), FQ(y) q = Point.from_y(y) self.assertEqual(q.x, x)
def test_random(self): # Randomized tests for _ in range(0, 10): alpha = [FQ.random() for _ in range(0, 4)] points = [(FQ(i), shamirs_poly(FQ(i), alpha)) for i in range(0, len(alpha))] assert alpha[0] == lagrange(points, 0) assert alpha[0] != lagrange(points[1:], 0) assert alpha[0] != lagrange(points[2:], 0)
def test_multiplicative(self): G = self._point_r() a = FQ.random() A = G*a b = FQ.random() B = G*b ab = (a.n * b.n) % JUBJUB_E AB = G*ab self.assertEqual(A*b, AB) self.assertEqual(B*a, AB)
def __init__(self, var, coeff=None): assert isinstance(var, Variable) self.var = var if coeff is None: coeff = FQ(1) elif isinstance(coeff, int_types): coeff = FQ(coeff) if not isinstance(coeff, FQ): raise TypeError( 'Coefficient expected to be field element, but got %r' % (type(coeff), )) self.coeff = coeff
def message(self): msg_parts = [ FQ(int(self.realmID), 1 << 32), FQ(int(self.accountID), 1 << 20), FQ(int(self.tokenID), 1 << 8), FQ(int(self.amountRequested), 1 << 96), FQ(int(self.walletAccountID), 1 << 20), FQ(int(self.feeTokenID), 1 << 8), FQ(int(self.fee), 1 << 96), FQ(int(self.walletSplitPercentage), 1 << 7), FQ(int(self.nonce), 1 << 32), FQ(int(0), 1 << 1) ] return PureEdDSA.to_bits(*msg_parts)
def message(self): msg_parts = [ FQ(int(self.realmID), 1 << 32), FQ(int(self.accountID), 1 << 20), FQ(int(self.orderTokenID), 1 << 8), FQ(int(self.orderID), 1 << 32), FQ(int(self.dualAuthorAccountID), 1 << 20), FQ(int(self.feeTokenID), 1 << 8), FQ(int(self.fee), 1 << 96), FQ(int(self.walletSplitPercentage), 1 << 7), FQ(int(self.nonce), 1 << 32), FQ(int(0), 1 << 2) ] return PureEdDSA.to_bits(*msg_parts)
def test_random_small(self): q = 100003 for _ in range(0, 10): alpha = [FQ.random(q) for _ in range(0, 4)] points = [(FQ(i, q), shamirs_poly(FQ(i, q), alpha)) for i in range(0, len(alpha))] assert alpha[0] == lagrange(points, 0) assert alpha[0] != lagrange(points[1:], 0) assert alpha[0] != lagrange(points[2:], 0) # XXX: scipy's lagrange has floating point precision for large numbers points_x, points_y = unzip(points) interpolation = scipy_lagrange([_.n for _ in points_x], [_.n for _ in points_y]) assert int(interpolation.c[-1]) == alpha[0]
def test_mult_all_known(self): rp = self._point_a() all_points = [rp, rp.as_proj(), rp.as_etec(), rp.as_mont()] expected = Point( FQ(6317123931401941284657971611369077243307682877199795030160588338302336995127 ), FQ(17705894757276775630165779951991641206660307982595100429224895554788146104270 )) for p in all_points: q = p.mult( 6890855772600357754907169075114257697580319025794532037257385534741338397365 ) r = q.as_point() self.assertEqual(r.x, expected.x) self.assertEqual(r.y, expected.y)
def test_fromdocs2(self): p = 100003 k = 4 a = [FQ.random(p) for _ in range(0, k)] # [6257, 85026, 44499, 14701] F = lambda i, x: a[i] * (x**i) X = lambda x: a[0] + F(1, x) + F(2, x) + F(3, x) # Create the shares Sx = range(1, 5) Sy = [X(_) for _ in Sx] for x, y in zip(Sx, Sy): z = shamirs_poly(FQ(x, p), a) assert z == y # Then recover secret result = int(scipy_lagrange(Sx, [_.n for _ in Sy]).c[-1]) % p assert a[0] == result
def orderFromJSON(jOrder, state): realmID = int(jOrder["realmID"]) orderID = int(jOrder["orderID"]) accountID = int(jOrder["accountID"]) walletAccountID = int(jOrder["walletAccountID"]) dualAuthPublicKeyX = int(jOrder["dualAuthPublicKeyX"]) dualAuthPublicKeyY = int(jOrder["dualAuthPublicKeyY"]) dualAuthSecretKey = int(jOrder["dualAuthSecretKey"]) tokenS = int(jOrder["tokenIdS"]) tokenB = int(jOrder["tokenIdB"]) tokenF = int(jOrder["tokenIdF"]) amountS = int(jOrder["amountS"]) amountB = int(jOrder["amountB"]) amountF = int(jOrder["amountF"]) allOrNone = int(jOrder["allOrNone"]) validSince = int(jOrder["validSince"]) validUntil = int(jOrder["validUntil"]) walletSplitPercentage = int(jOrder["walletSplitPercentage"]) waiveFeePercentage = int(jOrder["waiveFeePercentage"]) account = state.getAccount(accountID) walletAccount = state.getAccount(walletAccountID) order = Order(Point(account.publicKeyX, account.publicKeyY), Point(walletAccount.publicKeyX, walletAccount.publicKeyY), Point(dualAuthPublicKeyX, dualAuthPublicKeyY), dualAuthSecretKey, realmID, orderID, accountID, walletAccountID, tokenS, tokenB, tokenF, amountS, amountB, amountF, allOrNone, validSince, validUntil, walletSplitPercentage, waiveFeePercentage) order.sign(FQ(int(account.secretKey))) return order
def offchainWithdraw(self, realmID, accountID, tokenID, amountRequested, operatorAccountID, walletAccountID, feeTokenID, fee, walletSplitPercentage): feeToWallet = int(fee) * walletSplitPercentage // 100 feeToOperator = int(fee) - feeToWallet # Update account rootBefore = self._accountsTree._root accountBefore = copyAccountInfo(self.getAccount(accountID)) nonce = accountBefore.nonce proof = self._accountsTree.createProof(accountID) balanceUpdateF_A = self.getAccount(accountID).updateBalance( feeTokenID, -fee) balance = int(self.getAccount(accountID).getBalance(tokenID)) amountWithdrawn = int(amountRequested) if ( int(amountRequested) < balance) else balance balanceUpdateW_A = self.getAccount(accountID).updateBalance( tokenID, -amountWithdrawn) self.getAccount(accountID).nonce += 1 self.updateAccountTree(accountID) accountAfter = copyAccountInfo(self.getAccount(accountID)) rootAfter = self._accountsTree._root accountUpdate_A = AccountUpdateData(accountID, proof, rootBefore, rootAfter, accountBefore, accountAfter) ### # Update wallet rootBefore = self._accountsTree._root accountBefore = copyAccountInfo(self.getAccount(walletAccountID)) proof = self._accountsTree.createProof(walletAccountID) balanceUpdateF_W = self.getAccount(walletAccountID).updateBalance( feeTokenID, feeToWallet) self.updateAccountTree(walletAccountID) accountAfter = copyAccountInfo(self.getAccount(walletAccountID)) rootAfter = self._accountsTree._root accountUpdate_W = AccountUpdateData(walletAccountID, proof, rootBefore, rootAfter, accountBefore, accountAfter) ### # Operator payment balanceUpdateF_O = self.getAccount(operatorAccountID).updateBalance( feeTokenID, feeToOperator) account = self.getAccount(accountID) withdrawal = OffchainWithdrawal( realmID, accountID, tokenID, amountRequested, amountWithdrawn, walletAccountID, feeTokenID, fee, walletSplitPercentage, balanceUpdateF_A, balanceUpdateW_A, accountUpdate_A, balanceUpdateF_W, accountUpdate_W, balanceUpdateF_O, nonce) withdrawal.sign(FQ(int(account.secretKey))) return withdrawal
def __init__(self): """ The state holds all variables and linear combinations used by the program - Variables hold a single field element - Linear combinations hold a combination of variables Linear combinations can be used to combine multiple operations on variables into a single statement, whenever a variable is multiplied by a constant or two variables (optionally multipled by constants) are added together etc. This forms the basis of many optimisations, think of linear combinations as temporary variables, where intermediate results which don't require a constraint of their own can be calculated, stored and used in the same way as normal variables. Each variable or linear combination is addressed by an index, an index can only be a linear combination *or* a variable, but none will have the same index as another. When writing a function it can accept zero or more inputs, and emit zero or more outputs. For example, an `assert` statement takes one or more inputs and emits none, an `add` statement takes two or more inputs and emits one output. The inputs can be any combination of variables or linear combinations, as can the outputs. If a linear combination or variable is unused by any constraints then it has no purpose. """ self._vars = OrderedDict() self._lcs = dict() self._values = dict() self.var_new('ONE', value=FQ(1))
def message(self): msg_parts = [ FQ(int(self.orderA.hash), 1 << 254), FQ(int(self.orderB.hash), 1 << 254), FQ(int(self.orderA.waiveFeePercentage), 1 << 7), FQ(int(self.orderB.waiveFeePercentage), 1 << 7), FQ(int(self.minerAccountID), 1 << 20), FQ(int(self.tokenID), 1 << 8), FQ(int(self.fee), 1 << 96), FQ(int(self.feeRecipientAccountID), 1 << 20), FQ(int(self.nonce), 1 << 32) ] return PureEdDSA.to_bits(*msg_parts)
def set_value(self, idx, value): if not isinstance(value, FQ): if not isinstance(value, int_types): raise ProgramError("Value (%r=%r) is of wrong type: %r" % (idx, value, type(value))) value = FQ(value) if idx not in self.inputs and idx not in self.secrets: raise ProgramError("Cannot set a value (%r=%r) that's neither an input nor a secret" % (idx, value)) self.state.var_value_set(idx, value)
def test_mont_double(self): """ Verified in Sage, using `ejubjub.py` Ensure that addition laws remain the same between Montgomery and Edwards coordinates """ q = Point.from_hash(b'x') mq = MontPoint(FQ(4828722366376575650251607168518886976429844446767098803596167689250506416759), FQ(12919092401030192644826086113396919334232812611316996694878363256143428656958)) self.assertEqual(q.as_mont(), mq) q2 = MontPoint(FQ(760569539648116659146730905587051427168718890872716379895718021693339839266), FQ(19523163946365579499783218718995636854804792079073783994015125253921919723342)) self.assertEqual(q.double().as_mont(), q2) for _ in range(0, 10): p = Point.from_hash(urandom(32)) self.assertEqual(p.as_mont().double().as_edwards_yz().as_point().as_edwards_yz(), p.double().as_edwards_yz())
def loopring_sign(input_message, private_key): print(f"loopring sign message {input_message}") hasher = hashlib.sha256() hasher.update(input_message.encode('utf-8')) msgHash = int(hasher.hexdigest(), 16) % SNARK_SCALAR_FIELD signed = PoseidonEdDSA.sign(msgHash, FQ(int(private_key))) signature = ','.join( str(_) for _ in [signed.sig.R.x, signed.sig.R.y, signed.sig.s]) return signature
def ringFromJSON(jRing, state): orderA = orderFromJSON(jRing["orderA"], state) orderB = orderFromJSON(jRing["orderB"], state) minerAccountID = int(jRing["minerAccountID"]) feeRecipientAccountID = int(jRing["feeRecipientAccountID"]) tokenID = int(jRing["tokenID"]) fee = int(jRing["fee"]) minerAccount = state.getAccount(minerAccountID) ring = Ring(orderA, orderB, minerAccountID, feeRecipientAccountID, tokenID, fee, minerAccount.nonce) ring.sign(FQ(int(minerAccount.secretKey)), FQ(int(orderA.dualAuthSecretKey)), FQ(int(orderB.dualAuthSecretKey))) return ring
def test_make_proof(self): num_blocks = 32 wrapper = Contingent(NATIVE_LIB_PATH, num_blocks) # Generate proving and verification keys result = wrapper.genkeys(PK_PATH, VK_PATH) self.assertTrue(result == 0) print('Keygen done!') plaintext = [int(FQ.random()) for n in range(0, num_blocks)] plaintext_root = merkle_root(plaintext) key = int(FQ.random()) key_bytes = key.to_bytes(32, 'little') sha256 = hashlib.sha256() sha256.update(key_bytes) key_hash = sha256.digest() ciphertext = mimc_encrypt(plaintext, key) print('Number of blocks = {}'.format(num_blocks)) print('Plaintext = {}'.format(plaintext)) print('Plaintext Root = {}'.format(plaintext_root)) print('Key = {}'.format(key)) print('Key Hex = {}'.format(key_bytes.hex())) print('Key Hash = {}'.format(key_hash.hex())) print('Ciphertext = {}'.format(ciphertext)) # Generate proof proof = wrapper.prove(PK_PATH, key_hash, ciphertext, plaintext_root, key, plaintext) with open(PROOF_PATH, 'w') as f: f.write(proof) print('Prove done!') # Verify proof self.assertTrue( wrapper.verify(VK_PATH, proof, key_hash, ciphertext, plaintext_root)) print('Verify done!')
def test_mult_all_random(self): rp = self._point_a() x = FQ.random(JUBJUB_L) all_points = [rp, rp.as_proj(), rp.as_etec(), rp.as_mont()] expected = rp * x for p in all_points: q = p.mult(x) r = q.as_point() self.assertEqual(r.x, expected.x) self.assertEqual(r.y, expected.y)
def test_ifft(): p = 337 domain = [FQ(i) for i in [1, 85, 148, 111, 336, 252, 189, 226]] poly = [3, 1, 4, 1, 5, 9, 2, 6] result = [] p_x = fft(p, domain, poly) result = ifft(p, domain, p_x) assert result == poly
def test_mul_poly(): # (x + 1) **2 a = [1, 1, 0, 0, 0, 0, 0, 0] b = [1, 1, 0, 0, 0, 0, 0, 0] domain = [FQ(i) for i in [1, 85, 148, 111, 336, 252, 189, 226]] p = 337 a_fft = fft(p, domain, a) b_fft = fft(p, domain, b) c = [a * b for a, b in zip(a_fft, b_fft)] res = ifft(p, domain, c) assert res == [1, 2, 1, 0, 0, 0, 0, 0]
def test_degree_reduction(): a = [1, 0, 0, 0, 0, 0, 0, 1] b = [0, 1, 0, 0, 0, 0, 0, 0] domain = [FQ(i) for i in [1, 85, 148, 111, 336, 252, 189, 226]] p = 337 a_fft = fft(p, domain, a) b_fft = fft(p, domain, b) c = [a * b for a, b in zip(a_fft, b_fft)] res = ifft(p, domain, c) assert res == [1, 1, 0, 0, 0, 0, 0, 0]
def test_fft(): p = 337 _domain = [1, 85, 148, 111, 336, 252, 189, 226] domain = [FQ(i) for i in _domain] poly = [3, 1, 4, 1, 5, 9, 2, 6] result = [] p_x = fft(p, domain, poly) for x in _domain: result.append(polynomial_eval_prime(poly, x, p, 1, 0)) assert p_x == result
def test_12_nonsquares(self): """ If (A+2)*(A-2) is a square (e.g. if `ad` is a square) then there are two more points with v=0. These points have order 2 """ try: x = (MONT_A + 2) * (MONT_A - 2) FQ(int(x)).sqrt() self.assertTrue(False) except SquareRootError: pass """ If (A-2)/B is a square (e.g. if `d` is a square) then there are two points with `u=-1`. These points have order 4. These points correspond to two points of order 4 at infinity of the desingularization of E_{E,a,d} """ try: x = int((MONT_A - 2) / MONT_B) FQ(x).sqrt() self.assertTrue(False) except SquareRootError: pass
def _create_order(self, base_token, quote_token, buy, price, volume): if buy: tokenS = self.market_info_map[quote_token] tokenB = self.market_info_map[base_token] amountS = str(int(10 ** tokenS['decimals'] * price * volume)) amountB = str(int(10 ** tokenB['decimals'] * volume)) else: tokenS = self.market_info_map[base_token] tokenB = self.market_info_map[quote_token] amountS = str(int(10 ** tokenS['decimals'] * volume)) amountB = str(int(10 ** tokenB['decimals'] * price * volume)) tokenSId = tokenS['tokenId'] tokenBId = tokenB['tokenId'] orderId = self.orderId[tokenSId] assert orderId < self.MAX_ORDER_ID self.orderId[tokenSId] += 1 # make valid time ahead 1 hour validSince = int(time()) - self.time_offset - 3600 # order base order = { "exchangeId" : self.exchangeId, "orderId" : orderId, "accountId" : self.accountId, "tokenSId" : tokenSId, "tokenBId" : tokenBId, "amountS" : amountS, "amountB" : amountB, "allOrNone" : "false", "validSince" : validSince, "validUntil" : validSince + 60 * 24 * 60 * 60, "maxFeeBips" : 50, "label" : 211, "buy" : "true" if buy else "false", "clientOrderId" : "SampleOrder" + str(int(time()*1000)) } order_message = self._serialize_order(order) msgHash = poseidon(order_message, self.order_sign_param) signedMessage = PoseidonEdDSA.sign(msgHash, FQ(int(self.private_key))) # update signaure order.update({ "hash" : str(msgHash), "signatureRx" : str(signedMessage.sig.R.x), "signatureRy" : str(signedMessage.sig.R.y), "signatureS" : str(signedMessage.sig.s) }) return order
def message(self, nonce): """ Return an array of bits representing the on-chain transaction details that will be included in the signature. This is 104 bits long: +-----------+---------+---------+---------+ | from_idx | to_idx | amount | nonce | +-----------+---------+---------+---------+ | 24 bits | 24 bits | 32 bits | 24 bits | +-----------+---------+---------+---------+ Each integer is encoded in little-endian form, with the least significant bit first. """ assert self.from_idx < (1<<TREE_SIZE) assert self.to_idx < (1<<TREE_SIZE) assert self.amount < (1<<AMOUNT_BITS) assert nonce < (1<<TREE_SIZE) msg_parts = [FQ(self.from_idx, 1<<TREE_SIZE), FQ(self.to_idx, 1<<TREE_SIZE), FQ(self.amount, 1<<AMOUNT_BITS), FQ(nonce, 1<<TREE_SIZE)] return eddsa_tobits(*msg_parts)