mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
first deposit
This commit is contained in:
parent
8b26474df1
commit
709bde4705
@ -2,8 +2,8 @@ root = true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
@ -29,10 +29,11 @@ template DualMux() {
|
|||||||
// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path
|
// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path
|
||||||
template MerkleTree(levels) {
|
template MerkleTree(levels) {
|
||||||
signal input leaf;
|
signal input leaf;
|
||||||
signal input root;
|
|
||||||
signal input pathElements[levels];
|
signal input pathElements[levels];
|
||||||
signal input pathIndices;
|
signal input pathIndices;
|
||||||
|
|
||||||
|
signal output root;
|
||||||
|
|
||||||
component selectors[levels];
|
component selectors[levels];
|
||||||
component hashers[levels];
|
component hashers[levels];
|
||||||
|
|
||||||
@ -50,5 +51,5 @@ template MerkleTree(levels) {
|
|||||||
hashers[i].right <== selectors[i].out[1];
|
hashers[i].right <== selectors[i].out[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
root === hashers[levels - 1].hash;
|
root <== hashers[levels - 1].hash;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
// data for 2 transaction outputs
|
// data for 2 transaction outputs
|
||||||
signal private input outAmount[2];
|
signal private input outAmount[2];
|
||||||
signal private input outBlinding[2];
|
signal private input outBlinding[2];
|
||||||
|
signal private input outPubkey[2];
|
||||||
signal private input outPathIndices;
|
signal private input outPathIndices;
|
||||||
signal private input outPathElements[levels - 1];
|
signal private input outPathElements[levels - 1];
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
outUtxoHasher[tx] = TransactionHasher();
|
outUtxoHasher[tx] = TransactionHasher();
|
||||||
outUtxoHasher[tx].amount <== outAmount[tx];
|
outUtxoHasher[tx].amount <== outAmount[tx];
|
||||||
outUtxoHasher[tx].blinding <== outBlinding[tx];
|
outUtxoHasher[tx].blinding <== outBlinding[tx];
|
||||||
outUtxoHasher[tx].publicKey <== keypair.publicKey;
|
outUtxoHasher[tx].publicKey <== outPubkey[tx];
|
||||||
outUtxoHasher[tx].commitment === outputCommitment[tx];
|
outUtxoHasher[tx].commitment === outputCommitment[tx];
|
||||||
|
|
||||||
// Check that amount fits into 248 bits to prevent overflow
|
// Check that amount fits into 248 bits to prevent overflow
|
||||||
@ -120,4 +121,4 @@ template Transaction(levels, zeroLeaf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component main = Transaction(5, 3193090221241211970002919215846211184824251841300455796635909287157453409439);
|
component main = Transaction(5, 16923532097304556005972200564242292693309333953544141029519619077135960040221);
|
||||||
|
@ -7,8 +7,11 @@ template Keypair() {
|
|||||||
signal input privateKey;
|
signal input privateKey;
|
||||||
signal output publicKey;
|
signal output publicKey;
|
||||||
|
|
||||||
publicKey <== privateKey;
|
component hasher = MiMCSponge(1, 1);
|
||||||
// todo
|
hasher.ins[0] <== privateKey;
|
||||||
|
hasher.k <== 0;
|
||||||
|
|
||||||
|
publicKey <== hasher.outs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
template TransactionHasher() {
|
template TransactionHasher() {
|
||||||
|
@ -34,8 +34,9 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
@dev The constructor
|
@dev The constructor
|
||||||
@param _verifier the address of SNARK verifier for this contract
|
@param _verifier the address of SNARK verifier for this contract
|
||||||
*/
|
*/
|
||||||
constructor(IVerifier _verifier) public {
|
constructor(IVerifier _verifier, bytes32 _currentRoot) public {
|
||||||
verifier = _verifier;
|
verifier = _verifier;
|
||||||
|
currentRoot = _currentRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transaction(
|
function transaction(
|
||||||
@ -83,8 +84,8 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
transfer(_relayer, _fee);
|
transfer(_relayer, _fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit NewCommitment(_outputCommitments[0], currentDepositIndex++);
|
emit NewCommitment(_outputCommitments[0], currentCommitmentIndex++);
|
||||||
emit NewCommitment(_outputCommitments[1], currentDepositIndex++);
|
emit NewCommitment(_outputCommitments[1], currentCommitmentIndex++);
|
||||||
emit NewNullifier(_inputNullifiers[0]);
|
emit NewNullifier(_inputNullifiers[0]);
|
||||||
emit NewNullifier(_inputNullifiers[1]);
|
emit NewNullifier(_inputNullifiers[1]);
|
||||||
// emit Transaction();
|
// emit Transaction();
|
||||||
|
201
lib/merkleTree.js
Normal file
201
lib/merkleTree.js
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
const jsStorage = require('./storage')
|
||||||
|
const hasherImpl = require('./mimc')
|
||||||
|
|
||||||
|
class MerkleTree {
|
||||||
|
|
||||||
|
constructor(n_levels, defaultElements, prefix, storage, hasher) {
|
||||||
|
this.prefix = prefix
|
||||||
|
this.storage = storage || new jsStorage()
|
||||||
|
this.hasher = hasher || new hasherImpl()
|
||||||
|
this.n_levels = n_levels
|
||||||
|
this.zero_values = []
|
||||||
|
this.totalElements = 0
|
||||||
|
|
||||||
|
let current_zero_value = '21663839004416932945382355908790599225266501822907911457504978515578255421292'
|
||||||
|
this.zero_values.push(current_zero_value)
|
||||||
|
for (let i = 0; i < n_levels; i++) {
|
||||||
|
current_zero_value = this.hasher.hash(i, current_zero_value, current_zero_value)
|
||||||
|
this.zero_values.push(
|
||||||
|
current_zero_value.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (defaultElements) {
|
||||||
|
let level = 0
|
||||||
|
this.totalElements = defaultElements.length
|
||||||
|
defaultElements.forEach((element, i) => {
|
||||||
|
this.storage.put(MerkleTree.index_to_key(prefix, level, i), element)
|
||||||
|
})
|
||||||
|
level++
|
||||||
|
let numberOfElementsInLevel = Math.ceil(defaultElements.length / 2)
|
||||||
|
for (level; level <= this.n_levels; level++) {
|
||||||
|
for(let i = 0; i < numberOfElementsInLevel; i++) {
|
||||||
|
const leftKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i)
|
||||||
|
const rightKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i + 1)
|
||||||
|
|
||||||
|
const left = this.storage.get(leftKey)
|
||||||
|
const right = this.storage.get_or_element(rightKey, this.zero_values[level - 1])
|
||||||
|
|
||||||
|
const subRoot = this.hasher.hash(null, left, right)
|
||||||
|
this.storage.put(MerkleTree.index_to_key(prefix, level, i), subRoot)
|
||||||
|
}
|
||||||
|
numberOfElementsInLevel = Math.ceil(numberOfElementsInLevel / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static index_to_key(prefix, level, index) {
|
||||||
|
const key = `${prefix}_tree_${level}_${index}`
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
async root() {
|
||||||
|
let root = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, this.n_levels, 0),
|
||||||
|
this.zero_values[this.n_levels],
|
||||||
|
)
|
||||||
|
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
async path(index) {
|
||||||
|
class PathTraverser {
|
||||||
|
constructor(prefix, storage, zero_values) {
|
||||||
|
this.prefix = prefix
|
||||||
|
this.storage = storage
|
||||||
|
this.zero_values = zero_values
|
||||||
|
this.path_elements = []
|
||||||
|
this.path_index = []
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle_index(level, element_index, sibling_index) {
|
||||||
|
const sibling = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, level, sibling_index),
|
||||||
|
this.zero_values[level],
|
||||||
|
)
|
||||||
|
this.path_elements.push(sibling)
|
||||||
|
this.path_index.push(element_index % 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = Number(index)
|
||||||
|
let traverser = new PathTraverser(this.prefix, this.storage, this.zero_values)
|
||||||
|
const root = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, this.n_levels, 0),
|
||||||
|
this.zero_values[this.n_levels],
|
||||||
|
)
|
||||||
|
|
||||||
|
const element = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, 0, index),
|
||||||
|
this.zero_values[0],
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.traverse(index, traverser)
|
||||||
|
return {
|
||||||
|
root,
|
||||||
|
path_elements: traverser.path_elements,
|
||||||
|
path_index: traverser.path_index,
|
||||||
|
element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(index, element, insert = false) {
|
||||||
|
if (!insert && index >= this.totalElements) {
|
||||||
|
throw Error('Use insert method for new elements.')
|
||||||
|
} else if(insert && index < this.totalElements) {
|
||||||
|
throw Error('Use update method for existing elements.')
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
class UpdateTraverser {
|
||||||
|
constructor(prefix, storage, hasher, element, zero_values) {
|
||||||
|
this.prefix = prefix
|
||||||
|
this.current_element = element
|
||||||
|
this.zero_values = zero_values
|
||||||
|
this.storage = storage
|
||||||
|
this.hasher = hasher
|
||||||
|
this.key_values_to_put = []
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle_index(level, element_index, sibling_index) {
|
||||||
|
if (level == 0) {
|
||||||
|
this.original_element = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, level, element_index),
|
||||||
|
this.zero_values[level],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const sibling = await this.storage.get_or_element(
|
||||||
|
MerkleTree.index_to_key(this.prefix, level, sibling_index),
|
||||||
|
this.zero_values[level],
|
||||||
|
)
|
||||||
|
let left, right
|
||||||
|
if (element_index % 2 == 0) {
|
||||||
|
left = this.current_element
|
||||||
|
right = sibling
|
||||||
|
} else {
|
||||||
|
left = sibling
|
||||||
|
right = this.current_element
|
||||||
|
}
|
||||||
|
|
||||||
|
this.key_values_to_put.push({
|
||||||
|
key: MerkleTree.index_to_key(this.prefix, level, element_index),
|
||||||
|
value: this.current_element,
|
||||||
|
})
|
||||||
|
this.current_element = this.hasher.hash(level, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let traverser = new UpdateTraverser(
|
||||||
|
this.prefix,
|
||||||
|
this.storage,
|
||||||
|
this.hasher,
|
||||||
|
element,
|
||||||
|
this.zero_values
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.traverse(index, traverser)
|
||||||
|
traverser.key_values_to_put.push({
|
||||||
|
key: MerkleTree.index_to_key(this.prefix, this.n_levels, 0),
|
||||||
|
value: traverser.current_element,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.storage.put_batch(traverser.key_values_to_put)
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert(element) {
|
||||||
|
const index = this.totalElements
|
||||||
|
await this.update(index, element, true)
|
||||||
|
this.totalElements++
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo it can be mode optimal
|
||||||
|
async insertPair(first, second) {
|
||||||
|
await insert(first)
|
||||||
|
await insert(second)
|
||||||
|
}
|
||||||
|
|
||||||
|
async traverse(index, handler) {
|
||||||
|
let current_index = index
|
||||||
|
for (let i = 0; i < this.n_levels; i++) {
|
||||||
|
let sibling_index = current_index
|
||||||
|
if (current_index % 2 == 0) {
|
||||||
|
sibling_index += 1
|
||||||
|
} else {
|
||||||
|
sibling_index -= 1
|
||||||
|
}
|
||||||
|
await handler.handle_index(i, current_index, sibling_index)
|
||||||
|
current_index = Math.floor(current_index / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexByElement(element) {
|
||||||
|
for(let i = this.totalElements - 1; i >= 0; i--) {
|
||||||
|
const elementFromTree = this.storage.get(MerkleTree.index_to_key(this.prefix, 0, i))
|
||||||
|
if (elementFromTree === element) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MerkleTree
|
17
lib/mimc.js
Normal file
17
lib/mimc.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const circomlib = require('circomlib')
|
||||||
|
const mimcsponge = circomlib.mimcsponge
|
||||||
|
const snarkjs = require('snarkjs')
|
||||||
|
|
||||||
|
const bigInt = snarkjs.bigInt
|
||||||
|
|
||||||
|
class MimcSpongeHasher {
|
||||||
|
hash(level, left, right) {
|
||||||
|
return mimcsponge.multiHash([bigInt(left), bigInt(right)]).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
hashArray(items) {
|
||||||
|
return mimcsponge.multiHash(items.map(item => bigInt(item))).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MimcSpongeHasher
|
39
lib/storage.js
Normal file
39
lib/storage.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class JsStorage {
|
||||||
|
constructor() {
|
||||||
|
this.db = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this.db[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
get_or_element(key, defaultElement) {
|
||||||
|
const element = this.db[key]
|
||||||
|
if (element === undefined) {
|
||||||
|
return defaultElement
|
||||||
|
} else {
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put(key, value) {
|
||||||
|
if (key === undefined || value === undefined) {
|
||||||
|
throw Error('key or value is undefined')
|
||||||
|
}
|
||||||
|
this.db[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
del(key) {
|
||||||
|
delete this.db[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
put_batch(key_values) {
|
||||||
|
key_values.forEach(element => {
|
||||||
|
this.db[element.key] = element.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = JsStorage
|
@ -1,11 +1,19 @@
|
|||||||
/* global artifacts */
|
/* global artifacts */
|
||||||
const Verifier = artifacts.require('Verifier')
|
const Verifier = artifacts.require('Verifier')
|
||||||
const TornadoPool = artifacts.require('TornadoPool')
|
const TornadoPool = artifacts.require('TornadoPool')
|
||||||
|
const MERKLE_TREE_HEIGHT = 5
|
||||||
|
const MerkleTree = require('../lib/merkleTree')
|
||||||
|
const { bigInt } = require('snarkjs')
|
||||||
|
const toHex = (number, length = 32) => '0x' + (number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16)).padStart(length * 2, '0')
|
||||||
|
|
||||||
|
|
||||||
module.exports = function(deployer, network, accounts) {
|
module.exports = function(deployer, network, accounts) {
|
||||||
return deployer.then(async () => {
|
return deployer.then(async () => {
|
||||||
|
const tree = new MerkleTree(MERKLE_TREE_HEIGHT)
|
||||||
|
const root = await tree.root()
|
||||||
const verifier = await Verifier.deployed()
|
const verifier = await Verifier.deployed()
|
||||||
const tornado = await deployer.deploy(TornadoPool, verifier.address)
|
|
||||||
|
const tornado = await deployer.deploy(TornadoPool, verifier.address, toHex(root))
|
||||||
console.log('TornadoPool\'s address ', tornado.address)
|
console.log('TornadoPool\'s address ', tornado.address)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
108
src/index.js
108
src/index.js
@ -1,31 +1,39 @@
|
|||||||
const MERKLE_TREE_HEIGHT = 10;
|
const MerkleTree = require('../lib/merkleTree')
|
||||||
|
const fs = require('fs')
|
||||||
|
const { bigInt, stringifyBigInts } = require('snarkjs')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const Hasher = require('../lib/mimc')
|
||||||
|
const Web3 = require('web3')
|
||||||
|
const buildGroth16 = require('websnark/src/groth16')
|
||||||
|
const websnarkUtils = require('websnark/src/utils')
|
||||||
|
|
||||||
function rbigint(bytes = 31) {
|
let contract, web3, circuit, proving_key, groth16
|
||||||
|
const hasher = new Hasher()
|
||||||
|
|
||||||
}
|
// console.log(hasher.hashArray(['21663839004416932945382355908790599225266501822907911457504978515578255421292', '21663839004416932945382355908790599225266501822907911457504978515578255421292']))
|
||||||
|
|
||||||
function randomBoolArray(length) {
|
const MERKLE_TREE_HEIGHT = 5
|
||||||
|
const RPC_URL = 'http://localhost:8545'
|
||||||
|
|
||||||
}
|
/** Generate random number of specified byte length */
|
||||||
|
const rbigint = (nbytes = 31) => bigInt.leBuff2int(crypto.randomBytes(nbytes))
|
||||||
|
|
||||||
function randomArray(length) {
|
/** BigNumber to hex string of specified length */
|
||||||
|
const toHex = (number, length = 32) => '0x' + (number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16)).padStart(length * 2, '0')
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function hash(dataArray) {
|
|
||||||
for(let item of dataArray) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function merklePathIndicesToBigint(indexArray) {
|
function merklePathIndicesToBigint(indexArray) {
|
||||||
|
let result = 0
|
||||||
|
for(let item of indexArray) {
|
||||||
|
result = (result << 1) + item
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPrivkey(privkey) {
|
function fromPrivkey(privkey) {
|
||||||
return {
|
return {
|
||||||
privkey,
|
privkey,
|
||||||
pubkey: privkey,
|
pubkey: hasher.hashArray([privkey]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +42,14 @@ function randomKeypair() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createZeroUtxo(keypair) {
|
function createZeroUtxo(keypair) {
|
||||||
return createUtxo(0, rbigint(), keypair.pubkey, keypair.privkey, randomBoolArray(MERKLE_TREE_HEIGHT), randomArray(MERKLE_TREE_HEIGHT))
|
return createUtxo(
|
||||||
|
0,
|
||||||
|
rbigint(),
|
||||||
|
keypair.pubkey,
|
||||||
|
keypair.privkey,
|
||||||
|
Array(MERKLE_TREE_HEIGHT).fill(0),
|
||||||
|
Array(MERKLE_TREE_HEIGHT).fill(0)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOutput(amount, pubkey) {
|
function createOutput(amount, pubkey) {
|
||||||
@ -51,9 +66,9 @@ function createInput(amount, blinding, pubkey, privkey, merklePathIndices, merkl
|
|||||||
/// unsafe function without sanity checks
|
/// unsafe function without sanity checks
|
||||||
function createUtxo(amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements) {
|
function createUtxo(amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements) {
|
||||||
let utxo = { amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements }
|
let utxo = { amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements }
|
||||||
utxo.commitment = hash([amount, blinding, pubkey])
|
utxo.commitment = hasher.hashArray([amount, blinding, pubkey])
|
||||||
if (privkey) {
|
if (privkey) {
|
||||||
utxo.nullifier = hash([commitment, merklePathIndicesToBigint(merklePathIndices), privkey])
|
utxo.nullifier = hasher.hashArray([utxo.commitment, merklePathIndicesToBigint(merklePathIndices), privkey])
|
||||||
}
|
}
|
||||||
return utxo
|
return utxo
|
||||||
}
|
}
|
||||||
@ -62,7 +77,7 @@ function createDeposit(amount, pubkey) {
|
|||||||
const keypair = randomKeypair()
|
const keypair = randomKeypair()
|
||||||
const tx = {
|
const tx = {
|
||||||
inputs: [createZeroUtxo(keypair), createZeroUtxo(keypair)],
|
inputs: [createZeroUtxo(keypair), createZeroUtxo(keypair)],
|
||||||
outputs: [createOutput(amount, pubkey), createZeroUtxo()], // todo shuffle
|
outputs: [createOutput(amount, pubkey), createZeroUtxo(keypair)], // todo shuffle
|
||||||
}
|
}
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
@ -73,51 +88,64 @@ async function buildMerkleTree() {
|
|||||||
const leaves = events
|
const leaves = events
|
||||||
.sort((a, b) => a.returnValues.index - b.returnValues.index) // todo sort by event date
|
.sort((a, b) => a.returnValues.index - b.returnValues.index) // todo sort by event date
|
||||||
.map(e => e.returnValues.commitment)
|
.map(e => e.returnValues.commitment)
|
||||||
return new merkleTree(MERKLE_TREE_HEIGHT, leaves)
|
return new MerkleTree(MERKLE_TREE_HEIGHT, leaves)
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertTransaction(tree, tx) {
|
async function insertOutput(tree, output) {
|
||||||
let path = await tree.insertPair(tx.outputs[0].commitment, tx.outputs[1].commitment)
|
await tree.insert(output.commitment)
|
||||||
|
let { path_elements, path_index } = await tree.path(tree.totalElements - 1)
|
||||||
tx.outputs[0].merklePath = [...path, 0]
|
output.merklePathIndices = path_index
|
||||||
tx.outputs[1].merklePath = [...path, 1]
|
output.merklePathElements = path_elements
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const amount = 1e6;
|
web3 = new Web3(new Web3.providers.HttpProvider(RPC_URL, { timeout: 5 * 60 * 1000 }), null, { transactionConfirmationBlocks: 1 })
|
||||||
|
circuit = require('../build/circuits/transaction.json')
|
||||||
|
proving_key = fs.readFileSync('../build/circuits/transaction_proving_key.bin').buffer
|
||||||
|
groth16 = await buildGroth16()
|
||||||
|
netId = await web3.eth.net.getId()
|
||||||
|
const contractData = require('../build/contracts/TornadoPool.json')
|
||||||
|
contract = new web3.eth.Contract(contractData.abi, contractData.networks[netId].address)
|
||||||
|
web3.eth.defaultAccount = (await web3.eth.getAccounts())[0]
|
||||||
|
|
||||||
|
const amount = 1e6;
|
||||||
const tree = await buildMerkleTree()
|
const tree = await buildMerkleTree()
|
||||||
const oldRoot = tree.root;
|
const oldRoot = await tree.root()
|
||||||
const keypair = randomKeypair()
|
const keypair = randomKeypair()
|
||||||
const tx = createDeposit(amount, keypair.pubkey)
|
const tx = createDeposit(amount, keypair.pubkey)
|
||||||
insertTransaction(tree, tx)
|
await insertOutput(tree, tx.outputs[0])
|
||||||
|
await insertOutput(tree, tx.outputs[1])
|
||||||
|
console.log('Note', tx.outputs[0])
|
||||||
|
|
||||||
let input = {
|
let input = {
|
||||||
root: oldRoot,
|
root: oldRoot,
|
||||||
newRoot: tree.root,
|
newRoot: await tree.root(),
|
||||||
inputNullifier: [tx.inputs[0].nullifier, tx.inputs[1].nullifier],
|
inputNullifier: [tx.inputs[0].nullifier, tx.inputs[1].nullifier],
|
||||||
outputCommitment: [tx.outputs[0].commitment, tx.outputs[1].commitment],
|
outputCommitment: [tx.outputs[0].commitment, tx.outputs[1].commitment],
|
||||||
extAmount: amount,
|
extAmount: amount,
|
||||||
fee: 0,
|
fee: 0,
|
||||||
recipient: 0,
|
recipient: 0,
|
||||||
relayer: 0,
|
relayer: 0,
|
||||||
|
|
||||||
// private inputs
|
// private inputs
|
||||||
privateKey: tx.inputs[0].privkey,
|
privateKey: tx.inputs[0].privkey,
|
||||||
|
|
||||||
// data for 2 transaction inputs
|
// data for 2 transaction inputs
|
||||||
inAmount: [tx.inputs[0].amount, tx.inputs[1].amount],
|
inAmount: [tx.inputs[0].amount, tx.inputs[1].amount],
|
||||||
inBlinding: [tx.inputs[0].blinding, tx.inputs[1].blinding],
|
inBlinding: [tx.inputs[0].blinding, tx.inputs[1].blinding],
|
||||||
inPathIndices: [merklePathIndicesToBigint(tx.inputs[0].merklePathIndices), merklePathIndicesToBigint(tx.inputs[1].merklePathIndices)],
|
inPathIndices: [merklePathIndicesToBigint(tx.inputs[0].merklePathIndices), merklePathIndicesToBigint(tx.inputs[1].merklePathIndices)],
|
||||||
inPathElements: [tx.inputs[0].merklePathElements, tx.inputs[1].merklePathElements],
|
inPathElements: [tx.inputs[0].merklePathElements, tx.inputs[1].merklePathElements],
|
||||||
|
|
||||||
// data for 2 transaction outputs
|
// data for 2 transaction outputs
|
||||||
outAmount: [tx.outputs[0].amount, tx.outputs[1].amount],
|
outAmount: [tx.outputs[0].amount, tx.outputs[1].amount],
|
||||||
outBlinding: [tx.outputs[0].blinding, tx.outputs[1].blinding],
|
outBlinding: [tx.outputs[0].blinding, tx.outputs[1].blinding],
|
||||||
outPathIndices: merklePathIndicesToBigint(tx.outputs[0].merklePathIndices.slice(0, -1)),
|
outPubkey: [tx.outputs[0].pubkey, tx.outputs[1].pubkey],
|
||||||
outPathElements: tx.outputs[0].merklePathElements.slice(0, -1)
|
outPathIndices: merklePathIndicesToBigint(tx.outputs[0].merklePathIndices.slice(1)),
|
||||||
|
outPathElements: tx.outputs[0].merklePathElements.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('input', JSON.stringify(stringifyBigInts(input)))
|
||||||
|
|
||||||
console.log('Generating SNARK proof...')
|
console.log('Generating SNARK proof...')
|
||||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||||
@ -125,17 +153,17 @@ async function main() {
|
|||||||
const args = [
|
const args = [
|
||||||
toHex(input.root),
|
toHex(input.root),
|
||||||
toHex(input.newRoot),
|
toHex(input.newRoot),
|
||||||
[toHex(input.inputs[0].nullifier), toHex(input.inputs[1].nullifier)],
|
[toHex(tx.inputs[0].nullifier), toHex(tx.inputs[1].nullifier)],
|
||||||
[toHex(input.inputs[0].commitment), toHex(input.inputs[1].commitment)],
|
[toHex(tx.outputs[0].commitment), toHex(tx.outputs[1].commitment)],
|
||||||
toHex(input.amount),
|
toHex(amount),
|
||||||
toHex(input.fee),
|
toHex(input.fee),
|
||||||
toHex(input.recipient, 20),
|
toHex(input.recipient, 20),
|
||||||
toHex(input.relayer, 20),
|
toHex(input.relayer, 20),
|
||||||
]
|
]
|
||||||
|
|
||||||
console.log('Sending withdrawal transaction...')
|
console.log('Sending deposit transaction...')
|
||||||
const receipt = await contract.methods.transaction(proof, ...args).send({ valued: amount, from: web3.eth.defaultAccount, gas: 1e6 })
|
const receipt = await contract.methods.transaction(proof, ...args).send({ value: amount, from: web3.eth.defaultAccount, gas: 1e6 })
|
||||||
console.log(`https://kovan.etherscan.io/tx/${receipt.transactionHash}`)
|
console.log(`Receipt ${receipt.transactionHash}`)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user