Support creating deposit notes offline
Co-authored-by: yoyoismee <yoyoismee@gmail.com>
This commit is contained in:
parent
378bab8fbe
commit
2854b7a12f
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ node_modules
|
||||
.env
|
||||
.idea
|
||||
backup-tornado-*
|
||||
backup-tornadoInvoice-*
|
||||
|
40
README.md
40
README.md
@ -8,6 +8,14 @@ Current cli version doesn't support [Anonymity Mining](https://tornado-cash.medi
|
||||
### How to install tornado cli
|
||||
Download and install [node.js](https://nodejs.org/en/download/).
|
||||
|
||||
You also need to install C++ build tools in order to do 'npm install', for more information please checkout https://github.com/nodejs/node-gyp#on-unix.
|
||||
|
||||
For Windows: https://stackoverflow.com/a/64224475
|
||||
|
||||
For MacOS: Install XCode Command Line Tools
|
||||
|
||||
For Linux: Install make & gcc, for ubuntu `$ sudo apt-get install -y build-essentials`
|
||||
|
||||
If you have git installed on your system, clone the master branch.
|
||||
|
||||
```bash
|
||||
@ -57,6 +65,38 @@ Transaction mined in block 17036120
|
||||
Done
|
||||
```
|
||||
|
||||
### (Optional) Creating Deposit Notes & Invoices offline
|
||||
One of the main features of tornado-cli is that it supports creating deposit notes & invoices inside the offline computing environment.
|
||||
|
||||
After the private-key like notes are backed up somewhere safe, you can copy the created deposit invoices and use them to create new deposit transaction on online environment.
|
||||
|
||||
To create deposit notes with `createNote` command.
|
||||
|
||||
```bash
|
||||
$ node cli.js createNote ETH 0.1 5
|
||||
Your note: tornado-eth-0.1-5-0x1d9771a7b9f8b6c03d33116208ce8db1aa559d33e65d22dd2ff78375fc6b635f930536d2432b4bde0178c72cfc79d6b27023c5d9de60985f186b34c18c00
|
||||
Your invoice for deposit: tornadoInvoice-eth-0.1-5-0x1b680c7dda0c2dd1b85f0fe126d49b16ed594b3cd6d5114db5f4593877a6b84f
|
||||
Backed up deposit note as ./backup-tornado-eth-0.1-5-0x1d9771a7.txt
|
||||
Backed up invoice as ./backup-tornadoInvoice-eth-0.1-5-0x1b680c7d.txt
|
||||
```
|
||||
|
||||
To create corresponding deposit transaction you only need invoice value, so that the deposit note could be stored without exposed anywhere.
|
||||
|
||||
```bash
|
||||
node cli.js depositInvoice tornadoInvoice-eth-0.1-5-0x1b680c7dda0c2dd1b85f0fe126d49b16ed594b3cd6d5114db5f4593877a6b84f --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --tor 9150
|
||||
Using tor network
|
||||
Your remote IP address is xx.xx.xx.xx from xx.
|
||||
Creating ETH 0.1 deposit for Goerli network.
|
||||
Using supplied invoice for deposit
|
||||
Tornado contract balance is xxx.x ETH
|
||||
Sender account balance is x.xxxxxxx ETH
|
||||
Submitting deposit transaction
|
||||
Submitting transaction to the remote node
|
||||
View transaction on block explorer https://goerli.etherscan.io/tx/0x6ded443caed8d6f2666841149532c64bee149a9a8e1070ed4c91a12dd1837747
|
||||
Tornado contract balance is xxx.x ETH
|
||||
Sender account balance is x.xxxxxxx ETH
|
||||
```
|
||||
|
||||
### List of public rpc & relayers for withdrawal
|
||||
|
||||
Infura API key fetched from https://rpc.info (Same one with Metamask)
|
||||
|
103
cli.js
103
cli.js
@ -180,27 +180,68 @@ async function backupNote({ currency, amount, netId, note, noteString }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function backupInvoice({ currency, amount, netId, commitmentNote, invoiceString }) {
|
||||
try {
|
||||
await fs.writeFileSync(`./backup-tornadoInvoice-${currency}-${amount}-${netId}-${commitmentNote.slice(0, 10)}.txt`, invoiceString, 'utf8');
|
||||
console.log("Backed up invoice as", `./backup-tornadoInvoice-${currency}-${amount}-${netId}-${commitmentNote.slice(0, 10)}.txt`)
|
||||
} catch (e) {
|
||||
throw new Error('Writing backup invoice failed:', e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a deposit
|
||||
* create a deposit invoice.
|
||||
* @param currency Сurrency
|
||||
* @param amount Deposit amount
|
||||
*/
|
||||
async function deposit({ currency, amount }) {
|
||||
assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit');
|
||||
async function createInvoice({ currency, amount, chainId }) {
|
||||
const deposit = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31)
|
||||
});
|
||||
const note = toHex(deposit.preimage, 62);
|
||||
const noteString = `tornado-${currency}-${amount}-${netId}-${note}`;
|
||||
const noteString = `tornado-${currency}-${amount}-${chainId}-${note}`;
|
||||
console.log(`Your note: ${noteString}`);
|
||||
await backupNote({ currency, amount, netId, note, noteString });
|
||||
|
||||
const commitmentNote = toHex(deposit.commitment);
|
||||
const invoiceString = `tornadoInvoice-${currency}-${amount}-${chainId}-${commitmentNote}`;
|
||||
console.log(`Your invoice for deposit: ${invoiceString}`);
|
||||
|
||||
await backupNote({ currency, amount, netId: chainId, note, noteString });
|
||||
await backupInvoice({ currency, amount, netId: chainId, commitmentNote, invoiceString });
|
||||
|
||||
return (noteString, invoiceString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a deposit
|
||||
* @param currency Сurrency
|
||||
* @param amount Deposit amount
|
||||
*/
|
||||
async function deposit({ currency, amount, commitmentNote }) {
|
||||
assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit');
|
||||
let commitment;
|
||||
if (!commitmentNote) {
|
||||
console.log("Creating new random deposit note");
|
||||
const deposit = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31)
|
||||
});
|
||||
const note = toHex(deposit.preimage, 62);
|
||||
const noteString = `tornado-${currency}-${amount}-${netId}-${note}`;
|
||||
console.log(`Your note: ${noteString}`);
|
||||
await backupNote({ currency, amount, netId, note, noteString });
|
||||
commitment = toHex(deposit.commitment);
|
||||
} else {
|
||||
console.log("Using supplied invoice for deposit");
|
||||
commitment = toHex(commitmentNote);
|
||||
}
|
||||
if (currency === netSymbol.toLowerCase()) {
|
||||
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' });
|
||||
await printETHBalance({ address: senderAccount, name: 'Sender account' });
|
||||
const value = isTestRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 });
|
||||
console.log('Submitting deposit transaction');
|
||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).encodeABI(), value);
|
||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI(), value);
|
||||
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' });
|
||||
await printETHBalance({ address: senderAccount, name: 'Sender account' });
|
||||
} else {
|
||||
@ -222,12 +263,14 @@ async function deposit({ currency, amount }) {
|
||||
}
|
||||
|
||||
console.log('Submitting deposit transaction');
|
||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).encodeABI());
|
||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI());
|
||||
await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' });
|
||||
await printERC20Balance({ address: senderAccount, name: 'Sender account' });
|
||||
}
|
||||
|
||||
return noteString;
|
||||
if(!commitmentNote) {
|
||||
return noteString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1051,6 +1094,29 @@ function parseNote(noteString) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses Tornado.cash deposit invoice
|
||||
* @param invoiceString the note
|
||||
*/
|
||||
function parseInvoice(invoiceString) {
|
||||
const noteRegex = /tornadoInvoice-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<commitmentNote>[0-9a-fA-F]{64})/g
|
||||
const match = noteRegex.exec(invoiceString)
|
||||
if (!match) {
|
||||
throw new Error('The note has invalid format')
|
||||
}
|
||||
|
||||
const netId = Number(match.groups.netId)
|
||||
const buf = Buffer.from(match.groups.commitmentNote, 'hex')
|
||||
const commitmentNote = toHex(buf.slice(0, 32))
|
||||
|
||||
return {
|
||||
currency: match.groups.currency,
|
||||
amount: match.groups.amount,
|
||||
netId,
|
||||
commitmentNote
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDepositData({ amount, currency, deposit }) {
|
||||
try {
|
||||
const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount });
|
||||
@ -1257,6 +1323,27 @@ async function main() {
|
||||
.option('-R, --relayer <URL>', 'Withdraw via relayer')
|
||||
.option('-T, --tor <PORT>', 'Optional tor port')
|
||||
.option('-L, --local', 'Local Node - Does not submit signed transaction to the node');
|
||||
program
|
||||
.command('createNote <currency> <amount> <chainId>')
|
||||
.description(
|
||||
'Create deposit note and invoice, allows generating private key like deposit notes from secure, offline environment. The currency is one of (ETH|DAI|cDAI|USDC|cUSDC|USDT). The amount depends on currency, see config.js file or visit https://tornado.cash.'
|
||||
)
|
||||
.action(async (currency, amount, chainId) => {
|
||||
currency = currency.toLowerCase();
|
||||
await createInvoice({ currency, amount, chainId });
|
||||
});
|
||||
program
|
||||
.command('depositInvoice <invoice>')
|
||||
.description(
|
||||
'Submit a deposit of invoice from default eth account and return the resulting note.'
|
||||
)
|
||||
.action(async (invoice) => {
|
||||
torPort = program.tor;
|
||||
const { currency, amount, netId, commitmentNote } = parseInvoice(invoice);
|
||||
await init({ rpc: program.rpc, currency, amount, localMode: program.local });
|
||||
console.log("Creating", currency.toUpperCase(), amount, "deposit for", netName, "Tornado Cash Instance");
|
||||
await deposit({ currency, amount, commitmentNote });
|
||||
});
|
||||
program
|
||||
.command('deposit <currency> <amount>')
|
||||
.description(
|
||||
|
Loading…
Reference in New Issue
Block a user