wallet: remove resolution.
This commit is contained in:
parent
d01088b54d
commit
f9a1e18437
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user