mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
example
This commit is contained in:
parent
825e982f3b
commit
8b26474df1
@ -23,10 +23,11 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
|
|
||||||
mapping(bytes32 => bool) public nullifierHashes;
|
mapping(bytes32 => bool) public nullifierHashes;
|
||||||
bytes32 public currentRoot;
|
bytes32 public currentRoot;
|
||||||
|
uint public currentCommitmentIndex;
|
||||||
IVerifier public verifier;
|
IVerifier public verifier;
|
||||||
|
|
||||||
// todo: event Transaction();
|
// todo: event Transaction();
|
||||||
event NewCommitment(bytes32 commitment);
|
event NewCommitment(bytes32 commitment, uint index);
|
||||||
event NewNullifier(bytes32 nullifier);
|
event NewNullifier(bytes32 nullifier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,8 +83,8 @@ contract TornadoPool is ReentrancyGuard {
|
|||||||
transfer(_relayer, _fee);
|
transfer(_relayer, _fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit NewCommitment(_outputCommitments[0]);
|
emit NewCommitment(_outputCommitments[0], currentDepositIndex++);
|
||||||
emit NewCommitment(_outputCommitments[1]);
|
emit NewCommitment(_outputCommitments[1], currentDepositIndex++);
|
||||||
emit NewNullifier(_inputNullifiers[0]);
|
emit NewNullifier(_inputNullifiers[0]);
|
||||||
emit NewNullifier(_inputNullifiers[1]);
|
emit NewNullifier(_inputNullifiers[1]);
|
||||||
// emit Transaction();
|
// emit Transaction();
|
||||||
|
142
src/index.js
Normal file
142
src/index.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
const MERKLE_TREE_HEIGHT = 10;
|
||||||
|
|
||||||
|
function rbigint(bytes = 31) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomBoolArray(length) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomArray(length) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function hash(dataArray) {
|
||||||
|
for(let item of dataArray) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function merklePathIndicesToBigint(indexArray) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromPrivkey(privkey) {
|
||||||
|
return {
|
||||||
|
privkey,
|
||||||
|
pubkey: privkey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomKeypair() {
|
||||||
|
return fromPrivkey(rbigint())
|
||||||
|
}
|
||||||
|
|
||||||
|
function createZeroUtxo(keypair) {
|
||||||
|
return createUtxo(0, rbigint(), keypair.pubkey, keypair.privkey, randomBoolArray(MERKLE_TREE_HEIGHT), randomArray(MERKLE_TREE_HEIGHT))
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOutput(amount, pubkey) {
|
||||||
|
if (!pubkey) {
|
||||||
|
throw new Error('no pubkey')
|
||||||
|
}
|
||||||
|
return createUtxo(amount, rbigint(), pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInput(amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements) {
|
||||||
|
return createUtxo(amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// unsafe function without sanity checks
|
||||||
|
function createUtxo(amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements) {
|
||||||
|
let utxo = { amount, blinding, pubkey, privkey, merklePathIndices, merklePathElements }
|
||||||
|
utxo.commitment = hash([amount, blinding, pubkey])
|
||||||
|
if (privkey) {
|
||||||
|
utxo.nullifier = hash([commitment, merklePathIndicesToBigint(merklePathIndices), privkey])
|
||||||
|
}
|
||||||
|
return utxo
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDeposit(amount, pubkey) {
|
||||||
|
const keypair = randomKeypair()
|
||||||
|
const tx = {
|
||||||
|
inputs: [createZeroUtxo(keypair), createZeroUtxo(keypair)],
|
||||||
|
outputs: [createOutput(amount, pubkey), createZeroUtxo()], // todo shuffle
|
||||||
|
}
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildMerkleTree() {
|
||||||
|
console.log('Getting contract state...')
|
||||||
|
const events = await contract.getPastEvents('NewCommitment', { fromBlock: 0, toBlock: 'latest' })
|
||||||
|
const leaves = events
|
||||||
|
.sort((a, b) => a.returnValues.index - b.returnValues.index) // todo sort by event date
|
||||||
|
.map(e => e.returnValues.commitment)
|
||||||
|
return new merkleTree(MERKLE_TREE_HEIGHT, leaves)
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertTransaction(tree, tx) {
|
||||||
|
let path = await tree.insertPair(tx.outputs[0].commitment, tx.outputs[1].commitment)
|
||||||
|
|
||||||
|
tx.outputs[0].merklePath = [...path, 0]
|
||||||
|
tx.outputs[1].merklePath = [...path, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const amount = 1e6;
|
||||||
|
|
||||||
|
const tree = await buildMerkleTree()
|
||||||
|
const oldRoot = tree.root;
|
||||||
|
const keypair = randomKeypair()
|
||||||
|
const tx = createDeposit(amount, keypair.pubkey)
|
||||||
|
insertTransaction(tree, tx)
|
||||||
|
|
||||||
|
let input = {
|
||||||
|
root: oldRoot,
|
||||||
|
newRoot: tree.root,
|
||||||
|
inputNullifier: [tx.inputs[0].nullifier, tx.inputs[1].nullifier],
|
||||||
|
outputCommitment: [tx.outputs[0].commitment, tx.outputs[1].commitment],
|
||||||
|
extAmount: amount,
|
||||||
|
fee: 0,
|
||||||
|
recipient: 0,
|
||||||
|
relayer: 0,
|
||||||
|
|
||||||
|
// private inputs
|
||||||
|
privateKey: tx.inputs[0].privkey,
|
||||||
|
|
||||||
|
// data for 2 transaction inputs
|
||||||
|
inAmount: [tx.inputs[0].amount, tx.inputs[1].amount],
|
||||||
|
inBlinding: [tx.inputs[0].blinding, tx.inputs[1].blinding],
|
||||||
|
inPathIndices: [merklePathIndicesToBigint(tx.inputs[0].merklePathIndices), merklePathIndicesToBigint(tx.inputs[1].merklePathIndices)],
|
||||||
|
inPathElements: [tx.inputs[0].merklePathElements, tx.inputs[1].merklePathElements],
|
||||||
|
|
||||||
|
// data for 2 transaction outputs
|
||||||
|
outAmount: [tx.outputs[0].amount, tx.outputs[1].amount],
|
||||||
|
outBlinding: [tx.outputs[0].blinding, tx.outputs[1].blinding],
|
||||||
|
outPathIndices: merklePathIndicesToBigint(tx.outputs[0].merklePathIndices.slice(0, -1)),
|
||||||
|
outPathElements: tx.outputs[0].merklePathElements.slice(0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Generating SNARK proof...')
|
||||||
|
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||||
|
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
toHex(input.root),
|
||||||
|
toHex(input.newRoot),
|
||||||
|
[toHex(input.inputs[0].nullifier), toHex(input.inputs[1].nullifier)],
|
||||||
|
[toHex(input.inputs[0].commitment), toHex(input.inputs[1].commitment)],
|
||||||
|
toHex(input.amount),
|
||||||
|
toHex(input.fee),
|
||||||
|
toHex(input.recipient, 20),
|
||||||
|
toHex(input.relayer, 20),
|
||||||
|
]
|
||||||
|
|
||||||
|
console.log('Sending withdrawal transaction...')
|
||||||
|
const receipt = await contract.methods.transaction(proof, ...args).send({ valued: amount, from: web3.eth.defaultAccount, gas: 1e6 })
|
||||||
|
console.log(`https://kovan.etherscan.io/tx/${receipt.transactionHash}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
22
src/index.ts
22
src/index.ts
@ -5,6 +5,28 @@ class UTXO {
|
|||||||
amount: BigNumber;
|
amount: BigNumber;
|
||||||
blinding: BigNumber;
|
blinding: BigNumber;
|
||||||
pubkey: BigNumber;
|
pubkey: BigNumber;
|
||||||
|
privkey: BigNumber;
|
||||||
|
commitment: BigNumber;
|
||||||
|
treeIndex: Boolean[];
|
||||||
|
nullifier: BigNumber;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
amount,
|
||||||
|
blinding,
|
||||||
|
pubkey,
|
||||||
|
privkey,
|
||||||
|
commitment,
|
||||||
|
treeIndex,
|
||||||
|
nullifier,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transaction {
|
||||||
|
inputs: UTXO[];
|
||||||
|
outputs: UTXO[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user