From d55ca374e4320ac609005fc0ec81642d85101549 Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:24 +0900 Subject: [PATCH 1/6] Use merkle tree from npm & remove ./lib Reference: https://github.com/tornadocash/tornado-core/commit/0c6e6388529fa7740d9ec5a328ded006412cb0fb - fixed yarn installation bug as well --- cli.js | 13 +- lib/MerkleTree.js | 195 -------- lib/MiMC.js | 13 - lib/Storage.js | 39 -- .../local_modules/xhr2-cookies/.gitignore | 2 - .../local_modules/xhr2-cookies/dist/errors.js | 45 ++ .../xhr2-cookies/dist/errors.js.map | 1 + .../local_modules/xhr2-cookies/dist/index.js | 9 + .../xhr2-cookies/dist/index.js.map | 1 + .../xhr2-cookies/dist/progress-event.js | 15 + .../xhr2-cookies/dist/progress-event.js.map | 1 + .../dist/xml-http-request-event-target.js | 41 ++ .../dist/xml-http-request-event-target.js.map | 1 + .../dist/xml-http-request-upload.js | 78 +++ .../dist/xml-http-request-upload.js.map | 1 + .../xhr2-cookies/dist/xml-http-request.js | 448 ++++++++++++++++++ .../xhr2-cookies/dist/xml-http-request.js.map | 1 + .../web3-providers-http/package.json | 2 +- .../web3-providers-http/tsconfig.json | 2 +- package-lock.json | 22 + package.json | 5 +- yarn.lock | 24 +- 22 files changed, 697 insertions(+), 262 deletions(-) delete mode 100644 lib/MerkleTree.js delete mode 100644 lib/MiMC.js delete mode 100644 lib/Storage.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js.map create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js.map create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js.map create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js.map create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js.map create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js create mode 100644 local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js.map diff --git a/cli.js b/cli.js index a0db85c..e73029a 100755 --- a/cli.js +++ b/cli.js @@ -9,7 +9,7 @@ const snarkjs = require('snarkjs') const crypto = require('crypto') const circomlib = require('circomlib') const bigInt = snarkjs.bigInt -const merkleTree = require('./lib/MerkleTree') +const merkleTree = require('fixed-merkle-tree') const Web3 = require('web3') const Web3HttpProvider = require('web3-providers-http'); const buildGroth16 = require('websnark/src/groth16') @@ -232,7 +232,7 @@ async function generateMerkleProof(deposit, currency, amount) { const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) // Validate that our data is correct - const root = await tree.root() + const root = tree.root() const isValidRoot = await tornadoContract.methods.isKnownRoot(toHex(root)).call() const isSpent = await tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).call() assert(isValidRoot === true, 'Merkle tree is corrupted') @@ -240,7 +240,8 @@ async function generateMerkleProof(deposit, currency, amount) { assert(leafIndex >= 0, 'The deposit is not found in the tree') // Compute merkle proof of our commitment - return tree.path(leafIndex) + const { pathElements, pathIndices } = tree.path(leafIndex) + return { root, pathElements, pathIndices } } /** @@ -253,7 +254,7 @@ async function generateMerkleProof(deposit, currency, amount) { */ async function generateProof({ deposit, currency, amount, recipient, relayerAddress = 0, fee = 0, refund = 0 }) { // Compute merkle proof of our commitment - const { root, path_elements, path_index } = await generateMerkleProof(deposit, currency, amount) + const { root, pathElements, pathIndices } = await generateMerkleProof(deposit, currency, amount) // Prepare circuit input const input = { @@ -268,8 +269,8 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr // Private snark inputs nullifier: deposit.nullifier, secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index + pathElements: pathElements, + pathIndices: pathIndices } console.log('Generating SNARK proof') diff --git a/lib/MerkleTree.js b/lib/MerkleTree.js deleted file mode 100644 index af26b6d..0000000 --- a/lib/MerkleTree.js +++ /dev/null @@ -1,195 +0,0 @@ -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++ - } - - 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 diff --git a/lib/MiMC.js b/lib/MiMC.js deleted file mode 100644 index d070373..0000000 --- a/lib/MiMC.js +++ /dev/null @@ -1,13 +0,0 @@ -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() - } -} - -module.exports = MimcSpongeHasher diff --git a/lib/Storage.js b/lib/Storage.js deleted file mode 100644 index 52b97d6..0000000 --- a/lib/Storage.js +++ /dev/null @@ -1,39 +0,0 @@ - - -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 diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/.gitignore b/local_modules/web3-providers-http/local_modules/xhr2-cookies/.gitignore index ec471e4..022ec5d 100644 --- a/local_modules/web3-providers-http/local_modules/xhr2-cookies/.gitignore +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/.gitignore @@ -1,6 +1,4 @@ node_modules yarn-error.log .idea -**/*.js -**/*.js.map !wallaby.js diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js new file mode 100644 index 0000000..6a27a12 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js @@ -0,0 +1,45 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var SecurityError = /** @class */ (function (_super) { + __extends(SecurityError, _super); + function SecurityError() { + return _super !== null && _super.apply(this, arguments) || this; + } + return SecurityError; +}(Error)); +exports.SecurityError = SecurityError; +var InvalidStateError = /** @class */ (function (_super) { + __extends(InvalidStateError, _super); + function InvalidStateError() { + return _super !== null && _super.apply(this, arguments) || this; + } + return InvalidStateError; +}(Error)); +exports.InvalidStateError = InvalidStateError; +var NetworkError = /** @class */ (function (_super) { + __extends(NetworkError, _super); + function NetworkError() { + return _super !== null && _super.apply(this, arguments) || this; + } + return NetworkError; +}(Error)); +exports.NetworkError = NetworkError; +var SyntaxError = /** @class */ (function (_super) { + __extends(SyntaxError, _super); + function SyntaxError() { + return _super !== null && _super.apply(this, arguments) || this; + } + return SyntaxError; +}(Error)); +exports.SyntaxError = SyntaxError; +//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js.map new file mode 100644 index 0000000..b8867f8 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.js","sourceRoot":"","sources":["../errors.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA;IAAmC,iCAAK;IAAxC;;IAA0C,CAAC;IAAD,oBAAC;AAAD,CAAC,AAA3C,CAAmC,KAAK,GAAG;AAA9B,sCAAa;AAC1B;IAAuC,qCAAK;IAA5C;;IAA8C,CAAC;IAAD,wBAAC;AAAD,CAAC,AAA/C,CAAuC,KAAK,GAAG;AAAlC,8CAAiB;AAC9B;IAAkC,gCAAK;IAAvC;;IAAyC,CAAC;IAAD,mBAAC;AAAD,CAAC,AAA1C,CAAkC,KAAK,GAAG;AAA7B,oCAAY;AACzB;IAAiC,+BAAK;IAAtC;;IAAwC,CAAC;IAAD,kBAAC;AAAD,CAAC,AAAzC,CAAiC,KAAK,GAAG;AAA5B,kCAAW"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js new file mode 100644 index 0000000..dfadc7c --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js @@ -0,0 +1,9 @@ +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./xml-http-request")); +var xml_http_request_event_target_1 = require("./xml-http-request-event-target"); +exports.XMLHttpRequestEventTarget = xml_http_request_event_target_1.XMLHttpRequestEventTarget; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js.map new file mode 100644 index 0000000..f7421cb --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;AAAA,wCAAmC;AACnC,iFAA4E;AAAnE,oEAAA,yBAAyB,CAAA"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js new file mode 100644 index 0000000..9ed411a --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ProgressEvent = /** @class */ (function () { + function ProgressEvent(type) { + this.type = type; + this.bubbles = false; + this.cancelable = false; + this.loaded = 0; + this.lengthComputable = false; + this.total = 0; + } + return ProgressEvent; +}()); +exports.ProgressEvent = ProgressEvent; +//# sourceMappingURL=progress-event.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js.map new file mode 100644 index 0000000..fb763b4 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/progress-event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"progress-event.js","sourceRoot":"","sources":["../progress-event.ts"],"names":[],"mappings":";;AAEA;IAQC,uBAAoB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;QAPhC,YAAO,GAAG,KAAK,CAAC;QAChB,eAAU,GAAG,KAAK,CAAC;QAEnB,WAAM,GAAG,CAAC,CAAC;QACX,qBAAgB,GAAG,KAAK,CAAC;QACzB,UAAK,GAAG,CAAC,CAAC;IAEyB,CAAC;IACrC,oBAAC;AAAD,CAAC,AATD,IASC;AATY,sCAAa"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js new file mode 100644 index 0000000..d937043 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js @@ -0,0 +1,41 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var XMLHttpRequestEventTarget = /** @class */ (function () { + function XMLHttpRequestEventTarget() { + this.listeners = {}; + } + XMLHttpRequestEventTarget.prototype.addEventListener = function (eventType, listener) { + eventType = eventType.toLowerCase(); + this.listeners[eventType] = this.listeners[eventType] || []; + this.listeners[eventType].push(listener.handleEvent || listener); + }; + XMLHttpRequestEventTarget.prototype.removeEventListener = function (eventType, listener) { + eventType = eventType.toLowerCase(); + if (!this.listeners[eventType]) { + return; + } + var index = this.listeners[eventType].indexOf(listener.handleEvent || listener); + if (index < 0) { + return; + } + this.listeners[eventType].splice(index, 1); + }; + XMLHttpRequestEventTarget.prototype.dispatchEvent = function (event) { + var eventType = event.type.toLowerCase(); + event.target = this; // TODO: set event.currentTarget? + if (this.listeners[eventType]) { + for (var _i = 0, _a = this.listeners[eventType]; _i < _a.length; _i++) { + var listener_1 = _a[_i]; + listener_1.call(this, event); + } + } + var listener = this["on" + eventType]; + if (listener) { + listener.call(this, event); + } + return true; + }; + return XMLHttpRequestEventTarget; +}()); +exports.XMLHttpRequestEventTarget = XMLHttpRequestEventTarget; +//# sourceMappingURL=xml-http-request-event-target.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js.map new file mode 100644 index 0000000..371d324 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-event-target.js.map @@ -0,0 +1 @@ +{"version":3,"file":"xml-http-request-event-target.js","sourceRoot":"","sources":["../xml-http-request-event-target.ts"],"names":[],"mappings":";;AAMA;IAAA;QASS,cAAS,GAAmD,EAAE,CAAC;IAiCxE,CAAC;IA/BA,oDAAgB,GAAhB,UAAiB,SAAiB,EAAE,QAAqD;QACxF,SAAS,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,CAAE,QAAwC,CAAC,WAAW,IAAK,QAAkC,CAAC,CAAC;IAC9H,CAAC;IACD,uDAAmB,GAAnB,UAAoB,SAAiB,EAAE,QAAqD;QAC3F,SAAS,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACpC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC;QAAC,CAAC;QAE3C,IAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,OAAO,CAAE,QAAwC,CAAC,WAAW,IAAK,QAAkC,CAAC,CAAC;QAC9I,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC;QAAC,CAAC;QAE1B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,iDAAa,GAAb,UAAc,KAAoB;QACjC,IAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC3C,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,iCAAiC;QAEtD,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC/B,GAAG,CAAC,CAAiB,UAAyB,EAAzB,KAAA,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAzB,cAAyB,EAAzB,IAAyB;gBAAzC,IAAI,UAAQ,SAAA;gBAChB,UAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;aAC3B;QACF,CAAC;QAED,IAAM,QAAQ,GAAG,IAAI,CAAC,OAAK,SAAW,CAAC,CAAC;QACxC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YACd,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;IACb,CAAC;IACF,gCAAC;AAAD,CAAC,AA1CD,IA0CC;AA1CY,8DAAyB"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js new file mode 100644 index 0000000..ce99c78 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js @@ -0,0 +1,78 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var xml_http_request_event_target_1 = require("./xml-http-request-event-target"); +var XMLHttpRequestUpload = /** @class */ (function (_super) { + __extends(XMLHttpRequestUpload, _super); + function XMLHttpRequestUpload() { + var _this = _super.call(this) || this; + _this._contentType = null; + _this._body = null; + _this._reset(); + return _this; + } + XMLHttpRequestUpload.prototype._reset = function () { + this._contentType = null; + this._body = null; + }; + XMLHttpRequestUpload.prototype._setData = function (data) { + if (data == null) { + return; + } + if (typeof data === 'string') { + if (data.length !== 0) { + this._contentType = 'text/plain;charset=UTF-8'; + } + this._body = Buffer.from(data, 'utf-8'); + } + else if (Buffer.isBuffer(data)) { + this._body = data; + } + else if (data instanceof ArrayBuffer) { + var body = Buffer.alloc(data.byteLength); + var view = new Uint8Array(data); + for (var i = 0; i < data.byteLength; i++) { + body[i] = view[i]; + } + this._body = body; + } + else if (data.buffer && data.buffer instanceof ArrayBuffer) { + var body = Buffer.alloc(data.byteLength); + var offset = data.byteOffset; + var view = new Uint8Array(data.buffer); + for (var i = 0; i < data.byteLength; i++) { + body[i] = view[i + offset]; + } + this._body = body; + } + else { + throw new Error("Unsupported send() data " + data); + } + }; + XMLHttpRequestUpload.prototype._finalizeHeaders = function (headers, loweredHeaders) { + if (this._contentType && !loweredHeaders['content-type']) { + headers['Content-Type'] = this._contentType; + } + if (this._body) { + headers['Content-Length'] = this._body.length.toString(); + } + }; + XMLHttpRequestUpload.prototype._startUpload = function (request) { + if (this._body) { + request.write(this._body); + } + request.end(); + }; + return XMLHttpRequestUpload; +}(xml_http_request_event_target_1.XMLHttpRequestEventTarget)); +exports.XMLHttpRequestUpload = XMLHttpRequestUpload; +//# sourceMappingURL=xml-http-request-upload.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js.map new file mode 100644 index 0000000..8f6f72d --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request-upload.js.map @@ -0,0 +1 @@ +{"version":3,"file":"xml-http-request-upload.js","sourceRoot":"","sources":["../xml-http-request-upload.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js new file mode 100644 index 0000000..8872863 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js @@ -0,0 +1,448 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var http = require("http"); +var https = require("https"); +var url = require("url"); +var progress_event_1 = require("./progress-event"); +var errors_1 = require("./errors"); +var xml_http_request_event_target_1 = require("./xml-http-request-event-target"); +var xml_http_request_upload_1 = require("./xml-http-request-upload"); +var Cookie = require("cookiejar"); +var XMLHttpRequest = /** @class */ (function (_super) { + __extends(XMLHttpRequest, _super); + function XMLHttpRequest(options) { + if (options === void 0) { options = {}; } + var _this = _super.call(this) || this; + _this.UNSENT = XMLHttpRequest.UNSENT; + _this.OPENED = XMLHttpRequest.OPENED; + _this.HEADERS_RECEIVED = XMLHttpRequest.HEADERS_RECEIVED; + _this.LOADING = XMLHttpRequest.LOADING; + _this.DONE = XMLHttpRequest.DONE; + _this.onreadystatechange = null; + _this.readyState = XMLHttpRequest.UNSENT; + _this.response = null; + _this.responseText = ''; + _this.responseType = ''; + _this.status = 0; // TODO: UNSENT? + _this.statusText = ''; + _this.timeout = 0; + _this.upload = new xml_http_request_upload_1.XMLHttpRequestUpload(); + _this.responseUrl = ''; + _this.withCredentials = false; + _this._method = null; + _this._url = null; + _this._sync = false; + _this._headers = {}; + _this._loweredHeaders = {}; + _this._mimeOverride = null; // TODO: is type right? + _this._request = null; + _this._response = null; + _this._responseParts = null; + _this._responseHeaders = null; + _this._aborting = null; // TODO: type? + _this._error = null; // TODO: type? + _this._loadedBytes = 0; + _this._totalBytes = 0; + _this._lengthComputable = false; + _this._restrictedMethods = { CONNECT: true, TRACE: true, TRACK: true }; + _this._restrictedHeaders = { + 'accept-charset': true, + 'accept-encoding': true, + 'access-control-request-headers': true, + 'access-control-request-method': true, + connection: true, + 'content-length': true, + cookie: true, + cookie2: true, + date: true, + dnt: true, + expect: true, + host: true, + 'keep-alive': true, + origin: true, + referer: true, + te: true, + trailer: true, + 'transfer-encoding': true, + upgrade: true, + 'user-agent': true, + via: true + }; + _this._privateHeaders = { 'set-cookie': true, 'set-cookie2': true }; + //Redacted private information (${os.type()} ${os.arch()}) node.js/${process.versions.node} v8/${process.versions.v8} from the original version @ github + //Pretend to be tor-browser https://blog.torproject.org/browser-fingerprinting-introduction-and-challenges-ahead/ + _this._userAgent = "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"; + _this._anonymous = options.anon || false; + return _this; + } + XMLHttpRequest.prototype.open = function (method, url, async, user, password) { + if (async === void 0) { async = true; } + method = method.toUpperCase(); + if (this._restrictedMethods[method]) { + throw new XMLHttpRequest.SecurityError("HTTP method " + method + " is not allowed in XHR"); + } + ; + var xhrUrl = this._parseUrl(url, user, password); + if (this.readyState === XMLHttpRequest.HEADERS_RECEIVED || this.readyState === XMLHttpRequest.LOADING) { + // TODO(pwnall): terminate abort(), terminate send() + } + this._method = method; + this._url = xhrUrl; + this._sync = !async; + this._headers = {}; + this._loweredHeaders = {}; + this._mimeOverride = null; + this._setReadyState(XMLHttpRequest.OPENED); + this._request = null; + this._response = null; + this.status = 0; + this.statusText = ''; + this._responseParts = []; + this._responseHeaders = null; + this._loadedBytes = 0; + this._totalBytes = 0; + this._lengthComputable = false; + }; + XMLHttpRequest.prototype.setRequestHeader = function (name, value) { + if (this.readyState !== XMLHttpRequest.OPENED) { + throw new XMLHttpRequest.InvalidStateError('XHR readyState must be OPENED'); + } + var loweredName = name.toLowerCase(); + if (this._restrictedHeaders[loweredName] || /^sec-/.test(loweredName) || /^proxy-/.test(loweredName)) { + console.warn("Refused to set unsafe header \"" + name + "\""); + return; + } + value = value.toString(); + if (this._loweredHeaders[loweredName] != null) { + name = this._loweredHeaders[loweredName]; + this._headers[name] = this._headers[name] + ", " + value; + } + else { + this._loweredHeaders[loweredName] = name; + this._headers[name] = value; + } + }; + XMLHttpRequest.prototype.send = function (data) { + if (this.readyState !== XMLHttpRequest.OPENED) { + throw new XMLHttpRequest.InvalidStateError('XHR readyState must be OPENED'); + } + if (this._request) { + throw new XMLHttpRequest.InvalidStateError('send() already called'); + } + switch (this._url.protocol) { + case 'file:': + return this._sendFile(data); + case 'http:': + case 'https:': + return this._sendHttp(data); + default: + throw new XMLHttpRequest.NetworkError("Unsupported protocol " + this._url.protocol); + } + }; + XMLHttpRequest.prototype.abort = function () { + if (this._request == null) { + return; + } + this._request.abort(); + this._setError(); + this._dispatchProgress('abort'); + this._dispatchProgress('loadend'); + }; + XMLHttpRequest.prototype.getResponseHeader = function (name) { + if (this._responseHeaders == null || name == null) { + return null; + } + var loweredName = name.toLowerCase(); + return this._responseHeaders.hasOwnProperty(loweredName) + ? this._responseHeaders[name.toLowerCase()] + : null; + }; + XMLHttpRequest.prototype.getAllResponseHeaders = function () { + var _this = this; + if (this._responseHeaders == null) { + return ''; + } + return Object.keys(this._responseHeaders).map(function (key) { return key + ": " + _this._responseHeaders[key]; }).join('\r\n'); + }; + XMLHttpRequest.prototype.overrideMimeType = function (mimeType) { + if (this.readyState === XMLHttpRequest.LOADING || this.readyState === XMLHttpRequest.DONE) { + throw new XMLHttpRequest.InvalidStateError('overrideMimeType() not allowed in LOADING or DONE'); + } + this._mimeOverride = mimeType.toLowerCase(); + }; + XMLHttpRequest.prototype.nodejsSet = function (options) { + this.nodejsHttpAgent = options.httpAgent || this.nodejsHttpAgent; + this.nodejsHttpsAgent = options.httpsAgent || this.nodejsHttpsAgent; + if (options.hasOwnProperty('baseUrl')) { + if (options.baseUrl != null) { + var parsedUrl = url.parse(options.baseUrl, false, true); + if (!parsedUrl.protocol) { + throw new XMLHttpRequest.SyntaxError("baseUrl must be an absolute URL"); + } + } + this.nodejsBaseUrl = options.baseUrl; + } + }; + XMLHttpRequest.nodejsSet = function (options) { + XMLHttpRequest.prototype.nodejsSet(options); + }; + XMLHttpRequest.prototype._setReadyState = function (readyState) { + this.readyState = readyState; + this.dispatchEvent(new progress_event_1.ProgressEvent('readystatechange')); + }; + XMLHttpRequest.prototype._sendFile = function (data) { + // TODO + throw new Error('Protocol file: not implemented'); + }; + XMLHttpRequest.prototype._sendHttp = function (data) { + if (this._sync) { + throw new Error('Synchronous XHR processing not implemented'); + } + if (data && (this._method === 'GET' || this._method === 'HEAD')) { + console.warn("Discarding entity body for " + this._method + " requests"); + data = null; + } + else { + data = data || ''; + } + this.upload._setData(data); + this._finalizeHeaders(); + this._sendHxxpRequest(); + }; + XMLHttpRequest.prototype._sendHxxpRequest = function () { + var _this = this; + if (this.withCredentials) { + var cookie = XMLHttpRequest.cookieJar + .getCookies(Cookie.CookieAccessInfo(this._url.hostname, this._url.pathname, this._url.protocol === 'https:')).toValueString(); + this._headers.cookie = this._headers.cookie2 = cookie; + } + var _a = this._url.protocol === 'http:' ? [http, this.nodejsHttpAgent] : [https, this.nodejsHttpsAgent], hxxp = _a[0], agent = _a[1]; + var requestMethod = hxxp.request.bind(hxxp); + var request = requestMethod({ + hostname: this._url.hostname, + port: +this._url.port, + path: this._url.path, + auth: this._url.auth, + method: this._method, + headers: this._headers, + agent: agent + }); + this._request = request; + if (this.timeout) { + request.setTimeout(this.timeout, function () { return _this._onHttpTimeout(request); }); + } + request.on('response', function (response) { return _this._onHttpResponse(request, response); }); + request.on('error', function (error) { return _this._onHttpRequestError(request, error); }); + this.upload._startUpload(request); + if (this._request === request) { + this._dispatchProgress('loadstart'); + } + }; + XMLHttpRequest.prototype._finalizeHeaders = function () { + this._headers = __assign({}, this._headers, { Connection: 'keep-alive', Host: this._url.host, 'User-Agent': this._userAgent }, this._anonymous ? { Referer: 'about:blank' } : {}); + this.upload._finalizeHeaders(this._headers, this._loweredHeaders); + }; + XMLHttpRequest.prototype._onHttpResponse = function (request, response) { + var _this = this; + if (this._request !== request) { + return; + } + if (this.withCredentials && (response.headers['set-cookie'] || response.headers['set-cookie2'])) { + XMLHttpRequest.cookieJar + .setCookies(response.headers['set-cookie'] || response.headers['set-cookie2']); + } + if ([301, 302, 303, 307, 308].indexOf(response.statusCode) >= 0) { + this._url = this._parseUrl(response.headers.location); + this._method = 'GET'; + if (this._loweredHeaders['content-type']) { + delete this._headers[this._loweredHeaders['content-type']]; + delete this._loweredHeaders['content-type']; + } + if (this._headers['Content-Type'] != null) { + delete this._headers['Content-Type']; + } + delete this._headers['Content-Length']; + this.upload._reset(); + this._finalizeHeaders(); + this._sendHxxpRequest(); + return; + } + this._response = response; + this._response.on('data', function (data) { return _this._onHttpResponseData(response, data); }); + this._response.on('end', function () { return _this._onHttpResponseEnd(response); }); + this._response.on('close', function () { return _this._onHttpResponseClose(response); }); + this.responseUrl = this._url.href.split('#')[0]; + this.status = response.statusCode; + this.statusText = http.STATUS_CODES[this.status]; + this._parseResponseHeaders(response); + var lengthString = this._responseHeaders['content-length'] || ''; + this._totalBytes = +lengthString; + this._lengthComputable = !!lengthString; + this._setReadyState(XMLHttpRequest.HEADERS_RECEIVED); + }; + XMLHttpRequest.prototype._onHttpResponseData = function (response, data) { + if (this._response !== response) { + return; + } + this._responseParts.push(Buffer.from(data)); + this._loadedBytes += data.length; + if (this.readyState !== XMLHttpRequest.LOADING) { + this._setReadyState(XMLHttpRequest.LOADING); + } + this._dispatchProgress('progress'); + }; + XMLHttpRequest.prototype._onHttpResponseEnd = function (response) { + if (this._response !== response) { + return; + } + this._parseResponse(); + this._request = null; + this._response = null; + this._setReadyState(XMLHttpRequest.DONE); + this._dispatchProgress('load'); + this._dispatchProgress('loadend'); + }; + XMLHttpRequest.prototype._onHttpResponseClose = function (response) { + if (this._response !== response) { + return; + } + var request = this._request; + this._setError(); + request.abort(); + this._setReadyState(XMLHttpRequest.DONE); + this._dispatchProgress('error'); + this._dispatchProgress('loadend'); + }; + XMLHttpRequest.prototype._onHttpTimeout = function (request) { + if (this._request !== request) { + return; + } + this._setError(); + request.abort(); + this._setReadyState(XMLHttpRequest.DONE); + this._dispatchProgress('timeout'); + this._dispatchProgress('loadend'); + }; + XMLHttpRequest.prototype._onHttpRequestError = function (request, error) { + if (this._request !== request) { + return; + } + this._setError(); + request.abort(); + this._setReadyState(XMLHttpRequest.DONE); + this._dispatchProgress('error'); + this._dispatchProgress('loadend'); + }; + XMLHttpRequest.prototype._dispatchProgress = function (eventType) { + var event = new XMLHttpRequest.ProgressEvent(eventType); + event.lengthComputable = this._lengthComputable; + event.loaded = this._loadedBytes; + event.total = this._totalBytes; + this.dispatchEvent(event); + }; + XMLHttpRequest.prototype._setError = function () { + this._request = null; + this._response = null; + this._responseHeaders = null; + this._responseParts = null; + }; + XMLHttpRequest.prototype._parseUrl = function (urlString, user, password) { + var absoluteUrl = this.nodejsBaseUrl == null ? urlString : url.resolve(this.nodejsBaseUrl, urlString); + var xhrUrl = url.parse(absoluteUrl, false, true); + xhrUrl.hash = null; + var _a = (xhrUrl.auth || '').split(':'), xhrUser = _a[0], xhrPassword = _a[1]; + if (xhrUser || xhrPassword || user || password) { + xhrUrl.auth = (user || xhrUser || '') + ":" + (password || xhrPassword || ''); + } + return xhrUrl; + }; + XMLHttpRequest.prototype._parseResponseHeaders = function (response) { + this._responseHeaders = {}; + for (var name_1 in response.headers) { + var loweredName = name_1.toLowerCase(); + if (this._privateHeaders[loweredName]) { + continue; + } + this._responseHeaders[loweredName] = response.headers[name_1]; + } + if (this._mimeOverride != null) { + this._responseHeaders['content-type'] = this._mimeOverride; + } + }; + XMLHttpRequest.prototype._parseResponse = function () { + var buffer = Buffer.concat(this._responseParts); + this._responseParts = null; + switch (this.responseType) { + case 'json': + this.responseText = null; + try { + this.response = JSON.parse(buffer.toString('utf-8')); + } + catch (_a) { + this.response = null; + } + return; + case 'buffer': + this.responseText = null; + this.response = buffer; + return; + case 'arraybuffer': + this.responseText = null; + var arrayBuffer = new ArrayBuffer(buffer.length); + var view = new Uint8Array(arrayBuffer); + for (var i = 0; i < buffer.length; i++) { + view[i] = buffer[i]; + } + this.response = arrayBuffer; + return; + case 'text': + default: + try { + this.responseText = buffer.toString(this._parseResponseEncoding()); + } + catch (_b) { + this.responseText = buffer.toString('binary'); + } + this.response = this.responseText; + } + }; + XMLHttpRequest.prototype._parseResponseEncoding = function () { + return /;\s*charset=(.*)$/.exec(this._responseHeaders['content-type'] || '')[1] || 'utf-8'; + }; + XMLHttpRequest.ProgressEvent = progress_event_1.ProgressEvent; + XMLHttpRequest.InvalidStateError = errors_1.InvalidStateError; + XMLHttpRequest.NetworkError = errors_1.NetworkError; + XMLHttpRequest.SecurityError = errors_1.SecurityError; + XMLHttpRequest.SyntaxError = errors_1.SyntaxError; + XMLHttpRequest.XMLHttpRequestUpload = xml_http_request_upload_1.XMLHttpRequestUpload; + XMLHttpRequest.UNSENT = 0; + XMLHttpRequest.OPENED = 1; + XMLHttpRequest.HEADERS_RECEIVED = 2; + XMLHttpRequest.LOADING = 3; + XMLHttpRequest.DONE = 4; + XMLHttpRequest.cookieJar = Cookie.CookieJar(); + return XMLHttpRequest; +}(xml_http_request_event_target_1.XMLHttpRequestEventTarget)); +exports.XMLHttpRequest = XMLHttpRequest; +XMLHttpRequest.prototype.nodejsHttpAgent = http.globalAgent; +XMLHttpRequest.prototype.nodejsHttpsAgent = https.globalAgent; +XMLHttpRequest.prototype.nodejsBaseUrl = null; +//# sourceMappingURL=xml-http-request.js.map \ No newline at end of file diff --git a/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js.map b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js.map new file mode 100644 index 0000000..3b63559 --- /dev/null +++ b/local_modules/web3-providers-http/local_modules/xhr2-cookies/dist/xml-http-request.js.map @@ -0,0 +1 @@ +{"version":3,"file":"xml-http-request.js","sourceRoot":"","sources":["../xml-http-request.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/local_modules/web3-providers-http/package.json b/local_modules/web3-providers-http/package.json index 5aa87ff..dac04ae 100644 --- a/local_modules/web3-providers-http/package.json +++ b/local_modules/web3-providers-http/package.json @@ -15,7 +15,7 @@ "main": "lib/index.js", "dependencies": { "web3-core-helpers": "1.6.1", - "xhr2-cookies": "file:./local_modules/xhr2-cookies" + "xhr2-cookies": "file:local_modules/xhr2-cookies" }, "devDependencies": { "dtslint": "^3.4.1", diff --git a/local_modules/web3-providers-http/tsconfig.json b/local_modules/web3-providers-http/tsconfig.json index 06cc6c1..4c3deb7 100644 --- a/local_modules/web3-providers-http/tsconfig.json +++ b/local_modules/web3-providers-http/tsconfig.json @@ -6,4 +6,4 @@ "include": [ "./src" ] - } \ No newline at end of file + } diff --git a/package-lock.json b/package-lock.json index a342406..8788f83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2580,6 +2580,28 @@ "locate-path": "^3.0.0" } }, + "fixed-merkle-tree": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/fixed-merkle-tree/-/fixed-merkle-tree-0.6.1.tgz", + "integrity": "sha512-cYwK8ZJRC/qlRwB6f1WDbqDi3SLSraFr9LQp948U3LmHZpF0VnuB0QOsLhMVwOpR1eJ178oiqz0JcwaupRU9kA==", + "requires": { + "circomlib": "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c", + "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + }, + "dependencies": { + "circomlib": { + "version": "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c", + "from": "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c", + "requires": { + "blake-hash": "^1.1.0", + "blake2b": "^2.1.3", + "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", + "typedarray-to-buffer": "^3.1.5", + "web3": "^1.2.11" + } + } + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", diff --git a/package.json b/package.json index ae1b588..590051a 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,17 @@ "license": "ISC", "dependencies": { "axios": "^0.19.2", - "bignumber.js": "9.0.0", + "bignumber.js": "^9.0.0", "circom": "0.0.35", "circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4", "commander": "^5.1.0", "dotenv": "^8.2.0", + "fixed-merkle-tree": "^0.6.1", "gas-price-oracle": "^0.4.4", "socks-proxy-agent": "^6.1.1", "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", "web3": "^1.6.1", - "web3-providers-http": "file:./local_modules/web3-providers-http", + "web3-providers-http": "file:local_modules/web3-providers-http", "websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index a8ead56..5f19cd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -800,6 +800,16 @@ circom@0.0.35: typedarray-to-buffer "^3.1.5" web3 "^1.2.11" +"circomlib@git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c": + version "0.0.20" + resolved "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c" + dependencies: + blake-hash "^1.1.0" + blake2b "^2.1.3" + snarkjs "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + typedarray-to-buffer "^3.1.5" + web3 "^1.2.11" + class-is@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" @@ -1711,6 +1721,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +fixed-merkle-tree@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/fixed-merkle-tree/-/fixed-merkle-tree-0.6.1.tgz#ff64bf0beb5d862e73d6c643950926b05b440926" + integrity sha512-cYwK8ZJRC/qlRwB6f1WDbqDi3SLSraFr9LQp948U3LmHZpF0VnuB0QOsLhMVwOpR1eJ178oiqz0JcwaupRU9kA== + dependencies: + circomlib "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c" + snarkjs "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -4180,11 +4198,11 @@ web3-providers-http@1.6.1: web3-core-helpers "1.6.1" xhr2-cookies "1.1.0" -"web3-providers-http@file:./local_modules/web3-providers-http": +"web3-providers-http@file:local_modules/web3-providers-http": version "1.6.1" dependencies: web3-core-helpers "1.6.1" - xhr2-cookies "file:./../../AppData/Local/Yarn/Cache/v6/npm-web3-providers-http-1.6.1-6879cf8b-ee02-483d-9a46-76314494b334-1638895560210/node_modules/web3-providers-http/local_modules/xhr2-cookies" + xhr2-cookies "file:local_modules/web3-providers-http/local_modules/xhr2-cookies" web3-providers-ipc@1.3.1: version "1.3.1" @@ -4411,7 +4429,7 @@ xhr2-cookies@1.1.0: dependencies: cookiejar "^2.1.1" -"xhr2-cookies@file:./local_modules/web3-providers-http/local_modules/xhr2-cookies": +"xhr2-cookies@file:local_modules/web3-providers-http/local_modules/xhr2-cookies": version "1.1.0" dependencies: cookiejar "^2.1.1" From 894d4d3a96f6882ba31ffd95b5fdda86fb9a7a6d Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:30 +0900 Subject: [PATCH 2/6] Apply multicall for multiple contract calls - lint cli.js - fixed test networks with getGasPrice - replace mainnet proxy to new one that supports relayer registry, fixes #39 - fixed rounding error with bignumber.js --- README.md | 2 +- build/contracts/Multicall.abi.json | 1 + cli.js | 1092 +++++++++++++++------------- config.js | 10 +- 4 files changed, 580 insertions(+), 525 deletions(-) create mode 100644 build/contracts/Multicall.abi.json mode change 100755 => 100644 cli.js diff --git a/README.md b/README.md index f9004ef..494d8a2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Tornado cli +# Tornado-CLI Command line tool to interact with [Tornado Cash](https://tornadocash.eth.link). diff --git a/build/contracts/Multicall.abi.json b/build/contracts/Multicall.abi.json new file mode 100644 index 0000000..2a8a64e --- /dev/null +++ b/build/contracts/Multicall.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"}] diff --git a/cli.js b/cli.js old mode 100755 new mode 100644 index e73029a..ef0ad80 --- a/cli.js +++ b/cli.js @@ -1,76 +1,98 @@ #!/usr/bin/env node // Works both in browser and node.js -require('dotenv').config() -const fs = require('fs') -const axios = require('axios') -const assert = require('assert') -const snarkjs = require('snarkjs') -const crypto = require('crypto') -const circomlib = require('circomlib') -const bigInt = snarkjs.bigInt -const merkleTree = require('fixed-merkle-tree') -const Web3 = require('web3') +require('dotenv').config(); +const fs = require('fs'); +const axios = require('axios'); +const assert = require('assert'); +const snarkjs = require('snarkjs'); +const crypto = require('crypto'); +const circomlib = require('circomlib'); +const bigInt = snarkjs.bigInt; +const merkleTree = require('fixed-merkle-tree'); +const Web3 = require('web3'); const Web3HttpProvider = require('web3-providers-http'); -const buildGroth16 = require('websnark/src/groth16') -const websnarkUtils = require('websnark/src/utils') -const { toWei, fromWei, toBN, BN } = require('web3-utils') +const buildGroth16 = require('websnark/src/groth16'); +const websnarkUtils = require('websnark/src/utils'); +const { toWei, fromWei, toBN, BN } = require('web3-utils'); const BigNumber = require('bignumber.js'); -const config = require('./config') -const program = require('commander') -const { GasPriceOracle } = require('gas-price-oracle') -const SocksProxyAgent = require('socks-proxy-agent') +const config = require('./config'); +const program = require('commander'); +const { GasPriceOracle } = require('gas-price-oracle'); +const SocksProxyAgent = require('socks-proxy-agent'); -let web3, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, isLocalNode -let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY +let web3, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, isLocalNode, multiCall; +let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY; /** Whether we are in a browser or node.js */ -const inBrowser = typeof window !== 'undefined' -let isTestRPC = false +const inBrowser = typeof window !== 'undefined'; +let isTestRPC = false; /** Generate random number of specified byte length */ -const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)) +const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)); /** Compute pedersen hash */ -const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] +const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]; /** BigNumber to hex string of specified length */ function toHex(number, length = 32) { - const str = number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16) - return '0x' + str.padStart(length * 2, '0') + const str = number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16); + return '0x' + str.padStart(length * 2, '0'); +} + +/** Remove Decimal without rounding with BigNumber */ +function rmDecimalBN(bigNum, decimals = 6) { + return new BigNumber(bigNum).times(BigNumber(10).pow(decimals)).integerValue(BigNumber.ROUND_DOWN).div(BigNumber(10).pow(decimals)).toNumber(); +} + +/** Use MultiCall Contract */ +async function useMultiCall(queryArray) { + const multiCallABI = require('./build/contracts/Multicall.abi.json'); + const multiCallContract = new web3.eth.Contract(multiCallABI, multiCall); + const { returnData } = await multiCallContract.methods.aggregate(queryArray).call(); + return returnData; } /** Display ETH account balance */ async function printETHBalance({ address, name }) { - const checkBalance = new BigNumber(await web3.eth.getBalance(address)); - console.log(`${name} balance is`, checkBalance.div(BigNumber(10).pow(18)).toString(),`${netSymbol}`) + const checkBalance = new BigNumber(await web3.eth.getBalance(address)).div(BigNumber(10).pow(18)); + console.log(`${name} balance is`, rmDecimalBN(checkBalance), `${netSymbol}`); } /** Display ERC20 account balance */ async function printERC20Balance({ address, name, tokenAddress }) { - const erc20ContractJson = require('./build/contracts/ERC20Mock.json') - erc20 = tokenAddress ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : erc20 - const tokenBalance = new BigNumber(await erc20.methods.balanceOf(address).call()) - const tokenDecimals = await erc20.methods.decimals().call() - const tokenName = await erc20.methods.name().call() - const tokenSymbol = await erc20.methods.symbol().call() - console.log(`${name}`,tokenName,`Token Balance is`,tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(),tokenSymbol) + let tokenDecimals, tokenBalance, tokenName, tokenSymbol; + const erc20ContractJson = require('./build/contracts/ERC20Mock.json'); + erc20 = tokenAddress ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : erc20; + if (!isTestRPC || !multiCall) { + const tokenCall = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(address).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.name().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]); + tokenDecimals = parseInt(tokenCall[1]); + tokenBalance = new BigNumber(tokenCall[0]).div(BigNumber(10).pow(tokenDecimals)); + tokenName = web3.eth.abi.decodeParameter('string', tokenCall[2]); + tokenSymbol = web3.eth.abi.decodeParameter('string', tokenCall[3]); + } else { + tokenDecimals = await erc20.methods.decimals().call(); + tokenBalance = new BigNumber(await erc20.methods.balanceOf(address).call()).div(BigNumber(10).pow(tokenDecimals)); + tokenName = await erc20.methods.name().call(); + tokenSymbol = await erc20.methods.symbol().call(); + } + console.log(`${name}`, tokenName, `Balance is`, rmDecimalBN(tokenBalance), tokenSymbol); } async function submitTransaction(signedTX) { console.log("Submitting transaction to the remote node"); await web3.eth.sendSignedTransaction(signedTX) - .on('transactionHash', function (txHash) { - console.log(`View transaction on block explorer https://${getExplorerLink()}/tx/${txHash}`) - }) - .on('error', function (e) { - console.error('on transactionHash error', e.message) - }); + .on('transactionHash', function (txHash) { + console.log(`View transaction on block explorer https://${getExplorerLink()}/tx/${txHash}`); + }) + .on('error', function (e) { + console.error('on transactionHash error', e.message); + }); } async function generateTransaction(to, encodedData, value = 0) { - const nonce = await web3.eth.getTransactionCount(senderAccount) - const gasPrice = await fetchGasPrice() + const nonce = await web3.eth.getTransactionCount(senderAccount); + let gasPrice = await fetchGasPrice(); let gasLimit; async function estimateGas() { @@ -80,9 +102,9 @@ async function generateTransaction(to, encodedData, value = 0) { value : value, nonce : nonce, data : encodedData - }) - const bumped = Math.floor(fetchedGas * 1.3) - return web3.utils.toHex(bumped) + }); + const bumped = Math.floor(fetchedGas * 1.3); + return web3.utils.toHex(bumped); } if (encodedData) { gasLimit = await estimateGas(); @@ -90,7 +112,7 @@ async function generateTransaction(to, encodedData, value = 0) { gasLimit = web3.utils.toHex(21000); } - async function txoptions() { + function txoptions() { // Generate EIP-1559 transaction if (netId == 1) { return { @@ -123,15 +145,15 @@ async function generateTransaction(to, encodedData, value = 0) { } } } - const tx = await txoptions(); + const tx = txoptions(); const signed = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY); if (!isLocalNode) { await submitTransaction(signed.rawTransaction); } else { - console.log('\n=============Raw TX=================','\n') - console.log(`Please submit this raw tx to https://${getExplorerLink()}/pushTx, or otherwise broadcast with node cli.js broadcast command.`,`\n`) - console.log(signed.rawTransaction,`\n`) - console.log('=====================================','\n') + console.log('\n=============Raw TX=================', '\n'); + console.log(`Please submit this raw tx to https://${getExplorerLink()}/pushTx, or otherwise broadcast with node cli.js broadcast command.`, `\n`); + console.log(signed.rawTransaction, `\n`); + console.log('=====================================', '\n'); } } @@ -139,21 +161,21 @@ async function generateTransaction(to, encodedData, value = 0) { * Create deposit object from secret and nullifier */ function createDeposit({ nullifier, secret }) { - const deposit = { nullifier, secret } - deposit.preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)]) - deposit.commitment = pedersenHash(deposit.preimage) - deposit.commitmentHex = toHex(deposit.commitment) - deposit.nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31)) - deposit.nullifierHex = toHex(deposit.nullifierHash) - return deposit + const deposit = { nullifier, secret }; + deposit.preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)]); + deposit.commitment = pedersenHash(deposit.preimage); + deposit.commitmentHex = toHex(deposit.commitment); + deposit.nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31)); + deposit.nullifierHex = toHex(deposit.nullifierHash); + return deposit; } async function backupNote({ currency, amount, netId, note, noteString }) { try { await fs.writeFileSync(`./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`, noteString, 'utf8'); - console.log("Backed up deposit note as",`./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`) + console.log("Backed up deposit note as", `./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`); } catch (e) { - throw new Error('Writing backup note failed:',e) + throw new Error('Writing backup note failed:', e); } } @@ -163,48 +185,48 @@ async function backupNote({ currency, amount, netId, note, noteString }) { * @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') + assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit'); 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 }) + }); + 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 }); 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 printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' }) - await printETHBalance({ address: senderAccount, name: 'Sender account' }) + 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 printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' }); + await printETHBalance({ address: senderAccount, name: 'Sender account' }); } else { // a token - await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }) - await printERC20Balance({ address: senderAccount, name: 'Sender account' }) - const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals - const tokenAmount = isTestRPC ? TOKEN_AMOUNT : fromDecimals({ amount, decimals }) + await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }); + await printERC20Balance({ address: senderAccount, name: 'Sender account' }); + const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals; + const tokenAmount = isTestRPC ? TOKEN_AMOUNT : fromDecimals({ amount, decimals }); if (isTestRPC) { - console.log('Minting some test tokens to deposit') - await generateTransaction(erc20Address, erc20.methods.mint(senderAccount, tokenAmount).encodeABI()) + console.log('Minting some test tokens to deposit'); + await generateTransaction(erc20Address, erc20.methods.mint(senderAccount, tokenAmount).encodeABI()); } - const allowance = await erc20.methods.allowance(senderAccount, tornado._address).call({ from: senderAccount }) - console.log('Current allowance is', fromWei(allowance)) + const allowance = await erc20.methods.allowance(senderAccount, tornado._address).call({ from: senderAccount }); + console.log('Current allowance is', fromWei(allowance)); if (toBN(allowance).lt(toBN(tokenAmount))) { - console.log('Approving tokens for deposit') - await generateTransaction(erc20Address, erc20.methods.approve(tornado._address, tokenAmount).encodeABI()) + console.log('Approving tokens for deposit'); + await generateTransaction(erc20Address, erc20.methods.approve(tornado._address, tokenAmount).encodeABI()); } - console.log('Submitting deposit transaction') - await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).encodeABI()) - await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }) - await printERC20Balance({ address: senderAccount, name: 'Sender account' }) + console.log('Submitting deposit transaction'); + await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).encodeABI()); + await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }); + await printERC20Balance({ address: senderAccount, name: 'Sender account' }); } - return noteString + return noteString; } /** @@ -214,34 +236,41 @@ async function deposit({ currency, amount }) { * @param deposit Deposit object */ async function generateMerkleProof(deposit, currency, amount) { - let leafIndex = -1 + let leafIndex = -1; // Get all deposit events from smart contract and assemble merkle tree from them - const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount }) + const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount }); const leaves = cachedEvents .sort((a, b) => a.leafIndex - b.leafIndex) // Sort events in chronological order .map((e) => { - const index = toBN(e.leafIndex).toNumber() + const index = toBN(e.leafIndex).toNumber(); if (toBN(e.commitment).eq(toBN(deposit.commitmentHex))) { - leafIndex = index + leafIndex = index; } - return toBN(e.commitment).toString(10) - }) - const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) + return toBN(e.commitment).toString(10); + }); + const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves); // Validate that our data is correct - const root = tree.root() - const isValidRoot = await tornadoContract.methods.isKnownRoot(toHex(root)).call() - const isSpent = await tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).call() - assert(isValidRoot === true, 'Merkle tree is corrupted') - assert(isSpent === false, 'The note is already spent') - assert(leafIndex >= 0, 'The deposit is not found in the tree') + const root = tree.root(); + let isValidRoot, isSpent; + if (!isTestRPC || !multiCall) { + const callContract = await useMultiCall([[tornadoContract._address, tornadoContract.methods.isKnownRoot(toHex(root)).encodeABI()], [tornadoContract._address, tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).encodeABI()]]) + isValidRoot = web3.eth.abi.decodeParameter('bool', callContract[0]); + isSpent = web3.eth.abi.decodeParameter('bool', callContract[1]); + } else { + isValidRoot = await tornadoContract.methods.isKnownRoot(toHex(root)).call(); + isSpent = await tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).call(); + } + assert(isValidRoot === true, 'Merkle tree is corrupted'); + assert(isSpent === false, 'The note is already spent'); + assert(leafIndex >= 0, 'The deposit is not found in the tree'); // Compute merkle proof of our commitment - const { pathElements, pathIndices } = tree.path(leafIndex) - return { root, pathElements, pathIndices } + const { pathElements, pathIndices } = tree.path(leafIndex); + return { root, pathElements, pathIndices }; } /** @@ -254,7 +283,7 @@ async function generateMerkleProof(deposit, currency, amount) { */ async function generateProof({ deposit, currency, amount, recipient, relayerAddress = 0, fee = 0, refund = 0 }) { // Compute merkle proof of our commitment - const { root, pathElements, pathIndices } = await generateMerkleProof(deposit, currency, amount) + const { root, pathElements, pathIndices } = await generateMerkleProof(deposit, currency, amount); // Prepare circuit input const input = { @@ -273,11 +302,11 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr pathIndices: pathIndices } - console.log('Generating SNARK proof') - console.time('Proof time') - const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) - const { proof } = websnarkUtils.toSolidityInput(proofData) - console.timeEnd('Proof time') + console.log('Generating SNARK proof'); + console.time('Proof time'); + const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key); + const { proof } = websnarkUtils.toSolidityInput(proofData); + console.timeEnd('Proof time'); const args = [ toHex(input.root), @@ -286,9 +315,9 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr toHex(input.relayer, 20), toHex(input.fee), toHex(input.refund) - ] + ]; - return { proof, args } + return { proof, args }; } /** @@ -299,23 +328,23 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr async function withdraw({ deposit, currency, amount, recipient, relayerURL, torPort, refund = '0' }) { let options = {}; if (currency === netSymbol.toLowerCase() && refund !== '0') { - throw new Error('The ETH purchase is supposted to be 0 for ETH withdrawals') + throw new Error('The ETH purchase is supposted to be 0 for ETH withdrawals'); } - refund = toWei(refund) + refund = toWei(refund); if (relayerURL) { if (relayerURL.endsWith('.eth')) { - throw new Error('ENS name resolving is not supported. Please provide DNS name of the relayer. See instuctions in README.md') + throw new Error('ENS name resolving is not supported. Please provide DNS name of the relayer. See instuctions in README.md'); } if (torPort) { - options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } } + options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } } } - const relayerStatus = await axios.get(relayerURL + '/status', options) + const relayerStatus = await axios.get(relayerURL + '/status', options); const { rewardAccount, netId, ethPrices, tornadoServiceFee } = relayerStatus.data - assert(netId === (await web3.eth.net.getId()) || netId === '*', 'This relay is for different network') - console.log('Relay address:', rewardAccount) + assert(netId === (await web3.eth.net.getId()) || netId === '*', 'This relay is for different network'); + console.log('Relay address:', rewardAccount); - const gasPrice = await fetchGasPrice() + const gasPrice = await fetchGasPrice(); const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals const fee = calculateFee({ @@ -326,14 +355,14 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, torP ethPrices, relayerServiceFee: tornadoServiceFee, decimals - }) + }); if (fee.gt(fromDecimals({ amount, decimals }))) { - throw new Error('Too high refund') - } + throw new Error('Too high refund'); + }; - const { proof, args } = await generateProof({ deposit, currency, amount, recipient, relayerAddress: rewardAccount, fee, refund }) + const { proof, args } = await generateProof({ deposit, currency, amount, recipient, relayerAddress: rewardAccount, fee, refund }); - console.log('Sending withdraw transaction through relay') + console.log('Sending withdraw transaction through relay'); try { const response = await axios.post(relayerURL + '/v1/tornadoWithdraw', { contract: tornadoInstance, @@ -341,36 +370,36 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, torP args }, options) - const { id } = response.data + const { id } = response.data; - const result = await getStatus(id, relayerURL, options) - console.log('STATUS', result) + const result = await getStatus(id, relayerURL, options); + console.log('STATUS', result); } catch (e) { if (e.response) { - console.error(e.response.data.error) + console.error(e.response.data.error); } else { - console.error(e.message) + console.error(e.message); } } } else { // using private key // check if the address of recepient matches with the account of provided private key from environment to prevent accidental use of deposit address for withdrawal transaction. - assert(recipient.toLowerCase() == senderAccount.toLowerCase(), 'Withdrawal recepient mismatches with the account of provided private key from environment file') - const checkBalance = await web3.eth.getBalance(senderAccount) - assert(checkBalance !== 0, 'You have 0 balance, make sure to fund account by withdrawing from tornado using relayer first') + assert(recipient.toLowerCase() == senderAccount.toLowerCase(), 'Withdrawal recepient mismatches with the account of provided private key from environment file'); + const checkBalance = await web3.eth.getBalance(senderAccount); + assert(checkBalance !== 0, 'You have 0 balance, make sure to fund account by withdrawing from tornado using relayer first'); - const { proof, args } = await generateProof({ deposit, currency, amount, recipient, refund }) + const { proof, args } = await generateProof({ deposit, currency, amount, recipient, refund }); - console.log('Submitting withdraw transaction') - await generateTransaction(contractAddress, tornado.methods.withdraw(tornadoInstance, proof, ...args).encodeABI()) + console.log('Submitting withdraw transaction'); + await generateTransaction(contractAddress, tornado.methods.withdraw(tornadoInstance, proof, ...args).encodeABI()); } if (currency === netSymbol.toLowerCase()) { - await printETHBalance({ address: recipient, name: 'Recipient' }) + await printETHBalance({ address: recipient, name: 'Recipient' }); } else { - await printERC20Balance({ address: recipient, name: 'Recipient' }) + await printERC20Balance({ address: recipient, name: 'Recipient' }); } - console.log('Done withdrawal from Tornado Cash') + console.log('Done withdrawal from Tornado Cash'); } /** @@ -381,76 +410,84 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, torP */ async function send({ address, amount, tokenAddress }) { // using private key - assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you send') + assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you send'); if (tokenAddress) { - const erc20ContractJson = require('./build/contracts/ERC20Mock.json') - erc20 = new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) - const tokenBalance = new BigNumber(await erc20.methods.balanceOf(senderAccount).call()) - const tokenDecimals = await erc20.methods.decimals().call() - const tokenSymbol = await erc20.methods.symbol().call() - const toSend = new BigNumber(amount).times(BigNumber(10).pow(tokenDecimals)) + const erc20ContractJson = require('./build/contracts/ERC20Mock.json'); + erc20 = new web3.eth.Contract(erc20ContractJson.abi, tokenAddress); + let tokenBalance, tokenDecimals, tokenSymbol; + if (!isTestRPC || !multiCall) { + const callToken = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(senderAccount).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]); + tokenBalance = new BigNumber(callToken[0]); + tokenDecimals = parseInt(callToken[1]); + tokenSymbol = web3.eth.abi.decodeParameter('string', callToken[2]); + } else { + tokenBalance = new BigNumber(await erc20.methods.balanceOf(senderAccount).call()); + tokenDecimals = await erc20.methods.decimals().call(); + tokenSymbol = await erc20.methods.symbol().call(); + } + const toSend = new BigNumber(amount).times(BigNumber(10).pow(tokenDecimals)); if (tokenBalance.lt(toSend)) { - console.error("You have",tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(),tokenSymbol,", you can't send more than you have") + console.error("You have", rmDecimalBN(tokenBalance.div(BigNumber(10).pow(tokenDecimals))), tokenSymbol, ", you can't send more than you have"); process.exit(1); } - const encodeTransfer = erc20.methods.transfer(address, toSend).encodeABI() - await generateTransaction(tokenAddress, encodeTransfer) - console.log('Sent',amount,tokenSymbol,'to',address); + const encodeTransfer = erc20.methods.transfer(address, toSend).encodeABI(); + await generateTransaction(tokenAddress, encodeTransfer); + console.log('Sent', amount, tokenSymbol, 'to', address); } else { const balance = new BigNumber(await web3.eth.getBalance(senderAccount)); - assert(balance.toNumber() !== 0, "You have 0 balance, can't send transaction") + assert(balance.toNumber() !== 0, "You have 0 balance, can't send transaction"); if (amount) { - toSend = new BigNumber(amount).times(BigNumber(10).pow(18)) + toSend = new BigNumber(amount).times(BigNumber(10).pow(18)); if (balance.lt(toSend)) { - console.error("You have",balance.div(BigNumber(10).pow(18)),netSymbol+", you can't send more than you have.") + console.error("You have", rmDecimalBN(balance.div(BigNumber(10).pow(18))), netSymbol + ", you can't send more than you have."); process.exit(1); } } else { - console.log('Amount not defined, sending all available amounts') + console.log('Amount not defined, sending all available amounts'); const gasPrice = new BigNumber(await fetchGasPrice()); const gasLimit = new BigNumber(21000); - if (netId == 1 || netId == 5) { + if (netId == 1) { const priorityFee = new BigNumber(await gasPrices(3)); toSend = balance.minus(gasLimit.times(gasPrice.plus(priorityFee))); } else { toSend = balance.minus(gasLimit.times(gasPrice)); } } - await generateTransaction(address, null, toSend) - console.log('Sent',toSend.div(BigNumber(10).pow(18)).toString(),netSymbol,'to',address); + await generateTransaction(address, null, toSend); + console.log('Sent', rmDecimalBN(toSend.div(BigNumber(10).pow(18))), netSymbol, 'to', address); } } function getStatus(id, relayerURL, options) { return new Promise((resolve) => { async function getRelayerStatus() { - const responseStatus = await axios.get(relayerURL + '/v1/jobs/' + id, options) + const responseStatus = await axios.get(relayerURL + '/v1/jobs/' + id, options); if (responseStatus.status === 200) { const { txHash, status, confirmations, failedReason } = responseStatus.data - console.log(`Current job status ${status}, confirmations: ${confirmations}`) + console.log(`Current job status ${status}, confirmations: ${confirmations}`); if (status === 'FAILED') { - throw new Error(status + ' failed reason:' + failedReason) + throw new Error(status + ' failed reason:' + failedReason); } if (status === 'CONFIRMED') { - const receipt = await waitForTxReceipt({ txHash }) + const receipt = await waitForTxReceipt({ txHash }); console.log( `Transaction submitted through the relay. View transaction on block explorer https://${getExplorerLink()}/tx/${txHash}` - ) - console.log('Transaction mined in block', receipt.blockNumber) - resolve(status) + ); + console.log('Transaction mined in block', receipt.blockNumber); + resolve(status); } } setTimeout(() => { - getRelayerStatus(id, relayerURL) + getRelayerStatus(id, relayerURL); }, 3000) } - getRelayerStatus() + getRelayerStatus(); }) } @@ -459,110 +496,110 @@ function capitalizeFirstLetter(string) { } function fromDecimals({ amount, decimals }) { - amount = amount.toString() - let ether = amount.toString() - const base = new BN('10').pow(new BN(decimals)) - const baseLength = base.toString(10).length - 1 || 1 + amount = amount.toString(); + let ether = amount.toString(); + const base = new BN('10').pow(new BN(decimals)); + const baseLength = base.toString(10).length - 1 || 1; - const negative = ether.substring(0, 1) === '-' + const negative = ether.substring(0, 1) === '-'; if (negative) { - ether = ether.substring(1) + ether = ether.substring(1); } if (ether === '.') { - throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, invalid value') + throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, invalid value'); } // Split it into a whole and fractional part - const comps = ether.split('.') + const comps = ether.split('.'); if (comps.length > 2) { - throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points') + throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points'); } - let whole = comps[0] - let fraction = comps[1] + let whole = comps[0]; + let fraction = comps[1]; if (!whole) { - whole = '0' + whole = '0'; } if (!fraction) { - fraction = '0' + fraction = '0'; } if (fraction.length > baseLength) { - throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places') + throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places'); } while (fraction.length < baseLength) { - fraction += '0' + fraction += '0'; } - whole = new BN(whole) - fraction = new BN(fraction) - let wei = whole.mul(base).add(fraction) + whole = new BN(whole); + fraction = new BN(fraction); + let wei = whole.mul(base).add(fraction); if (negative) { - wei = wei.mul(negative) + wei = wei.mul(negative); } - return new BN(wei.toString(10), 10) + return new BN(wei.toString(10), 10); } function toDecimals(value, decimals, fixed) { - const zero = new BN(0) - const negative1 = new BN(-1) - decimals = decimals || 18 - fixed = fixed || 7 + const zero = new BN(0); + const negative1 = new BN(-1); + decimals = decimals || 18; + fixed = fixed || 7; - value = new BN(value) - const negative = value.lt(zero) - const base = new BN('10').pow(new BN(decimals)) - const baseLength = base.toString(10).length - 1 || 1 + value = new BN(value); + const negative = value.lt(zero); + const base = new BN('10').pow(new BN(decimals)); + const baseLength = base.toString(10).length - 1 || 1; if (negative) { - value = value.mul(negative1) + value = value.mul(negative1); } - let fraction = value.mod(base).toString(10) + let fraction = value.mod(base).toString(10); while (fraction.length < baseLength) { - fraction = `0${fraction}` + fraction = `0${fraction}`; } - fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1] + fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]; - const whole = value.div(base).toString(10) - value = `${whole}${fraction === '0' ? '' : `.${fraction}`}` + const whole = value.div(base).toString(10); + value = `${whole}${fraction === '0' ? '' : `.${fraction}`}`; if (negative) { - value = `-${value}` + value = `-${value}`; } if (fixed) { - value = value.slice(0, fixed) + value = value.slice(0, fixed); } - return value + return value; } // List fetched from https://github.com/ethereum-lists/chains/blob/master/_data/chains function getExplorerLink() { switch (netId) { case 56: - return 'bscscan.com' + return 'bscscan.com'; case 100: - return 'blockscout.com/poa/xdai' + return 'blockscout.com/poa/xdai'; case 137: - return 'polygonscan.com' + return 'polygonscan.com'; case 42161: - return 'arbiscan.io' + return 'arbiscan.io'; case 43114: - return 'snowtrace.io' + return 'snowtrace.io'; case 5: - return 'goerli.etherscan.io' + return 'goerli.etherscan.io'; case 42: - return 'kovan.etherscan.io' + return 'kovan.etherscan.io'; case 10: - return 'optimistic.etherscan.io' + return 'optimistic.etherscan.io'; default: - return 'etherscan.io' + return 'etherscan.io'; } } @@ -570,52 +607,52 @@ function getExplorerLink() { function getCurrentNetworkName() { switch (netId) { case 1: - return 'Ethereum' + return 'Ethereum'; case 56: - return 'BinanceSmartChain' + return 'BinanceSmartChain'; case 100: - return 'GnosisChain' + return 'GnosisChain'; case 137: - return 'Polygon' + return 'Polygon'; case 42161: - return 'Arbitrum' + return 'Arbitrum'; case 43114: - return 'Avalanche' + return 'Avalanche'; case 5: - return 'Goerli' + return 'Goerli'; case 42: - return 'Kovan' + return 'Kovan'; case 10: - return 'Optimism' + return 'Optimism'; default: - return 'testRPC' + return 'testRPC'; } } function getCurrentNetworkSymbol() { switch (netId) { case 56: - return 'BNB' + return 'BNB'; case 100: - return 'xDAI' + return 'xDAI'; case 137: - return 'MATIC' + return 'MATIC'; case 43114: - return 'AVAX' + return 'AVAX'; default: - return 'ETH' + return 'ETH'; } } function gasPricesETH(value = 80) { - const tenPercent = (Number(value) * 5) / 100 - const max = Math.max(tenPercent, 3) - const bumped = Math.floor(Number(value) + max) - return toHex(toWei(bumped.toString(), 'gwei')) + const tenPercent = (Number(value) * 5) / 100; + const max = Math.max(tenPercent, 3); + const bumped = Math.floor(Number(value) + max); + return toHex(toWei(bumped.toString(), 'gwei')); } function gasPrices(value = 5) { - return toHex(toWei(value.toString(), 'gwei')) + return toHex(toWei(value.toString(), 'gwei')); } async function fetchGasPrice() { @@ -625,44 +662,45 @@ async function fetchGasPrice() { } // Bump fees for Ethereum network if (netId == 1) { - const oracle = new GasPriceOracle(options) - const gas = await oracle.gasPrices() - return gasPricesETH(gas.instant) + const oracle = new GasPriceOracle(options); + const gas = await oracle.gasPrices(); + return gasPricesETH(gas.instant); } else if (netId == 5 || isTestRPC) { - return gasPrices(1) + const web3GasPrice = await web3.eth.getGasPrice(); + return web3GasPrice; } else { - const oracle = new GasPriceOracle(options) - const gas = await oracle.gasPrices() - return gasPrices(gas.instant) + const oracle = new GasPriceOracle(options); + const gas = await oracle.gasPrices(); + return gasPrices(gas.instant); } } catch (err) { - throw new Error(`Method fetchGasPrice has error ${err.message}`) + throw new Error(`Method fetchGasPrice has error ${err.message}`); } } function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerServiceFee, decimals }) { const decimalsPoint = - Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? 0 : relayerServiceFee.toString().split('.')[1].length - const roundDecimal = 10 ** decimalsPoint - const total = toBN(fromDecimals({ amount, decimals })) - const feePercent = total.mul(toBN(relayerServiceFee * roundDecimal)).div(toBN(roundDecimal * 100)) - const expense = toBN(gasPrice).mul(toBN(5e5)) - let desiredFee + Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? 0 : relayerServiceFee.toString().split('.')[1].length; + const roundDecimal = 10 ** decimalsPoint; + const total = toBN(fromDecimals({ amount, decimals })); + const feePercent = total.mul(toBN(relayerServiceFee * roundDecimal)).div(toBN(roundDecimal * 100)); + const expense = toBN(gasPrice).mul(toBN(5e5)); + let desiredFee; switch (currency) { case netSymbol.toLowerCase(): { - desiredFee = expense.add(feePercent) - break + desiredFee = expense.add(feePercent); + break; } default: { desiredFee = expense .add(toBN(refund)) .mul(toBN(10 ** decimals)) - .div(toBN(ethPrices[currency])) - desiredFee = desiredFee.add(feePercent) - break + .div(toBN(ethPrices[currency])); + desiredFee = desiredFee.add(feePercent); + break; } } - return desiredFee + return desiredFee; } /** @@ -674,42 +712,42 @@ function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerSe function waitForTxReceipt({ txHash, attempts = 60, delay = 1000 }) { return new Promise((resolve, reject) => { const checkForTx = async (txHash, retryAttempt = 0) => { - const result = await web3.eth.getTransactionReceipt(txHash) + const result = await web3.eth.getTransactionReceipt(txHash); if (!result || !result.blockNumber) { if (retryAttempt <= attempts) { - setTimeout(() => checkForTx(txHash, retryAttempt + 1), delay) + setTimeout(() => checkForTx(txHash, retryAttempt + 1), delay); } else { - reject(new Error('tx was not mined')) + reject(new Error('tx was not mined')); } } else { - resolve(result) + resolve(result); } } - checkForTx(txHash) + checkForTx(txHash); }) } function initJson(file) { - return new Promise((resolve, reject) => { - fs.readFile(file, 'utf8', (error, data) => { - if (error) { - resolve([]); - } - try { - resolve(JSON.parse(data)); - } catch (error) { - resolve([]); - } - }); + return new Promise((resolve, reject) => { + fs.readFile(file, 'utf8', (error, data) => { + if (error) { + resolve([]); + } + try { + resolve(JSON.parse(data)); + } catch (error) { + resolve([]); + } }); + }); }; function loadCachedEvents({ type, currency, amount }) { try { - const module = require(`./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`) + const module = require(`./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`); if (module) { - const events = module + const events = module; return { events, @@ -717,7 +755,7 @@ function loadCachedEvents({ type, currency, amount }) { } } } catch (err) { - console.log("Error fetching cached files, syncing from block",deployedBlockNumber) + console.log("Error fetching cached files, syncing from block", deployedBlockNumber); return { events: [], lastBlock: deployedBlockNumber, @@ -726,101 +764,108 @@ function loadCachedEvents({ type, currency, amount }) { } async function fetchEvents({ type, currency, amount}) { - if (type === "withdraw") { - type = "withdrawal" - } + if (type === "withdraw") { + type = "withdrawal"; + } - const cachedEvents = loadCachedEvents({ type, currency, amount }) - const startBlock = cachedEvents.lastBlock + 1 + const cachedEvents = loadCachedEvents({ type, currency, amount }); + const startBlock = cachedEvents.lastBlock + 1; - console.log("Loaded cached",amount,currency.toUpperCase(),type,"events for",startBlock,"block") - console.log("Fetching",amount,currency.toUpperCase(),type,"events for",netName,"network") + console.log("Loaded cached",amount,currency.toUpperCase(),type,"events for",startBlock,"block"); + console.log("Fetching",amount,currency.toUpperCase(),type,"events for",netName,"network"); - async function syncEvents() { - try { - let targetBlock = await web3.eth.getBlockNumber(); - let chunks = 1000; - for (let i=startBlock; i < targetBlock; i+=chunks) { - let fetchedEvents = []; - async function fetchLatestEvents(i) { - let j; - if (i+chunks-1 > targetBlock) { - j = targetBlock; - } else { - j = i+chunks-1; + async function syncEvents() { + try { + let targetBlock = await web3.eth.getBlockNumber(); + let chunks = 1000; + + for (let i = startBlock; i < targetBlock; i += chunks) { + let fetchedEvents = []; + + function mapDepositEvents() { + fetchedEvents = fetchedEvents.map(({ blockNumber, transactionHash, returnValues }) => { + const { commitment, leafIndex, timestamp } = returnValues; + return { + blockNumber, + transactionHash, + commitment, + leafIndex: Number(leafIndex), + timestamp } - await tornadoContract.getPastEvents(capitalizeFirstLetter(type), { - fromBlock: i, - toBlock: j, - }).then(r => { fetchedEvents = fetchedEvents.concat(r); console.log("Fetched",amount,currency.toUpperCase(),type,"events to block:", j) }, err => { console.error(i + " failed fetching",type,"events from node", err); process.exit(1); }).catch(console.log); - } - - async function mapDepositEvents() { - fetchedEvents = fetchedEvents.map(({ blockNumber, transactionHash, returnValues }) => { - const { commitment, leafIndex, timestamp } = returnValues - return { - blockNumber, - transactionHash, - commitment, - leafIndex: Number(leafIndex), - timestamp - } - }) - } - - async function mapWithdrawEvents() { - fetchedEvents = fetchedEvents.map(({ blockNumber, transactionHash, returnValues }) => { - const { nullifierHash, to, fee } = returnValues - return { - blockNumber, - transactionHash, - nullifierHash, - to, - fee - } - }) - } - - async function mapLatestEvents() { - if (type === "deposit"){ - await mapDepositEvents(); - } else { - await mapWithdrawEvents(); - } - } - - async function updateCache() { - try { - const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json` - const localEvents = await initJson(fileName); - const events = localEvents.concat(fetchedEvents); - await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8') - } catch (error) { - throw new Error('Writing cache file failed:',error) - } - } - await fetchLatestEvents(i); - await mapLatestEvents(); - await updateCache(); + }); } - } catch (error) { - throw new Error("Error while updating cache") - process.exit(1) + + function mapWithdrawEvents() { + fetchedEvents = fetchedEvents.map(({ blockNumber, transactionHash, returnValues }) => { + const { nullifierHash, to, fee } = returnValues; + return { + blockNumber, + transactionHash, + nullifierHash, + to, + fee + } + }); + } + + function mapLatestEvents() { + if (type === "deposit"){ + mapDepositEvents(); + } else { + mapWithdrawEvents(); + } + } + + async function fetchLatestEvents(i) { + let j; + if (i + chunks - 1 > targetBlock) { + j = targetBlock; + } else { + j = i + chunks - 1; + } + + await tornadoContract.getPastEvents(capitalizeFirstLetter(type), { + fromBlock: i, + toBlock: j, + }).then(r => { fetchedEvents = fetchedEvents.concat(r); console.log("Fetched",amount,currency.toUpperCase(),type,"events to block:", j) }, err => { console.error(i + " failed fetching",type,"events from node", err); process.exit(1); }).catch(console.log); + + if (type === "deposit"){ + mapDepositEvents(); + } else { + mapWithdrawEvents(); + } + } + + async function updateCache() { + try { + const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`; + const localEvents = await initJson(fileName); + const events = localEvents.concat(fetchedEvents); + await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8'); + } catch (error) { + throw new Error('Writing cache file failed:',error); + } + } + await fetchLatestEvents(i); + await updateCache(); } + } catch (error) { + throw new Error("Error while updating cache"); + process.exit(1); } - await syncEvents(); + } + await syncEvents(); - async function loadUpdatedEvents() { - const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json` - const updatedEvents = await initJson(fileName); - const updatedBlock = updatedEvents[updatedEvents.length - 1].blockNumber - console.log("Cache updated for Tornado",type,amount,currency,"instance to block",updatedBlock,"successfully") - console.log(`Total ${type}s:`, updatedEvents.length) - return updatedEvents; - } - const events = await loadUpdatedEvents(); - - return events + async function loadUpdatedEvents() { + const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`; + const updatedEvents = await initJson(fileName); + const updatedBlock = updatedEvents[updatedEvents.length - 1].blockNumber; + console.log("Cache updated for Tornado",type,amount,currency,"instance to block",updatedBlock,"successfully"); + console.log(`Total ${type}s:`, updatedEvents.length); + return updatedEvents; + } + const events = await loadUpdatedEvents(); + return events; } /** @@ -829,16 +874,16 @@ async function fetchEvents({ type, currency, amount}) { */ function parseNote(noteString) { const noteRegex = /tornado-(?\w+)-(?[\d.]+)-(?\d+)-0x(?[0-9a-fA-F]{124})/g - const match = noteRegex.exec(noteString) + const match = noteRegex.exec(noteString); if (!match) { - throw new Error('The note has invalid format') + throw new Error('The note has invalid format'); } - const buf = Buffer.from(match.groups.note, 'hex') - const nullifier = bigInt.leBuff2int(buf.slice(0, 31)) - const secret = bigInt.leBuff2int(buf.slice(31, 62)) - const deposit = createDeposit({ nullifier, secret }) - const netId = Number(match.groups.netId) + const buf = Buffer.from(match.groups.note, 'hex'); + const nullifier = bigInt.leBuff2int(buf.slice(0, 31)); + const secret = bigInt.leBuff2int(buf.slice(31, 62)); + const deposit = createDeposit({ nullifier, secret }); + const netId = Number(match.groups.netId); return { currency: match.groups.currency, @@ -850,19 +895,19 @@ function parseNote(noteString) { async function loadDepositData({ amount, currency, deposit }) { try { - const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount }) + const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount }); const eventWhenHappened = await cachedEvents.filter(function (event) { return event.commitment === deposit.commitmentHex; - })[0] + })[0]; if (eventWhenHappened.length === 0) { - throw new Error('There is no related deposit, the note is invalid') + throw new Error('There is no related deposit, the note is invalid'); } - const timestamp = eventWhenHappened.timestamp - const txHash = eventWhenHappened.transactionHash - const isSpent = await tornadoContract.methods.isSpent(deposit.nullifierHex).call() - const receipt = await web3.eth.getTransactionReceipt(txHash) + const timestamp = eventWhenHappened.timestamp; + const txHash = eventWhenHappened.transactionHash; + const isSpent = await tornadoContract.methods.isSpent(deposit.nullifierHex).call(); + const receipt = await web3.eth.getTransactionReceipt(txHash); return { timestamp, @@ -872,22 +917,22 @@ async function loadDepositData({ amount, currency, deposit }) { commitment: deposit.commitmentHex } } catch (e) { - console.error('loadDepositData', e) + console.error('loadDepositData', e); } return {} } async function loadWithdrawalData({ amount, currency, deposit }) { try { - const cachedEvents = await fetchEvents({ type: 'withdrawal', currency, amount }) + const cachedEvents = await fetchEvents({ type: 'withdrawal', currency, amount }); const withdrawEvent = cachedEvents.filter((event) => { return event.nullifierHash === deposit.nullifierHex - })[0] + })[0]; - const fee = withdrawEvent.fee - const decimals = config.deployments[`netId${netId}`][currency].decimals - const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(toBN(fee)) - const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockNumber) + const fee = withdrawEvent.fee; + const decimals = config.deployments[`netId${netId}`][currency].decimals; + const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(toBN(fee)); + const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockNumber); return { amount: toDecimals(withdrawalAmount, decimals, 9), txHash: withdrawEvent.transactionHash, @@ -897,7 +942,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) { fee: toDecimals(fee, decimals, 9) } } catch (e) { - console.error('loadWithdrawalData', e) + console.error('loadWithdrawalData', e); } } @@ -912,157 +957,158 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, // To assemble web version run `npm run browserify` web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 - }) - contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json() - instanceJson = await (await fetch('build/contracts/Instance.abi.json')).json() - circuit = await (await fetch('build/circuits/tornado.json')).json() - proving_key = await (await fetch('build/circuits/tornadoProvingKey.bin')).arrayBuffer() - MERKLE_TREE_HEIGHT = 20 - ETH_AMOUNT = 1e18 - TOKEN_AMOUNT = 1e19 - senderAccount = (await web3.eth.getAccounts())[0] + }); + contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json(); + instanceJson = await (await fetch('build/contracts/Instance.abi.json')).json(); + circuit = await (await fetch('build/circuits/tornado.json')).json(); + proving_key = await (await fetch('build/circuits/tornadoProvingKey.bin')).arrayBuffer(); + MERKLE_TREE_HEIGHT = 20; + ETH_AMOUNT = 1e18; + TOKEN_AMOUNT = 1e19; + senderAccount = (await web3.eth.getAccounts())[0]; } else { let ipOptions = {}; if (torPort && rpc.includes("https")) { - console.log("Using tor network") - web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort) }, timeout: 60000 } + console.log("Using tor network"); + web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }; // Use forked web3-providers-http from local file to modify user-agent header value which improves privacy. - web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }) - ipOptions = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } } + web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }); + ipOptions = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } }; } else if (torPort && rpc.includes("http")) { - console.log("Using tor network") - web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort) }, timeout: 60000 } + console.log("Using tor network"); + web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }; // Use forked web3-providers-http from local file to modify user-agent header value which improves privacy. - web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }) - ipOptions = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } } + web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }); + ipOptions = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } }; } else if (rpc.includes("ipc")) { - console.log("Using ipc connection") - web3 = new Web3(new Web3.providers.IpcProvider(rpc, net), null, { transactionConfirmationBlocks: 1 }) + console.log("Using ipc connection"); + web3 = new Web3(new Web3.providers.IpcProvider(rpc, net), null, { transactionConfirmationBlocks: 1 }); } else if (rpc.includes("ws") || rpc.includes("wss")) { - console.log("Using websocket connection (Note: Tor is not supported for Websocket providers)") - web3Options = { clientConfig: { keepalive: true, keepaliveInterval: -1 }, reconnect: { auto: true, delay: 1000, maxAttempts: 10, onTimeout: false } } - web3 = new Web3(new Web3.providers.WebsocketProvider(rpc, web3Options), net, { transactionConfirmationBlocks: 1 }) + console.log("Using websocket connection (Note: Tor is not supported for Websocket providers)"); + web3Options = { clientConfig: { keepalive: true, keepaliveInterval: -1 }, reconnect: { auto: true, delay: 1000, maxAttempts: 10, onTimeout: false } }; + web3 = new Web3(new Web3.providers.WebsocketProvider(rpc, web3Options), net, { transactionConfirmationBlocks: 1 }); } else { - console.log("Connecting to remote node") - web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }) + console.log("Connecting to remote node"); + web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }); } try { const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions) const { country, ip } = fetchRemoteIP.data - console.log('Your remote IP address is',ip,'from',country+'.'); + console.log('Your remote IP address is', ip, 'from', country + '.'); } catch (error) { console.error('Could not fetch remote IP from ip.tornado.cash, use VPN if the problem repeats.'); } - contractJson = require('./build/contracts/TornadoProxy.abi.json') - instanceJson = require('./build/contracts/Instance.abi.json') - circuit = require('./build/circuits/tornado.json') - proving_key = fs.readFileSync('build/circuits/tornadoProvingKey.bin').buffer - MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20 - ETH_AMOUNT = process.env.ETH_AMOUNT - TOKEN_AMOUNT = process.env.TOKEN_AMOUNT - const privKey = process.env.PRIVATE_KEY + contractJson = require('./build/contracts/TornadoProxy.abi.json'); + instanceJson = require('./build/contracts/Instance.abi.json'); + circuit = require('./build/circuits/tornado.json'); + proving_key = fs.readFileSync('build/circuits/tornadoProvingKey.bin').buffer; + MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20; + ETH_AMOUNT = process.env.ETH_AMOUNT; + TOKEN_AMOUNT = process.env.TOKEN_AMOUNT; + const privKey = process.env.PRIVATE_KEY; if (privKey) { if (privKey.includes("0x")) { - PRIVATE_KEY = process.env.PRIVATE_KEY.substring(2) + PRIVATE_KEY = process.env.PRIVATE_KEY.substring(2); } else { - PRIVATE_KEY = process.env.PRIVATE_KEY + PRIVATE_KEY = process.env.PRIVATE_KEY; } } if (PRIVATE_KEY) { - const account = web3.eth.accounts.privateKeyToAccount('0x' + PRIVATE_KEY) - web3.eth.accounts.wallet.add('0x' + PRIVATE_KEY) - web3.eth.defaultAccount = account.address - senderAccount = account.address + const account = web3.eth.accounts.privateKeyToAccount('0x' + PRIVATE_KEY); + web3.eth.accounts.wallet.add('0x' + PRIVATE_KEY); + web3.eth.defaultAccount = account.address; + senderAccount = account.address; } - erc20ContractJson = require('./build/contracts/ERC20Mock.json') - erc20tornadoJson = require('./build/contracts/ERC20Tornado.json') + erc20ContractJson = require('./build/contracts/ERC20Mock.json'); + erc20tornadoJson = require('./build/contracts/ERC20Tornado.json'); } // groth16 initialises a lot of Promises that will never be resolved, that's why we need to use process.exit to terminate the CLI - groth16 = await buildGroth16() - netId = await web3.eth.net.getId() - netName = getCurrentNetworkName() - netSymbol = getCurrentNetworkSymbol() + groth16 = await buildGroth16(); + netId = await web3.eth.net.getId(); + netName = getCurrentNetworkName(); + netSymbol = getCurrentNetworkSymbol(); if (noteNetId && Number(noteNetId) !== netId) { - throw new Error('This note is for a different network. Specify the --rpc option explicitly') + throw new Error('This note is for a different network. Specify the --rpc option explicitly'); } if (netName === "testRPC") { isTestRPC = true; } if (localMode) { - console.log("Local mode detected: will not submit signed TX to remote node") + console.log("Local mode detected: will not submit signed TX to remote node"); isLocalNode = true; } if (isTestRPC) { - tornadoAddress = currency === netSymbol.toLowerCase() ? contractJson.networks[netId].address : erc20tornadoJson.networks[netId].address - tokenAddress = currency !== netSymbol.toLowerCase() ? erc20ContractJson.networks[netId].address : null - deployedBlockNumber = 0 - senderAccount = (await web3.eth.getAccounts())[0] + tornadoAddress = currency === netSymbol.toLowerCase() ? contractJson.networks[netId].address : erc20tornadoJson.networks[netId].address; + tokenAddress = currency !== netSymbol.toLowerCase() ? erc20ContractJson.networks[netId].address : null; + deployedBlockNumber = 0; + senderAccount = (await web3.eth.getAccounts())[0]; } else { try { if (balanceCheck) { - currency = netSymbol.toLowerCase() - amount = Object.keys(config.deployments[`netId${netId}`][currency].instanceAddress)[0] + currency = netSymbol.toLowerCase(); + amount = Object.keys(config.deployments[`netId${netId}`][currency].instanceAddress)[0]; } - tornadoAddress = config.deployments[`netId${netId}`].proxy - tornadoInstance = config.deployments[`netId${netId}`][currency].instanceAddress[amount] - deployedBlockNumber = config.deployments[`netId${netId}`][currency].deployedBlockNumber[amount] + tornadoAddress = config.deployments[`netId${netId}`].proxy; + multiCall = config.deployments[`netId${netId}`].multicall; + tornadoInstance = config.deployments[`netId${netId}`][currency].instanceAddress[amount]; + deployedBlockNumber = config.deployments[`netId${netId}`][currency].deployedBlockNumber[amount]; if (!tornadoAddress) { - throw new Error() + throw new Error(); } - tokenAddress = currency !== netSymbol.toLowerCase() ? config.deployments[`netId${netId}`][currency].tokenAddress : null + tokenAddress = currency !== netSymbol.toLowerCase() ? config.deployments[`netId${netId}`][currency].tokenAddress : null; } catch (e) { - console.error('There is no such tornado instance, check the currency and amount you provide', e) - process.exit(1) + console.error('There is no such tornado instance, check the currency and amount you provide', e); + process.exit(1); } } - tornado = new web3.eth.Contract(contractJson, tornadoAddress) - tornadoContract = new web3.eth.Contract(instanceJson, tornadoInstance) - contractAddress = tornadoAddress - erc20 = currency !== netSymbol.toLowerCase() ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : {} - erc20Address = tokenAddress + tornado = new web3.eth.Contract(contractJson, tornadoAddress); + tornadoContract = new web3.eth.Contract(instanceJson, tornadoInstance); + contractAddress = tornadoAddress; + erc20 = currency !== netSymbol.toLowerCase() ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : {}; + erc20Address = tokenAddress; } async function main() { if (inBrowser) { - const instance = { currency: 'eth', amount: '0.1' } - await init(instance) + const instance = { currency: 'eth', amount: '0.1' }; + await init(instance); window.deposit = async () => { - await deposit(instance) + await deposit(instance); } window.withdraw = async () => { - const noteString = prompt('Enter the note to withdraw') - const recipient = (await web3.eth.getAccounts())[0] + const noteString = prompt('Enter the note to withdraw'); + const recipient = (await web3.eth.getAccounts())[0]; - const { currency, amount, netId, deposit } = parseNote(noteString) - await init({ noteNetId: netId, currency, amount }) - await withdraw({ deposit, currency, amount, recipient }) + const { currency, amount, netId, deposit } = parseNote(noteString); + await init({ noteNetId: netId, currency, amount }); + await withdraw({ deposit, currency, amount, recipient }); } } else { program .option('-r, --rpc ', 'The RPC that CLI should interact with', 'http://localhost:8545') .option('-R, --relayer ', 'Withdraw via relayer') .option('-T, --tor ', 'Optional tor port') - .option('-L, --local', 'Local Node - Does not submit signed transaction to the node') + .option('-L, --local', 'Local Node - Does not submit signed transaction to the node'); program .command('deposit ') .description( 'Submit a deposit of specified currency and amount from default eth account and return the resulting note. 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) => { - currency = currency.toLowerCase() - await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local }) - await deposit({ currency, amount }) - }) + currency = currency.toLowerCase(); + await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local }); + await deposit({ currency, amount }); + }); program .command('withdraw [ETH_purchase]') .description( 'Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.' ) .action(async (noteString, recipient, refund) => { - const { currency, amount, netId, deposit } = parseNote(noteString) - await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor, localMode: program.local }) + const { currency, amount, netId, deposit } = parseNote(noteString); + await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor, localMode: program.local }); await withdraw({ deposit, currency, @@ -1071,106 +1117,106 @@ async function main() { refund, relayerURL: program.relayer, torPort: program.tor - }) - }) + }); + }); program .command('balance [address] [token_address]') .description('Check ETH and ERC20 balance') .action(async (address, tokenAddress) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }) + await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }); if (!address && senderAccount) { - console.log("Using address",senderAccount,"from private key") + console.log("Using address", senderAccount, "from private key"); address = senderAccount; } - await printETHBalance({ address, name: 'Account' }) + await printETHBalance({ address, name: 'Account' }); if (tokenAddress) { - await printERC20Balance({ address, name: 'Account', tokenAddress }) + await printERC20Balance({ address, name: 'Account', tokenAddress }); } - }) + }); program .command('send
[amount] [token_address]') .description('Send ETH or ERC to address') .action(async (address, amount, tokenAddress) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true, localMode: program.local }) - await send({ address, amount, tokenAddress }) - }) + await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true, localMode: program.local }); + await send({ address, amount, tokenAddress }); + }); program .command('broadcast ') .description('Submit signed TX to the remote node') .action(async (signedTX) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }) - await submitTransaction(signedTX) - }) + await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }); + await submitTransaction(signedTX); + }); program .command('compliance ') .description( 'Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.' ) .action(async (noteString) => { - const { currency, amount, netId, deposit } = parseNote(noteString) - await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor }) - const depositInfo = await loadDepositData({ amount, currency, deposit }) - const depositDate = new Date(depositInfo.timestamp * 1000) - console.log('\n=============Deposit=================') - console.log('Deposit :', amount, currency.toUpperCase()) - console.log('Date :', depositDate.toLocaleDateString(), depositDate.toLocaleTimeString()) - console.log('From :', `https://${getExplorerLink()}/address/${depositInfo.from}`) - console.log('Transaction :', `https://${getExplorerLink()}/tx/${depositInfo.txHash}`) - console.log('Commitment :', depositInfo.commitment) - console.log('Spent :', depositInfo.isSpent) + const { currency, amount, netId, deposit } = parseNote(noteString); + await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor }); + const depositInfo = await loadDepositData({ amount, currency, deposit }); + const depositDate = new Date(depositInfo.timestamp * 1000); + console.log('\n=============Deposit================='); + console.log('Deposit :', amount, currency.toUpperCase()); + console.log('Date :', depositDate.toLocaleDateString(), depositDate.toLocaleTimeString()); + console.log('From :', `https://${getExplorerLink()}/address/${depositInfo.from}`); + console.log('Transaction :', `https://${getExplorerLink()}/tx/${depositInfo.txHash}`); + console.log('Commitment :', depositInfo.commitment); + console.log('Spent :', depositInfo.isSpent); if (!depositInfo.isSpent) { - console.log('The note was not spent') - return + console.log('The note was not spent'); + return; } - console.log('=====================================','\n') + console.log('=====================================', '\n'); - const withdrawInfo = await loadWithdrawalData({ amount, currency, deposit }) - const withdrawalDate = new Date(withdrawInfo.timestamp * 1000) - console.log('\n=============Withdrawal==============') - console.log('Withdrawal :', withdrawInfo.amount, currency) - console.log('Relayer Fee :', withdrawInfo.fee, currency) - console.log('Date :', withdrawalDate.toLocaleDateString(), withdrawalDate.toLocaleTimeString()) - console.log('To :', `https://${getExplorerLink()}/address/${withdrawInfo.to}`) - console.log('Transaction :', `https://${getExplorerLink()}/tx/${withdrawInfo.txHash}`) - console.log('Nullifier :', withdrawInfo.nullifier) - console.log('=====================================','\n') - }) + const withdrawInfo = await loadWithdrawalData({ amount, currency, deposit }); + const withdrawalDate = new Date(withdrawInfo.timestamp * 1000); + console.log('\n=============Withdrawal=============='); + console.log('Withdrawal :', withdrawInfo.amount, currency); + console.log('Relayer Fee :', withdrawInfo.fee, currency); + console.log('Date :', withdrawalDate.toLocaleDateString(), withdrawalDate.toLocaleTimeString()); + console.log('To :', `https://${getExplorerLink()}/address/${withdrawInfo.to}`); + console.log('Transaction :', `https://${getExplorerLink()}/tx/${withdrawInfo.txHash}`); + console.log('Nullifier :', withdrawInfo.nullifier); + console.log('=====================================', '\n'); + }); program .command('syncEvents ') .description( 'Sync the local cache file of deposit / withdrawal events for specific currency.' ) .action(async (type, currency, amount) => { - console.log("Starting event sync command") - currency = currency.toLowerCase() - await init({ rpc: program.rpc, type, currency, amount, torPort: program.tor }) - const cachedEvents = await fetchEvents({ type, currency, amount }) - console.log("Synced event for",type,amount,currency.toUpperCase(),netName,"Tornado instance to block",cachedEvents[cachedEvents.length - 1].blockNumber) - }) + console.log("Starting event sync command"); + currency = currency.toLowerCase(); + await init({ rpc: program.rpc, type, currency, amount, torPort: program.tor }); + const cachedEvents = await fetchEvents({ type, currency, amount }); + console.log("Synced event for", type, amount, currency.toUpperCase(), netName, "Tornado instance to block", cachedEvents[cachedEvents.length - 1].blockNumber); + }); program .command('test') .description('Perform an automated test. It deposits and withdraws one ETH and one ERC20 note. Uses ganache.') .action(async () => { - console.log('Start performing ETH deposit-withdraw test') - let currency = 'eth' - let amount = '0.1' - await init({ rpc: program.rpc, currency, amount }) - let noteString = await deposit({ currency, amount }) - let parsedNote = parseNote(noteString) + console.log('Start performing ETH deposit-withdraw test'); + let currency = 'eth'; + let amount = '0.1'; + await init({ rpc: program.rpc, currency, amount }); + let noteString = await deposit({ currency, amount }); + let parsedNote = parseNote(noteString); await withdraw({ deposit: parsedNote.deposit, currency, amount, recipient: senderAccount, relayerURL: program.relayer - }) + }); - console.log('\nStart performing DAI deposit-withdraw test') - currency = 'dai' - amount = '100' - await init({ rpc: program.rpc, currency, amount }) - noteString = await deposit({ currency, amount }) - parsedNote = parseNote(noteString) + console.log('\nStart performing DAI deposit-withdraw test'); + currency = 'dai'; + amount = '100'; + await init({ rpc: program.rpc, currency, amount }); + noteString = await deposit({ currency, amount }); + parsedNote = parseNote(noteString); await withdraw({ deposit: parsedNote.deposit, currency, @@ -1178,16 +1224,16 @@ async function main() { recipient: senderAccount, refund: '0.02', relayerURL: program.relayer - }) - }) + }); + }); try { - await program.parseAsync(process.argv) - process.exit(0) + await program.parseAsync(process.argv); + process.exit(0); } catch (e) { - console.log('Error:', e) - process.exit(1) + console.log('Error:', e); + process.exit(1); } } } -main() +main(); diff --git a/config.js b/config.js index 16a4578..677b1b0 100644 --- a/config.js +++ b/config.js @@ -115,7 +115,8 @@ module.exports = { 'decimals': 8, 'gasLimit': '85000' }, - proxy: '0x722122dF12D4e14e13Ac3b6895a86e84145b6967', + proxy: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b', + multicall: '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441', }, netId5: { 'eth': { @@ -231,6 +232,7 @@ module.exports = { 'gasLimit': '85000' }, proxy: '0x454d870a72e29d5e5697f635128d18077bd04c60', + multicall: '0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e', }, netId56: { 'bnb': { @@ -251,6 +253,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0x41263cBA59EB80dC200F3E2544eda4ed6A90E76C', }, netId100: { 'xdai': { @@ -271,6 +274,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0xb5b692a88BDFc81ca69dcB1d924f59f0413A602a', }, netId137: { 'matic': { @@ -291,6 +295,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507', }, netId42161: { 'eth': { @@ -311,6 +316,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0xB064Fe785d8131653eE12f3581F9A55F6D6E1ca3', }, netId43114: { 'avax': { @@ -329,6 +335,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0x98e2060F672FD1656a07bc12D7253b5e41bF3876', }, netId10: { 'eth': { @@ -349,6 +356,7 @@ module.exports = { 'decimals': 18 }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', + multicall: '0x142E2FEaC30d7fc3b61f9EE85FCCad8e560154cc', }, } } From cf988cc03385ec17273c6fa0af93b826912c90a7 Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:33 +0900 Subject: [PATCH 3/6] Exclude private ip addresses from remote ip checks --- cli.js | 28 ++++++++++++++++++---------- package-lock.json | 36 ++++++++++++++++++++++++++++++++++++ package.json | 3 ++- yarn.lock | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 11 deletions(-) diff --git a/cli.js b/cli.js index ef0ad80..20ba7d6 100644 --- a/cli.js +++ b/cli.js @@ -20,8 +20,9 @@ const config = require('./config'); const program = require('commander'); const { GasPriceOracle } = require('gas-price-oracle'); const SocksProxyAgent = require('socks-proxy-agent'); +const is_ip_private = require('private-ip'); -let web3, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, isLocalNode, multiCall; +let web3, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, doNotSubmitTx, multiCall, privateRpc; let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY; /** Whether we are in a browser or node.js */ @@ -147,7 +148,7 @@ async function generateTransaction(to, encodedData, value = 0) { } const tx = txoptions(); const signed = await web3.eth.accounts.signTransaction(tx, PRIVATE_KEY); - if (!isLocalNode) { + if (!doNotSubmitTx) { await submitTransaction(signed.rawTransaction); } else { console.log('\n=============Raw TX=================', '\n'); @@ -950,7 +951,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) { * Init web3, contracts, and snark */ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, balanceCheck, localMode }) { - let contractJson, instanceJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress + let contractJson, instanceJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress; // TODO do we need this? should it work in browser really? if (inBrowser) { // Initialize using injected web3 (Metamask) @@ -991,12 +992,19 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, console.log("Connecting to remote node"); web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }); } - try { - const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions) - const { country, ip } = fetchRemoteIP.data - console.log('Your remote IP address is', ip, 'from', country + '.'); - } catch (error) { - console.error('Could not fetch remote IP from ip.tornado.cash, use VPN if the problem repeats.'); + const rpcHost = new URL(rpc).hostname; + const isIpPrivate = is_ip_private(rpcHost); + if (!isIpPrivate && !rpc.includes("localhost")) { + try { + const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions); + const { country, ip } = fetchRemoteIP.data; + console.log('Your remote IP address is', ip, 'from', country + '.'); + } catch (error) { + console.error('Could not fetch remote IP from ip.tornado.cash, use VPN if the problem repeats.'); + } + } else if (isIpPrivate || rpc.includes("localhost")) { + console.log('Local RPC detected'); + privateRpc = true; } contractJson = require('./build/contracts/TornadoProxy.abi.json'); instanceJson = require('./build/contracts/Instance.abi.json'); @@ -1035,7 +1043,7 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, } if (localMode) { console.log("Local mode detected: will not submit signed TX to remote node"); - isLocalNode = true; + doNotSubmitTx = true; } if (isTestRPC) { diff --git a/package-lock.json b/package-lock.json index 8788f83..f40cadb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3139,6 +3139,11 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3221,6 +3226,14 @@ "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "requires": { + "ip-regex": "^4.0.0" + } + }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -3679,6 +3692,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -3985,6 +4003,24 @@ "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" }, + "private-ip": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/private-ip/-/private-ip-2.3.3.tgz", + "integrity": "sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==", + "requires": { + "ip-regex": "^4.3.0", + "ipaddr.js": "^2.0.1", + "is-ip": "^3.1.0", + "netmask": "^2.0.2" + }, + "dependencies": { + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + } + } + }, "process": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", diff --git a/package.json b/package.json index 590051a..387758c 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,9 @@ "dotenv": "^8.2.0", "fixed-merkle-tree": "^0.6.1", "gas-price-oracle": "^0.4.4", - "socks-proxy-agent": "^6.1.1", + "private-ip": "^2.3.3", "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", + "socks-proxy-agent": "^6.1.1", "web3": "^1.6.1", "web3-providers-http": "file:local_modules/web3-providers-http", "websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d" diff --git a/yarn.lock b/yarn.lock index 5f19cd1..42ed4f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2155,6 +2155,11 @@ invert-kv@^2.0.0: resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip-regex@^4.0.0, ip-regex@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -2165,6 +2170,11 @@ ipaddr.js@1.9.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-arguments@^1.0.4: version "1.1.0" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" @@ -2226,6 +2236,13 @@ is-hex-prefixed@1.0.0: resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= +is-ip@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== + dependencies: + ip-regex "^4.0.0" + is-negative-zero@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -2685,6 +2702,11 @@ negotiator@0.6.2: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + next-tick@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -2989,6 +3011,16 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== +private-ip@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/private-ip/-/private-ip-2.3.3.tgz#1e80ff8443e5ac78f555631aec3ea6ff027fa6aa" + integrity sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw== + dependencies: + ip-regex "^4.3.0" + ipaddr.js "^2.0.1" + is-ip "^3.1.0" + netmask "^2.0.2" + process@^0.11.10: version "0.11.10" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" From 378bab8fbe0a101585170433dea7303f54b8f130 Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:35 +0900 Subject: [PATCH 4/6] Use TheGraph while syncing events - support fallback to web3 when not available - fixes #38 --- cli.js | 200 +++++++++++++++++++++++++++++++++++++++++++++++++----- config.js | 8 +++ 2 files changed, 190 insertions(+), 18 deletions(-) diff --git a/cli.js b/cli.js index 20ba7d6..23a481c 100644 --- a/cli.js +++ b/cli.js @@ -22,7 +22,7 @@ const { GasPriceOracle } = require('gas-price-oracle'); const SocksProxyAgent = require('socks-proxy-agent'); const is_ip_private = require('private-ip'); -let web3, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, doNotSubmitTx, multiCall, privateRpc; +let web3, torPort, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, doNotSubmitTx, multiCall, privateRpc, subgraph; let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY; /** Whether we are in a browser or node.js */ @@ -326,7 +326,7 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr * @param noteString Note to withdraw * @param recipient Recipient address */ -async function withdraw({ deposit, currency, amount, recipient, relayerURL, torPort, refund = '0' }) { +async function withdraw({ deposit, currency, amount, recipient, relayerURL, refund = '0' }) { let options = {}; if (currency === netSymbol.toLowerCase() && refund !== '0') { throw new Error('The ETH purchase is supposted to be 0 for ETH withdrawals'); @@ -764,7 +764,7 @@ function loadCachedEvents({ type, currency, amount }) { } } -async function fetchEvents({ type, currency, amount}) { +async function fetchEvents({ type, currency, amount }) { if (type === "withdraw") { type = "withdrawal"; } @@ -779,6 +779,7 @@ async function fetchEvents({ type, currency, amount}) { try { let targetBlock = await web3.eth.getBlockNumber(); let chunks = 1000; + console.log("Querying latest events from RPC"); for (let i = startBlock; i < targetBlock; i += chunks) { let fetchedEvents = []; @@ -817,18 +818,17 @@ async function fetchEvents({ type, currency, amount}) { } } - async function fetchLatestEvents(i) { + async function fetchWeb3Events(i) { let j; if (i + chunks - 1 > targetBlock) { j = targetBlock; } else { j = i + chunks - 1; } - await tornadoContract.getPastEvents(capitalizeFirstLetter(type), { fromBlock: i, toBlock: j, - }).then(r => { fetchedEvents = fetchedEvents.concat(r); console.log("Fetched",amount,currency.toUpperCase(),type,"events to block:", j) }, err => { console.error(i + " failed fetching",type,"events from node", err); process.exit(1); }).catch(console.log); + }).then(r => { fetchedEvents = fetchedEvents.concat(r); console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", j) }, err => { console.error(i + " failed fetching", type, "events from node", err); process.exit(1); }).catch(console.log); if (type === "deposit"){ mapDepositEvents(); @@ -847,7 +847,7 @@ async function fetchEvents({ type, currency, amount}) { throw new Error('Writing cache file failed:',error); } } - await fetchLatestEvents(i); + await fetchWeb3Events(i); await updateCache(); } } catch (error) { @@ -855,7 +855,164 @@ async function fetchEvents({ type, currency, amount}) { process.exit(1); } } - await syncEvents(); + + async function syncGraphEvents() { + let options = {}; + if (torPort) { + options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } }; + } + + async function queryLatestTimestamp() { + try { + const variables = { + currency: currency.toString(), + amount: amount.toString() + } + if (type === "deposit") { + const query = { + query: ` + query($currency: String, $amount: String){ + deposits(first: 1, orderBy: timestamp, orderDirection: desc, where: {currency: $currency, amount: $amount}) { + timestamp + } + } + `, + variables + } + const querySubgraph = await axios.post(subgraph, query, options); + const queryResult = querySubgraph.data.data.deposits; + const result = queryResult[0].timestamp; + return Number(result); + } else { + const query = { + query: ` + query($currency: String, $amount: String){ + withdrawals(first: 1, orderBy: timestamp, orderDirection: desc, where: {currency: $currency, amount: $amount}) { + timestamp + } + } + `, + variables + } + const querySubgraph = await axios.post(subgraph, query, options); + const queryResult = querySubgraph.data.data.withdrawals; + const result = queryResult[0].timestamp; + return Number(result); + } + } catch (error) { + console.error("Failed to fetch latest event from thegraph"); + } + } + + async function queryFromGraph(timestamp) { + try { + const variables = { + currency: currency.toString(), + amount: amount.toString(), + timestamp: timestamp + } + if (type === "deposit") { + const query = { + query: ` + query($currency: String, $amount: String, $timestamp: Int){ + deposits(orderBy: timestamp, first: 1000, where: {currency: $currency, amount: $amount, timestamp_gt: $timestamp}) { + blockNumber + transactionHash + commitment + index + timestamp + } + } + `, + variables + } + const querySubgraph = await axios.post(subgraph, query, options); + const queryResult = querySubgraph.data.data.deposits; + const mapResult = queryResult.map(({ blockNumber, transactionHash, commitment, index, timestamp }) => { + return { + blockNumber: Number(blockNumber), + transactionHash, + commitment, + leafIndex: Number(index), + timestamp + } + }); + return mapResult; + } else { + const query = { + query: ` + query($currency: String, $amount: String, $timestamp: Int){ + withdrawals(orderBy: timestamp, first: 1000, where: {currency: $currency, amount: $amount, timestamp_gt: $timestamp}) { + blockNumber + transactionHash + nullifier + to + fee + } + } + `, + variables + } + const querySubgraph = await axios.post(subgraph, query, options); + const queryResult = querySubgraph.data.data.withdrawals; + const mapResult = queryResult.map(({ blockNumber, transactionHash, nullifier, to, fee }) => { + return { + blockNumber: Number(blockNumber), + transactionHash, + nullifierHash: nullifier, + to, + fee + } + }); + return mapResult; + } + } catch (error) { + console.error(error); + } + } + + async function updateCache(fetchedEvents) { + try { + const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`; + const localEvents = await initJson(fileName); + const events = localEvents.concat(fetchedEvents); + await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8'); + } catch (error) { + throw new Error('Writing cache file failed:',error); + } + } + + async function fetchGraphEvents() { + console.log("Querying latest events from TheGraph"); + const latestTimestamp = await queryLatestTimestamp(); + if (latestTimestamp) { + const getCachedBlock = await web3.eth.getBlock(startBlock); + const cachedTimestamp = getCachedBlock.timestamp; + for (let i = cachedTimestamp; i < latestTimestamp;) { + const result = await queryFromGraph(i); + if (Object.keys(result).length === 0) { + i = latestTimestamp; + } else { + const resultBlock = result[result.length - 1].blockNumber; + const getResultBlock = await web3.eth.getBlock(resultBlock); + const resultTimestamp = getResultBlock.timestamp; + await updateCache(result); + i = resultTimestamp; + console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock)); + } + } + } else { + console.log("Fallback to web3 events"); + await syncEvents(); + } + } + await fetchGraphEvents(); + } + if (!privateRpc || !subgraph || !isTestRPC) { + await syncGraphEvents(); + } else { + await syncEvents(); + } async function loadUpdatedEvents() { const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`; @@ -950,7 +1107,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) { /** * Init web3, contracts, and snark */ -async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, balanceCheck, localMode }) { +async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceCheck, localMode }) { let contractJson, instanceJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress; // TODO do we need this? should it work in browser really? if (inBrowser) { @@ -1059,6 +1216,7 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort, } tornadoAddress = config.deployments[`netId${netId}`].proxy; multiCall = config.deployments[`netId${netId}`].multicall; + subgraph = config.deployments[`netId${netId}`].subgraph; tornadoInstance = config.deployments[`netId${netId}`][currency].instanceAddress[amount]; deployedBlockNumber = config.deployments[`netId${netId}`][currency].deployedBlockNumber[amount]; @@ -1106,7 +1264,8 @@ async function main() { ) .action(async (currency, amount) => { currency = currency.toLowerCase(); - await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local }); + torPort = program.tor; + await init({ rpc: program.rpc, currency, amount, localMode: program.local }); await deposit({ currency, amount }); }); program @@ -1116,22 +1275,23 @@ async function main() { ) .action(async (noteString, recipient, refund) => { const { currency, amount, netId, deposit } = parseNote(noteString); - await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor, localMode: program.local }); + torPort = program.tor; + await init({ rpc: program.rpc, noteNetId: netId, currency, amount, localMode: program.local }); await withdraw({ deposit, currency, amount, recipient, refund, - relayerURL: program.relayer, - torPort: program.tor + relayerURL: program.relayer }); }); program .command('balance [address] [token_address]') .description('Check ETH and ERC20 balance') .action(async (address, tokenAddress) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }); + torPort = program.tor; + await init({ rpc: program.rpc, balanceCheck: true }); if (!address && senderAccount) { console.log("Using address", senderAccount, "from private key"); address = senderAccount; @@ -1145,14 +1305,16 @@ async function main() { .command('send
[amount] [token_address]') .description('Send ETH or ERC to address') .action(async (address, amount, tokenAddress) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true, localMode: program.local }); + torPort = program.tor; + await init({ rpc: program.rpc, balanceCheck: true, localMode: program.local }); await send({ address, amount, tokenAddress }); }); program .command('broadcast ') .description('Submit signed TX to the remote node') .action(async (signedTX) => { - await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true }); + torPort = program.tor; + await init({ rpc: program.rpc, balanceCheck: true }); await submitTransaction(signedTX); }); program @@ -1162,7 +1324,8 @@ async function main() { ) .action(async (noteString) => { const { currency, amount, netId, deposit } = parseNote(noteString); - await init({ rpc: program.rpc, noteNetId: netId, currency, amount, torPort: program.tor }); + torPort = program.tor; + await init({ rpc: program.rpc, noteNetId: netId, currency, amount }); const depositInfo = await loadDepositData({ amount, currency, deposit }); const depositDate = new Date(depositInfo.timestamp * 1000); console.log('\n=============Deposit================='); @@ -1197,7 +1360,8 @@ async function main() { .action(async (type, currency, amount) => { console.log("Starting event sync command"); currency = currency.toLowerCase(); - await init({ rpc: program.rpc, type, currency, amount, torPort: program.tor }); + torPort = program.tor; + await init({ rpc: program.rpc, type, currency, amount }); const cachedEvents = await fetchEvents({ type, currency, amount }); console.log("Synced event for", type, amount, currency.toUpperCase(), netName, "Tornado instance to block", cachedEvents[cachedEvents.length - 1].blockNumber); }); diff --git a/config.js b/config.js index 677b1b0..536ce2d 100644 --- a/config.js +++ b/config.js @@ -117,6 +117,7 @@ module.exports = { }, proxy: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b', multicall: '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/mainnet-tornado-subgraph', }, netId5: { 'eth': { @@ -233,6 +234,7 @@ module.exports = { }, proxy: '0x454d870a72e29d5e5697f635128d18077bd04c60', multicall: '0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/goerli-tornado-subgraph', }, netId56: { 'bnb': { @@ -254,6 +256,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0x41263cBA59EB80dC200F3E2544eda4ed6A90E76C', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/bsc-tornado-subgraph', }, netId100: { 'xdai': { @@ -275,6 +278,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0xb5b692a88BDFc81ca69dcB1d924f59f0413A602a', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/xdai-tornado-subgraph', }, netId137: { 'matic': { @@ -296,6 +300,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/matic-tornado-subgraph', }, netId42161: { 'eth': { @@ -317,6 +322,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0xB064Fe785d8131653eE12f3581F9A55F6D6E1ca3', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/arbitrum-tornado-subgraph', }, netId43114: { 'avax': { @@ -336,6 +342,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0x98e2060F672FD1656a07bc12D7253b5e41bF3876', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/avalanche-tornado-subgraph', }, netId10: { 'eth': { @@ -357,6 +364,7 @@ module.exports = { }, proxy: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', multicall: '0x142E2FEaC30d7fc3b61f9EE85FCCad8e560154cc', + subgraph: 'https://api.thegraph.com/subgraphs/name/tornadocash/optimism-tornado-subgraph', }, } } From 2854b7a12f683abc2f7c985b08da24c460097f41 Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:37 +0900 Subject: [PATCH 5/6] Support creating deposit notes offline Co-authored-by: yoyoismee --- .gitignore | 3 +- README.md | 40 +++++++++++++++++++++ cli.js | 103 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 137 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e68e0db..5d605ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules .env .idea -backup-tornado-* \ No newline at end of file +backup-tornado-* +backup-tornadoInvoice-* diff --git a/README.md b/README.md index 494d8a2..7094498 100644 --- a/README.md +++ b/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) diff --git a/cli.js b/cli.js index 23a481c..164c484 100644 --- a/cli.js +++ b/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-(?\w+)-(?[\d.]+)-(?\d+)-0x(?[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 ', 'Withdraw via relayer') .option('-T, --tor ', 'Optional tor port') .option('-L, --local', 'Local Node - Does not submit signed transaction to the node'); + program + .command('createNote ') + .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 ') + .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 ') .description( From c185961a624cd1d7e139c63d1600fd2ae17b76f4 Mon Sep 17 00:00:00 2001 From: Ayanami Date: Sun, 27 Feb 2022 07:32:39 +0900 Subject: [PATCH 6/6] Add --onlyrpc mode + Add mode to disable remote ip connection and to disable thegraph api + Faster event sync for deposit + Update README guide for users --- README.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++----- cli.js | 62 +++++++++++++++++++++++++++++++++---------- 2 files changed, 120 insertions(+), 21 deletions(-) mode change 100644 => 100755 cli.js diff --git a/README.md b/README.md index 7094498..b6e8872 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ 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 Windows: https://stackoverflow.com/a/64224475 -For MacOS: Install XCode Command Line Tools +- For MacOS: Install XCode Command Line Tools -For Linux: Install make & gcc, for ubuntu `$ sudo apt-get install -y build-essentials` +- 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. @@ -33,14 +33,26 @@ $ cd tornado-cli $ npm install ``` -If you want to use Tor connection to conceal ip address, install [Tor Browser](https://www.torproject.org/download/) and add `--tor 9150` for `cli.js` if you connect tor with browser. +If you want to use Tor connection to conceal ip address, install [Tor Browser](https://www.torproject.org/download/) and add `--tor 9150` for `cli.js` if you connect tor with browser. (For non tor-browser tor service you can use the default 9050 port). + Note that you should reset your tor connection by restarting the browser every time when you deposit & withdraw otherwise you will have the same exit node used for connection. ### Goerli, Mainnet, Binance Smart Chain, Gnosis Chain, Polygon Network, Arbitrum, Avalanche 1. Add `PRIVATE_KEY` to `.env` file 2. `node cli.js --help` +3. If you want to use secure, anonymous tor connection add `--tor ` behind the command. -Example: +#### To deposit: + +```bash +$ node cli.js deposit --rpc --tor +``` + +Note that `--tor ` is optional. + +For RPC nodes please refer to the list of public RPC nodes below. + +##### Example: ```bash $ node cli.js deposit ETH 0.1 --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --tor 9150 @@ -52,6 +64,22 @@ Tornado ETH balance is 9 Sender account ETH balance is 1004873.361652048361352542 ``` +#### To withdraw: + +```bash +$ node cli.js withdraw --rpc --relayer --tor +``` + +Note that `--relayer `, `--tor ` is optional. + +If you want to use Tornado Cash relayer for your first withdrawal to your new ethereum account, please refer to the list of relayers below. + +If you don't need relayer while doing withdrawals, you must apply your withdrawal account's private key to `.env` file. + +Copy the `PRIVATE_KEY=` line of `.env.example` to `.env`, and add your private key behind the `=`. + +##### Example: + ```bash $ node cli.js withdraw tornado-eth-0.1-5-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652 0x8589427373D6D84E98730D7795D8f6f8731FDA16 --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --relayer https://goerli-frelay.duckdns.org --tor 9150 @@ -70,7 +98,15 @@ One of the main features of tornado-cli is that it supports creating deposit not 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. +#### To create deposit notes with `createNote` command. + +```bash +$ node cli.js createNote +``` + +To find out chainId value for your network, refer to https://chainlist.org/. + +##### Example: ```bash $ node cli.js createNote ETH 0.1 5 @@ -80,7 +116,15 @@ 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. +#### To create corresponding deposit transaction with `depositInvoice` command. + +Creating deposit transaction with `depositInvoice` only requires valid deposit note created by `createNote` command, so that the deposit note could be stored without exposed anywhere. + +```bash +$ node cli.js depositInvoice +``` + +##### Example: ```bash node cli.js depositInvoice tornadoInvoice-eth-0.1-5-0x1b680c7dda0c2dd1b85f0fe126d49b16ed594b3cd6d5114db5f4593877a6b84f --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --tor 9150 @@ -97,6 +141,27 @@ Tornado contract balance is xxx.x ETH Sender account balance is x.xxxxxxx ETH ``` +#### To withdraw, you will need deposit note that matches with your deposit transaction. + +```bash +$ node cli.js withdraw +``` + +##### Example: + +```bash +$ node cli.js withdraw tornado-eth-0.1-5-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652 0x8589427373D6D84E98730D7795D8f6f8731FDA16 --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161 --relayer https://goerli-frelay.duckdns.org --tor 9150 + +Relay address: 0x6A31736e7490AbE5D5676be059DFf064AB4aC754 +Getting current state from tornado contract +Generating SNARK proof +Proof time: 9117.051ms +Sending withdraw transaction through relay +Transaction submitted through the relay. View transaction on etherscan https://goerli.etherscan.io/tx/0xcb21ae8cad723818c6bc7273e83e00c8393fcdbe74802ce5d562acad691a2a7b +Transaction mined in block 17036120 +Done +``` + ### List of public rpc & relayers for withdrawal Infura API key fetched from https://rpc.info (Same one with Metamask) diff --git a/cli.js b/cli.js old mode 100644 new mode 100755 index 164c484..d41e36d --- a/cli.js +++ b/cli.js @@ -65,7 +65,7 @@ async function printERC20Balance({ address, name, tokenAddress }) { let tokenDecimals, tokenBalance, tokenName, tokenSymbol; const erc20ContractJson = require('./build/contracts/ERC20Mock.json'); erc20 = tokenAddress ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : erc20; - if (!isTestRPC || !multiCall) { + if (!isTestRPC && !multiCall) { const tokenCall = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(address).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.name().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]); tokenDecimals = parseInt(tokenCall[1]); tokenBalance = new BigNumber(tokenCall[0]).div(BigNumber(10).pow(tokenDecimals)); @@ -220,7 +220,7 @@ async function createInvoice({ currency, amount, chainId }) { */ 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; + let commitment, noteString; if (!commitmentNote) { console.log("Creating new random deposit note"); const deposit = createDeposit({ @@ -228,7 +228,7 @@ async function deposit({ currency, amount, commitmentNote }) { secret: rbigint(31) }); const note = toHex(deposit.preimage, 62); - const noteString = `tornado-${currency}-${amount}-${netId}-${note}`; + noteString = `tornado-${currency}-${amount}-${netId}-${note}`; console.log(`Your note: ${noteString}`); await backupNote({ currency, amount, netId, note, noteString }); commitment = toHex(deposit.commitment); @@ -300,7 +300,7 @@ async function generateMerkleProof(deposit, currency, amount) { // Validate that our data is correct const root = tree.root(); let isValidRoot, isSpent; - if (!isTestRPC || !multiCall) { + if (!isTestRPC && !multiCall) { const callContract = await useMultiCall([[tornadoContract._address, tornadoContract.methods.isKnownRoot(toHex(root)).encodeABI()], [tornadoContract._address, tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).encodeABI()]]) isValidRoot = web3.eth.abi.decodeParameter('bool', callContract[0]); isSpent = web3.eth.abi.decodeParameter('bool', callContract[1]); @@ -459,7 +459,7 @@ async function send({ address, amount, tokenAddress }) { const erc20ContractJson = require('./build/contracts/ERC20Mock.json'); erc20 = new web3.eth.Contract(erc20ContractJson.abi, tokenAddress); let tokenBalance, tokenDecimals, tokenSymbol; - if (!isTestRPC || !multiCall) { + if (!isTestRPC && !multiCall) { const callToken = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(senderAccount).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]); tokenBalance = new BigNumber(callToken[0]); tokenDecimals = parseInt(callToken[1]); @@ -1036,12 +1036,20 @@ async function fetchEvents({ type, currency, amount }) { if (Object.keys(result).length === 0) { i = latestTimestamp; } else { - const resultBlock = result[result.length - 1].blockNumber; - const getResultBlock = await web3.eth.getBlock(resultBlock); - const resultTimestamp = getResultBlock.timestamp; - await updateCache(result); - i = resultTimestamp; - console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock)); + if (type === "deposit") { + const resultBlock = result[result.length - 1].blockNumber; + const resultTimestamp = result[result.length - 1].timestamp; + await updateCache(result); + i = resultTimestamp; + console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock)); + } else { + const resultBlock = result[result.length - 1].blockNumber; + const getResultBlock = await web3.eth.getBlock(resultBlock); + const resultTimestamp = getResultBlock.timestamp; + await updateCache(result); + i = resultTimestamp; + console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock)); + } } } } else { @@ -1051,7 +1059,7 @@ async function fetchEvents({ type, currency, amount }) { } await fetchGraphEvents(); } - if (!privateRpc || !subgraph || !isTestRPC) { + if (!privateRpc && !subgraph && !isTestRPC) { await syncGraphEvents(); } else { await syncEvents(); @@ -1217,7 +1225,7 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC } const rpcHost = new URL(rpc).hostname; const isIpPrivate = is_ip_private(rpcHost); - if (!isIpPrivate && !rpc.includes("localhost")) { + if (!isIpPrivate && !rpc.includes("localhost") && !privateRpc) { try { const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions); const { country, ip } = fetchRemoteIP.data; @@ -1322,7 +1330,8 @@ async function main() { .option('-r, --rpc ', 'The RPC that CLI should interact with', 'http://localhost:8545') .option('-R, --relayer ', 'Withdraw via relayer') .option('-T, --tor ', 'Optional tor port') - .option('-L, --local', 'Local Node - Does not submit signed transaction to the node'); + .option('-L, --local', 'Local Node - Does not submit signed transaction to the node') + .option('-o, --onlyrpc', 'Only rpc mode - Does not enable thegraph api nor remote ip detection'); program .command('createNote ') .description( @@ -1338,6 +1347,9 @@ async function main() { 'Submit a deposit of invoice from default eth account and return the resulting note.' ) .action(async (invoice) => { + if (program.onlyrpc) { + privateRpc = true; + } torPort = program.tor; const { currency, amount, netId, commitmentNote } = parseInvoice(invoice); await init({ rpc: program.rpc, currency, amount, localMode: program.local }); @@ -1350,6 +1362,9 @@ async function main() { 'Submit a deposit of specified currency and amount from default eth account and return the resulting note. 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) => { + if (program.onlyrpc) { + privateRpc = true; + } currency = currency.toLowerCase(); torPort = program.tor; await init({ rpc: program.rpc, currency, amount, localMode: program.local }); @@ -1361,6 +1376,9 @@ async function main() { 'Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.' ) .action(async (noteString, recipient, refund) => { + if (program.onlyrpc) { + privateRpc = true; + } const { currency, amount, netId, deposit } = parseNote(noteString); torPort = program.tor; await init({ rpc: program.rpc, noteNetId: netId, currency, amount, localMode: program.local }); @@ -1377,6 +1395,9 @@ async function main() { .command('balance [address] [token_address]') .description('Check ETH and ERC20 balance') .action(async (address, tokenAddress) => { + if (program.onlyrpc) { + privateRpc = true; + } torPort = program.tor; await init({ rpc: program.rpc, balanceCheck: true }); if (!address && senderAccount) { @@ -1392,6 +1413,9 @@ async function main() { .command('send
[amount] [token_address]') .description('Send ETH or ERC to address') .action(async (address, amount, tokenAddress) => { + if (program.onlyrpc) { + privateRpc = true; + } torPort = program.tor; await init({ rpc: program.rpc, balanceCheck: true, localMode: program.local }); await send({ address, amount, tokenAddress }); @@ -1400,6 +1424,9 @@ async function main() { .command('broadcast ') .description('Submit signed TX to the remote node') .action(async (signedTX) => { + if (program.onlyrpc) { + privateRpc = true; + } torPort = program.tor; await init({ rpc: program.rpc, balanceCheck: true }); await submitTransaction(signedTX); @@ -1410,6 +1437,9 @@ async function main() { 'Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.' ) .action(async (noteString) => { + if (program.onlyrpc) { + privateRpc = true; + } const { currency, amount, netId, deposit } = parseNote(noteString); torPort = program.tor; await init({ rpc: program.rpc, noteNetId: netId, currency, amount }); @@ -1445,6 +1475,9 @@ async function main() { 'Sync the local cache file of deposit / withdrawal events for specific currency.' ) .action(async (type, currency, amount) => { + if (program.onlyrpc) { + privateRpc = true; + } console.log("Starting event sync command"); currency = currency.toLowerCase(); torPort = program.tor; @@ -1456,6 +1489,7 @@ async function main() { .command('test') .description('Perform an automated test. It deposits and withdraws one ETH and one ERC20 note. Uses ganache.') .action(async () => { + privateRpc = true; console.log('Start performing ETH deposit-withdraw test'); let currency = 'eth'; let amount = '0.1';