tx-pool: verify all signatures

This commit is contained in:
Fedor Indutny 2014-05-10 12:28:49 +04:00
parent 86088f8ae9
commit 2e3fb9a7a3
3 changed files with 61 additions and 37 deletions

View File

@ -70,18 +70,26 @@ TXPool.prototype.add = function add(tx, noWrite) {
if (unspent) { if (unspent) {
// Add TX to inputs and spend money // Add TX to inputs and spend money
tx.input(unspent.tx, unspent.index); var index = tx._input(unspent.tx, unspent.index);
// Skip invalid transactions
if (!tx.verify(index))
return;
delete this._unspent[key]; delete this._unspent[key];
updated = true; updated = true;
continue; continue;
} }
// Double-spend?! if (!own)
if (!own || this._orphans[key])
continue; continue;
// Add orphan, if no parent transaction is yet known // Add orphan, if no parent transaction is yet known
this._orphans[key] = { tx: tx, index: input.out.index }; var orphan = { tx: tx, index: input.out.index };
if (this._orphans[key])
this._orphans[key].push(orphan);
else
this._orphans[key] = [orphan];
} }
if (!own) { if (!own) {
@ -99,27 +107,28 @@ TXPool.prototype.add = function add(tx, noWrite) {
var out = tx.outputs[i]; var out = tx.outputs[i];
var key = hash + '/' + i; var key = hash + '/' + i;
var orphan = this._orphans[key]; var orphans = this._orphans[key];
// Add input to orphan // Add input to orphan
if (orphan) { if (orphans) {
orphan.tx.input(tx, orphan.index); var some = orphans.some(function(orphan) {
var index = orphan.tx.inputIndex(tx, orphan.index); var index = orphan.tx._input(tx, orphan.index);
// Verify that input script is correct, if not - add output to unspent // Verify that input script is correct, if not - add output to unspent
// and remove orphan from storage // and remove orphan from storage
if (!orphan.tx.verify(orphan, index)) { if (!orphan.tx.verify(index)) {
orphan = null;
if (this._storage)
this._removeTX(orphan.tx); this._removeTX(orphan.tx);
} return false;
}
return true;
}, this);
if (!some)
orphans = null;
} }
if (!orphan) {
this._unspent[key] = { tx: tx, index: i };
updated = true;
continue;
}
delete this._orphans[key]; delete this._orphans[key];
if (!orphans)
this._unspent[key] = { tx: tx, index: i };
} }
this._lastTs = Math.max(tx.ts, this._lastTs); this._lastTs = Math.max(tx.ts, this._lastTs);
@ -143,6 +152,11 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx) {
}; };
TXPool.prototype._removeTX = function _removeTX(tx) { TXPool.prototype._removeTX = function _removeTX(tx) {
for (var i = 0; i < tx.outputs.length; i++)
delete this._unspent[tx.hash('hex') + '/' + i];
if (!this._storage)
return;
var self = this; var self = this;
this._storage.del(this._prefix + tx.hash('hex'), function(err) { this._storage.del(this._prefix + tx.hash('hex'), function(err) {
if (err) if (err)

View File

@ -51,7 +51,7 @@ TX.prototype.render = function render() {
return bcoin.protocol.framer.tx(this); return bcoin.protocol.framer.tx(this);
}; };
TX.prototype.input = function input(i, index) { TX.prototype._input = function _input(i, index) {
if (i instanceof TX) if (i instanceof TX)
i = { tx: i, index: index }; i = { tx: i, index: index };
else if (typeof i === 'string' || Array.isArray(i)) else if (typeof i === 'string' || Array.isArray(i))
@ -79,7 +79,7 @@ TX.prototype.input = function input(i, index) {
}; };
// Try modifying existing input first // Try modifying existing input first
var index = this.inputIndex(hash, index); var index = this._inputIndex(hash, index);
if (index !== -1) { if (index !== -1) {
var ex = this.inputs[index]; var ex = this.inputs[index];
@ -90,12 +90,13 @@ TX.prototype.input = function input(i, index) {
this.inputs.push(input); this.inputs.push(input);
if (input.out.tx) if (input.out.tx)
this.funds.iadd(input.out.tx.outputs[input.out.index].value); this.funds.iadd(input.out.tx.outputs[input.out.index].value);
index = this.inputs.length - 1;
} }
return this; return index;
}; };
TX.prototype.inputIndex = function inputIndex(hash, index) { TX.prototype._inputIndex = function _inputIndex(hash, index) {
if (hash instanceof TX) if (hash instanceof TX)
hash = hash.hash('hex'); hash = hash.hash('hex');
for (var i = 0; i < this.inputs.length; i++) { for (var i = 0; i < this.inputs.length; i++) {
@ -107,6 +108,11 @@ TX.prototype.inputIndex = function inputIndex(hash, index) {
return -1; return -1;
}; };
TX.prototype.input = function input(i, index) {
this._input(i, index);
return this;
};
TX.prototype.out = function out(output, value) { TX.prototype.out = function out(output, value) {
if (output instanceof bcoin.wallet) if (output instanceof bcoin.wallet)
output = output.getAddress(); output = output.getAddress();
@ -175,9 +181,9 @@ TX.prototype.verify = function verify(index, force) {
if (!force && this.ts !== 0) if (!force && this.ts !== 0)
return true; return true;
return this.inputs.filter(function(input, i) { return this.inputs.every(function(input, i) {
if (index !== undefined && index !== i) if (index !== undefined && index !== i)
return false; return true;
if (!input.out.tx) if (!input.out.tx)
return false; return false;

View File

@ -79,24 +79,25 @@ describe('Wallet', function() {
// Coinbase // Coinbase
var t1 = bcoin.tx().out(w, 50000).out(w, 1000); var t1 = bcoin.tx().out(w, 50000).out(w, 1000);
var t2 = bcoin.tx().input(t1.hash(), 0) w.sign(t1);
var t2 = bcoin.tx().input(t1, 0)
.out(w, 24000) .out(w, 24000)
.out(w, 24000); .out(w, 24000);
var t3 = bcoin.tx().input(t1.hash(), 1) w.sign(t2);
.input(t2.hash(), 0) var t3 = bcoin.tx().input(t1, 1)
.input(t2, 0)
.out(w, 23000); .out(w, 23000);
var t4 = bcoin.tx().input(t2.hash(), 1) w.sign(t3);
.input(t3.hash(), 0) var t4 = bcoin.tx().input(t2, 1)
.input(t3, 0)
.out(w, 11000) .out(w, 11000)
.out(w, 11000); .out(w, 11000);
var f1 = bcoin.tx().input(t4.hash(), 1)
.out(f, 10000);
var fake = bcoin.tx().input(t1.hash(), 1);
w.sign(t1);
w.sign(t2);
w.sign(t3);
w.sign(t4); w.sign(t4);
var f1 = bcoin.tx().input(t4, 1)
.out(f, 10000);
w.sign(f1); w.sign(f1);
var fake = bcoin.tx().input(t1, 1)
.out(w, 500);
// Just for debugging // Just for debugging
t1.hint = 't1'; t1.hint = 't1';
@ -104,10 +105,13 @@ describe('Wallet', function() {
t3.hint = 't3'; t3.hint = 't3';
t4.hint = 't4'; t4.hint = 't4';
f1.hint = 'f1'; f1.hint = 'f1';
fake.hint = 'fake';
// Fake TX should temporarly change output
w.addTX(fake); w.addTX(fake);
w.addTX(t4); w.addTX(t4);
assert.equal(w.balance().toString(10), '22000'); assert.equal(w.balance().toString(10), '22500');
w.addTX(t1); w.addTX(t1);
assert.equal(w.balance().toString(10), '73000'); assert.equal(w.balance().toString(10), '73000');
w.addTX(t2); w.addTX(t2);