From f9a1e18437d14b702070c8e5b67df0cda7af8419 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 13 Dec 2016 14:15:58 -0800 Subject: [PATCH] wallet: remove resolution. --- lib/wallet/txdb.js | 251 +++++------------------------------------ lib/wallet/wallet.js | 21 ---- lib/wallet/walletdb.js | 40 +------ test/wallet-test.js | 16 +-- 4 files changed, 35 insertions(+), 293 deletions(-) diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 8dd29010..0d201076 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -286,218 +286,6 @@ TXDB.prototype.hasPath = function hasPath(output) { return this.wallet.hasPath(output.getAddress()); }; -/** - * Determine which transactions to add. - * Attempt to resolve orphans (for SPV). - * @param {TX} tx - * @returns {Promise} - */ - -TXDB.prototype.resolve = co(function* resolve(tx, block) { - var orphan, hash, result; - - if (!this.options.resolution) { - orphan = new ResolvedOrphan(tx, block); - return [orphan]; - } - - hash = tx.hash('hex'); - - if (yield this.hasTX(hash)) { - orphan = new ResolvedOrphan(tx, block); - return [orphan]; - } - - result = yield this.verifyInputs(tx, block); - - if (!result) - return []; - - return yield this.resolveOutputs(tx, block); -}); - -/** - * Verify inputs and potentially add orphans. - * Used in SPV mode. - * @param {TX} tx - * @returns {Promise} - */ - -TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) { - var flags = constants.flags.MANDATORY_VERIFY_FLAGS; - var hash = tx.hash('hex'); - var hasOrphans = false; - var orphans = []; - var i, input, prevout; - var key, coin, spent; - - if (tx.isCoinbase()) - return true; - - // We already have this one. - if (this.count[hash]) - return false; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - - // If we have a coin, we can verify this right now. - coin = yield this.getCoin(prevout.hash, prevout.index); - - if (coin) { - if (this.options.verify && !block) { - if (!(yield tx.verifyInputAsync(i, coin, flags))) - return false; - } - - continue; - } - - // Check whether the input is already part of the stxo set. - spent = yield this.getSpent(prevout.hash, prevout.index); - - if (spent) { - coin = yield this.getSpentCoin(spent, prevout); - - // If we don't have an undo coin, - // this is an unknown input we - // decided to index for the hell - // of it. - if (coin) { - if (this.options.verify && !block) { - if (!(yield tx.verifyInputAsync(i, coin, flags))) - return false; - } - continue; - } - } - - // This is a HACK: try to extract an - // address from the input to tell if - // it's ours and should be regarded - // as an orphan input. - if (yield this.hasPath(input)) - orphans[i] = true; - } - - // Store orphans now that we've - // fully verified everything to - // the best of our ability. - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - - if (!orphans[i]) - continue; - - key = prevout.toKey(); - - // In theory, someone could try to DoS - // us by creating tons of fake transactions - // with our pubkey in the scriptsig. This - // is due to the hack mentioned above. Only - //allow 20 orphans at a time. - if (this.totalOrphans > 20) { - this.logger.warning('Potential orphan flood!'); - this.logger.warning( - 'More than 20 orphans for %s. Purging.', - this.wallet.id); - this.totalOrphans = 0; - this.orphans = {}; - this.count = {}; - } - - if (!this.orphans[key]) - this.orphans[key] = []; - - if (!this.count[hash]) - this.count[hash] = 0; - - this.orphans[key].push(new Orphan(tx, i, block)); - this.count[hash]++; - this.totalOrphans++; - - hasOrphans = true; - } - - // We wait til _all_ orphans are resolved - // before inserting this transaction. - if (hasOrphans) - return false; - - return true; -}); - -/** - * Resolve orphans for outputs. - * Used in SPV mode. - * @param {TX} tx - * @returns {Promise} - */ - -TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved) { - var flags = constants.flags.MANDATORY_VERIFY_FLAGS; - var hash = tx.hash('hex'); - var i, j, input, output, key; - var orphans, orphan, valid; - - if (!resolved) - resolved = []; - - // Always push the first transaction on. - // Necessary for the first resolved tx - // as well as the recursive behavior - // below. - resolved.push(new ResolvedOrphan(tx, block)); - - for (i = 0; i < tx.outputs.length; i++) { - output = tx.outputs[i]; - key = Outpoint.toKey(hash, i); - orphans = this.orphans[key]; - - if (!orphans) - continue; - - delete this.orphans[key]; - - // Note that their might be multiple - // orphans per input, either due to - // double spends or an adversary - // creating fake transactions. We - // take the first valid orphan - // transaction. - for (j = 0; j < orphans.length; j++) { - orphan = orphans[j]; - valid = true; - - input = orphan.tx.inputs[orphan.index]; - - assert(input.prevout.hash === hash); - assert(input.prevout.index === i); - - // We can finally verify this input. - if (this.options.verify && !orphan.block) - valid = yield orphan.tx.verifyInputAsync(orphan.index, output, flags); - - // If it's valid and fully resolved, - // we can resolve _its_ outputs. - if (valid) { - if (--this.count[orphan.hash] === 0) { - delete this.count[orphan.hash]; - yield this.resolveOutputs(orphan.tx, orphan.block, resolved); - } - break; - } - - // Forget about it if invalid. - delete this.count[orphan.hash]; - } - } - - return resolved; -}); - /** * Save credit. * @param {Credit} credit @@ -1042,6 +830,15 @@ TXDB.prototype.insert = co(function* insert(wtx, block) { } coin = credit.coin; + + // Do some verification. + if (!block) { + if (!(yield this.verifyInput(tx, i, coin))) { + this.clear(); + return false; + } + } + path = yield this.getPath(coin); assert(path); @@ -1147,6 +944,7 @@ TXDB.prototype.insert = co(function* insert(wtx, block) { this.put(layout.H(account, height, hash), DUMMY); } + // Update block records. if (block) { yield this.addBlockMap(hash, height); yield this.addBlock(tx.hash(), block); @@ -1455,6 +1253,7 @@ TXDB.prototype.erase = co(function* erase(wtx, block) { this.del(layout.H(account, height, hash)); } + // Update block records. if (block) { yield this.removeBlockMap(hash, height); yield this.removeBlockSlow(hash, height); @@ -1747,6 +1546,22 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, conf) { return true; }); +/** + * Attempt to verify an input. + * @private + * @param {TX} tx + * @param {Number} index + * @param {Coin} coin + * @returns {Promise} + */ + +TXDB.prototype.verifyInput = co(function* verifyInput(tx, index, coin) { + var flags = constants.flags.MANDATORY_VERIFY_FLAGS; + if (!this.options.verify) + return true; + return yield tx.verifyInputAsync(index, coin, flags); +}); + /** * Lock all coins in a transaction. * @param {TX} tx @@ -3471,18 +3286,6 @@ BlockRecord.fromMeta = function fromMeta(block) { * Helpers */ -function Orphan(tx, index, block) { - this.tx = tx; - this.hash = tx.hash('hex'); - this.index = index; - this.block = block || null; -} - -function ResolvedOrphan(tx, block) { - this.tx = tx; - this.block = block || null; -} - function cmp(a, b) { return a - b; } diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 6958754d..3a30c227 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -2186,27 +2186,6 @@ Wallet.prototype.add = co(function* add(tx, block) { */ Wallet.prototype._add = co(function* add(tx, block) { - var resolved = yield this.txdb.resolve(tx, block); - var result = false; - var i, orphan; - - for (i = 0; i < resolved.length; i++) { - orphan = resolved[i]; - if (yield this._insert(orphan.tx, orphan.block)) - result = true; - } - - return result; -}); - -/** - * Insert a transaction into the wallet (no lock). - * @private - * @param {TX} tx - * @returns {Promise} - */ - -Wallet.prototype._insert = co(function* insert(tx, block) { var details, derived; this.txdb.start(); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 7310754f..bf50a067 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -1495,38 +1495,6 @@ WalletDB.prototype.resend = co(function* resend() { yield this.send(txs[i]); }); -/** - * Insert all wallet ids for an input - * (special behavior for SPV). - * @private - * @param {Input} input - * @param {WalletID[]} result - * @returns {Promise} - */ - -WalletDB.prototype.testInput = co(function* testInput(input, result) { - var i, hash, map; - - if (!this.options.resolution) - return; - - hash = input.getHash('hex'); - - if (!hash) - return; - - if (!this.testFilter(hash)) - return; - - map = yield this.getPathMap(hash); - - if (!map) - return; - - for (i = 0; i < map.wids.length; i++) - util.binaryInsert(result, map.wids[i], cmp, true); -}); - /** * Get all wallet ids by output addresses and outpoints. * @param {Hash[]} hashes @@ -1543,17 +1511,13 @@ WalletDB.prototype.getWalletsByTX = co(function* getWalletsByTX(tx) { input = tx.inputs[i]; prevout = input.prevout; - if (!this.testFilter(prevout.toRaw())) { - yield this.testInput(input, result); + if (!this.testFilter(prevout.toRaw())) continue; - } map = yield this.getOutpointMap(prevout.hash, prevout.index); - if (!map) { - yield this.testInput(input, result); + if (!map) continue; - } for (j = 0; j < map.wids.length; j++) util.binaryInsert(result, map.wids[j], cmp, true); diff --git a/test/wallet-test.js b/test/wallet-test.js index 02283b5b..fbc7afac 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -66,7 +66,6 @@ describe('Wallet', function() { walletdb = new WalletDB({ name: 'wallet-test', db: 'memory', - resolution: true, verify: true }); @@ -273,20 +272,20 @@ describe('Wallet', function() { yield walletdb.addTX(t4); balance = yield w.getBalance(); - //assert.equal(balance.unconfirmed, 22500); - assert.equal(balance.unconfirmed, 0); + assert.equal(balance.unconfirmed, 22500); + // assert.equal(balance.unconfirmed, 0); yield walletdb.addTX(t1); balance = yield w.getBalance(); - //assert.equal(balance.unconfirmed, 73000); - assert.equal(balance.unconfirmed, 51000); + assert.equal(balance.unconfirmed, 72500); + // assert.equal(balance.unconfirmed, 51000); yield walletdb.addTX(t2); balance = yield w.getBalance(); - //assert.equal(balance.unconfirmed, 47000); - assert.equal(balance.unconfirmed, 49000); + assert.equal(balance.unconfirmed, 46500); + // assert.equal(balance.unconfirmed, 49000); yield walletdb.addTX(t3); @@ -353,7 +352,6 @@ describe('Wallet', function() { walletdb = new WalletDB({ name: 'wallet-test', db: 'memory', - resolution: false, verify: false }); @@ -1209,7 +1207,6 @@ describe('Wallet', function() { walletdb = new WalletDB({ name: 'wallet-test', db: 'memory', - resolution: false, verify: false }); @@ -1276,7 +1273,6 @@ describe('Wallet', function() { walletdb = new WalletDB({ name: 'wallet-test', db: 'memory', - resolution: false, verify: false });