wallet: remove resolution.

This commit is contained in:
Christopher Jeffrey 2016-12-13 14:15:58 -08:00
parent d01088b54d
commit f9a1e18437
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 35 additions and 293 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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
});