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());
|
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.
|
* Save credit.
|
||||||
* @param {Credit} credit
|
* @param {Credit} credit
|
||||||
@ -1042,6 +830,15 @@ TXDB.prototype.insert = co(function* insert(wtx, block) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coin = credit.coin;
|
coin = credit.coin;
|
||||||
|
|
||||||
|
// Do some verification.
|
||||||
|
if (!block) {
|
||||||
|
if (!(yield this.verifyInput(tx, i, coin))) {
|
||||||
|
this.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path = yield this.getPath(coin);
|
path = yield this.getPath(coin);
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
@ -1147,6 +944,7 @@ TXDB.prototype.insert = co(function* insert(wtx, block) {
|
|||||||
this.put(layout.H(account, height, hash), DUMMY);
|
this.put(layout.H(account, height, hash), DUMMY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update block records.
|
||||||
if (block) {
|
if (block) {
|
||||||
yield this.addBlockMap(hash, height);
|
yield this.addBlockMap(hash, height);
|
||||||
yield this.addBlock(tx.hash(), block);
|
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));
|
this.del(layout.H(account, height, hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update block records.
|
||||||
if (block) {
|
if (block) {
|
||||||
yield this.removeBlockMap(hash, height);
|
yield this.removeBlockMap(hash, height);
|
||||||
yield this.removeBlockSlow(hash, height);
|
yield this.removeBlockSlow(hash, height);
|
||||||
@ -1747,6 +1546,22 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, conf) {
|
|||||||
return true;
|
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.
|
* Lock all coins in a transaction.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
@ -3471,18 +3286,6 @@ BlockRecord.fromMeta = function fromMeta(block) {
|
|||||||
* Helpers
|
* 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) {
|
function cmp(a, b) {
|
||||||
return 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) {
|
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;
|
var details, derived;
|
||||||
|
|
||||||
this.txdb.start();
|
this.txdb.start();
|
||||||
|
|||||||
@ -1495,38 +1495,6 @@ WalletDB.prototype.resend = co(function* resend() {
|
|||||||
yield this.send(txs[i]);
|
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.
|
* Get all wallet ids by output addresses and outpoints.
|
||||||
* @param {Hash[]} hashes
|
* @param {Hash[]} hashes
|
||||||
@ -1543,17 +1511,13 @@ WalletDB.prototype.getWalletsByTX = co(function* getWalletsByTX(tx) {
|
|||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
prevout = input.prevout;
|
prevout = input.prevout;
|
||||||
|
|
||||||
if (!this.testFilter(prevout.toRaw())) {
|
if (!this.testFilter(prevout.toRaw()))
|
||||||
yield this.testInput(input, result);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
map = yield this.getOutpointMap(prevout.hash, prevout.index);
|
map = yield this.getOutpointMap(prevout.hash, prevout.index);
|
||||||
|
|
||||||
if (!map) {
|
if (!map)
|
||||||
yield this.testInput(input, result);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < map.wids.length; j++)
|
for (j = 0; j < map.wids.length; j++)
|
||||||
util.binaryInsert(result, map.wids[j], cmp, true);
|
util.binaryInsert(result, map.wids[j], cmp, true);
|
||||||
|
|||||||
@ -66,7 +66,6 @@ describe('Wallet', function() {
|
|||||||
walletdb = new WalletDB({
|
walletdb = new WalletDB({
|
||||||
name: 'wallet-test',
|
name: 'wallet-test',
|
||||||
db: 'memory',
|
db: 'memory',
|
||||||
resolution: true,
|
|
||||||
verify: true
|
verify: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -273,20 +272,20 @@ describe('Wallet', function() {
|
|||||||
yield walletdb.addTX(t4);
|
yield walletdb.addTX(t4);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
//assert.equal(balance.unconfirmed, 22500);
|
assert.equal(balance.unconfirmed, 22500);
|
||||||
assert.equal(balance.unconfirmed, 0);
|
// assert.equal(balance.unconfirmed, 0);
|
||||||
|
|
||||||
yield walletdb.addTX(t1);
|
yield walletdb.addTX(t1);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
//assert.equal(balance.unconfirmed, 73000);
|
assert.equal(balance.unconfirmed, 72500);
|
||||||
assert.equal(balance.unconfirmed, 51000);
|
// assert.equal(balance.unconfirmed, 51000);
|
||||||
|
|
||||||
yield walletdb.addTX(t2);
|
yield walletdb.addTX(t2);
|
||||||
|
|
||||||
balance = yield w.getBalance();
|
balance = yield w.getBalance();
|
||||||
//assert.equal(balance.unconfirmed, 47000);
|
assert.equal(balance.unconfirmed, 46500);
|
||||||
assert.equal(balance.unconfirmed, 49000);
|
// assert.equal(balance.unconfirmed, 49000);
|
||||||
|
|
||||||
yield walletdb.addTX(t3);
|
yield walletdb.addTX(t3);
|
||||||
|
|
||||||
@ -353,7 +352,6 @@ describe('Wallet', function() {
|
|||||||
walletdb = new WalletDB({
|
walletdb = new WalletDB({
|
||||||
name: 'wallet-test',
|
name: 'wallet-test',
|
||||||
db: 'memory',
|
db: 'memory',
|
||||||
resolution: false,
|
|
||||||
verify: false
|
verify: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1209,7 +1207,6 @@ describe('Wallet', function() {
|
|||||||
walletdb = new WalletDB({
|
walletdb = new WalletDB({
|
||||||
name: 'wallet-test',
|
name: 'wallet-test',
|
||||||
db: 'memory',
|
db: 'memory',
|
||||||
resolution: false,
|
|
||||||
verify: false
|
verify: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1276,7 +1273,6 @@ describe('Wallet', function() {
|
|||||||
walletdb = new WalletDB({
|
walletdb = new WalletDB({
|
||||||
name: 'wallet-test',
|
name: 'wallet-test',
|
||||||
db: 'memory',
|
db: 'memory',
|
||||||
resolution: false,
|
|
||||||
verify: false
|
verify: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user