From 4b85715ec266679bc65798dfcc4c28a56e2ad7dc Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 17 Aug 2016 16:45:42 -0700 Subject: [PATCH] do not accept mutable txs anywhere except wallet. --- lib/bcoin/block.js | 4 +++- lib/bcoin/mempool.js | 3 +-- lib/bcoin/miner.js | 17 +++++++++-------- lib/bcoin/pool.js | 6 ++---- lib/bcoin/txdb.js | 10 +++++----- lib/bcoin/walletdb.js | 2 ++ test/chain-test.js | 2 +- test/http-test.js | 1 + test/mempool-test.js | 16 ++++++++-------- test/wallet-test.js | 21 +++++++++++++++++++++ 10 files changed, 53 insertions(+), 29 deletions(-) diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 5157dbb5..97aaa4f6 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -236,7 +236,9 @@ Block.prototype.hasWitness = function hasWitness() { */ Block.prototype.addTX = function addTX(tx) { - var index = this.txs.push(tx) - 1; + var index; + assert(!tx.mutable, 'Cannot add mutable TX to block.'); + index = this.txs.push(tx) - 1; tx.setBlock(this, index); return index; }; diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 7c6a26eb..69c32dac 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -517,8 +517,7 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { if (!unlock) return; - if (tx.mutable) - tx = tx.toTX(); + assert(!tx.mutable, 'Cannot add mutable TX to mempool.'); callback = utils.wrap(callback, unlock); callback = utils.asyncify(callback); diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index dfcbd342..b47bb118 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -505,8 +505,7 @@ MinerBlock.prototype.updateMerkle = function updateMerkle() { MinerBlock.prototype.addTX = function addTX(tx) { var cost; - if (tx.mutable) - tx = tx.toTX(); + assert(!tx.mutable, 'Cannot add mutable TX to block.'); cost = this.block.getCost() + tx.getCost(); @@ -550,26 +549,28 @@ MinerBlock.prototype.findNonce = function findNonce() { // The heart and soul of the miner: match the target. while (block.nonce <= 0xffffffff) { - // Hash and test against the next target - if (rcmp(utils.hash256(data), target) <= 0) + // Hash and test against the next target. + if (rcmp(utils.hash256(data), target) <= 0) { + this.block.mutable = false; return true; + } // Increment the nonce to get a different hash block.nonce++; // Update the raw buffer (faster than - // constantly serializing the block) + // constantly serializing the headers). data.writeUInt32LE(block.nonce, 76, true); - // Send progress report every so often + // Send progress report every so often. if (block.nonce % 500000 === 0) this.sendStatus(); } - // Keep track of our iterations + // Keep track of our iterations. this.iterations++; - // Send progress report + // Send progress report. this.sendStatus(); // If we took more a second or more (likely), diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index f923c7db..2657b161 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -2212,10 +2212,8 @@ function BroadcastItem(pool, item, callback) { this.id = this.pool.uid++; this.msg = null; - if (item instanceof bcoin.tx) { - if (item.mutable) - item = item.toTX(); - } + if (item instanceof bcoin.tx) + assert(!item.mutable, 'Cannot broadcast mutable TX.'); if (item.toInv) { this.msg = item; diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index bed36121..2dd2cbdb 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -587,16 +587,17 @@ TXDB.prototype._verify = function _verify(tx, info, callback) { return callback(null, false); } - self._removeConflict(spent.hash, tx, function(err, rtx, rinfo) { + self._removeConflict(spent.hash, tx, function(err, tx, info) { if (err) return next(err); // Spender was not removed, the current // transaction is not elligible to be added. - if (!rtx) + if (!tx) return callback(null, false); - self.emit('conflict', rtx, rinfo); + // Emit the _removed_ transaction. + self.emit('conflict', tx, info); next(); }); @@ -687,8 +688,7 @@ TXDB.prototype.add = function add(tx, info, callback) { callback = utils.wrap(callback, unlock); - if (tx.mutable) - tx = tx.toTX(); + assert(!tx.mutable, 'Cannot add mutable TX to wallet.'); // Attempt to confirm tx before adding it. this._confirm(tx, info, function(err, existing) { diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index c260a6cc..74ba7b43 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -1473,6 +1473,8 @@ WalletDB.prototype.removeBlock = function removeBlock(entry, callback, force) { WalletDB.prototype.addTX = function addTX(tx, callback, force) { var self = this; + assert(!tx.mutable, 'Cannot add mutable TX to wallet.'); + // Note: // Atomicity doesn't matter here. If we crash, // the automatic rescan will get the database diff --git a/test/chain-test.js b/test/chain-test.js index e644d239..76ccefa0 100644 --- a/test/chain-test.js +++ b/test/chain-test.js @@ -48,7 +48,7 @@ describe('Chain', function() { redeemer.setLocktime(chain.height); return wallet.sign(redeemer, function(err) { assert.ifError(err); - attempt.addTX(redeemer); + attempt.addTX(redeemer.toTX()); callback(null, attempt.mineSync()); }); } diff --git a/test/http-test.js b/test/http-test.js index 09e4d3ae..3fec8a30 100644 --- a/test/http-test.js +++ b/test/http-test.js @@ -92,6 +92,7 @@ describe('HTTP', function() { .addOutput(addr, 50460); t1.addInput(dummyInput); + t1 = t1.toTX(); wallet.once('balance', function(b) { balance = b; diff --git a/test/mempool-test.js b/test/mempool-test.js index 1fceab09..65e20093 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -75,18 +75,21 @@ describe('Mempool', function() { // balance: 51000 w.sign(t1, function(err, total) { assert.ifError(err); + t1 = t1.toTX(); var t2 = bcoin.mtx().addInput(t1, 0) // 50000 .addOutput(w, 20000) .addOutput(w, 20000); // balance: 49000 w.sign(t2, function(err, total) { assert.ifError(err); + t2 = t2.toTX(); var t3 = bcoin.mtx().addInput(t1, 1) // 10000 .addInput(t2, 0) // 20000 .addOutput(w, 23000); // balance: 47000 w.sign(t3, function(err, total) { assert.ifError(err); + t3 = t3.toTX(); var t4 = bcoin.mtx().addInput(t2, 1) // 24000 .addInput(t3, 0) // 23000 .addOutput(w, 11000) @@ -94,11 +97,13 @@ describe('Mempool', function() { // balance: 22000 w.sign(t4, function(err, total) { assert.ifError(err); + t4 = t4.toTX(); var f1 = bcoin.mtx().addInput(t4, 1) // 11000 .addOutput(bcoin.address.fromData(new Buffer([])).toBase58(), 9000); // balance: 11000 w.sign(f1, function(err, total) { assert.ifError(err); + f1 = f1.toTX(); var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed) .addOutput(w, 6000); // 6000 instead of 500 // Script inputs but do not sign @@ -107,6 +112,7 @@ describe('Mempool', function() { // Fake signature fake.inputs[0].script.set(0, new Buffer([0,0,0,0,0,0,0,0,0])); fake.inputs[0].script.compile(); + fake = fake.toTX(); // balance: 11000 [t2, t3, t4, f1, fake].forEach(function(tx) { tx.inputs.forEach(function(input) { @@ -114,14 +120,6 @@ describe('Mempool', function() { }); }); - // Just for debugging - t1.hint = 't1'; - t2.hint = 't2'; - t3.hint = 't3'; - t4.hint = 't4'; - f1.hint = 'f1'; - fake.hint = 'fake'; - mempool.addTX(fake, function(err) { assert.ifError(err); mempool.addTX(t4, function(err) { @@ -191,6 +189,7 @@ describe('Mempool', function() { t1.setLocktime(200); chain.tip.height = 200; t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, kp.privateKey, 'all', 0)]), + t1 = t1.toTX(); mempool.addTX(t1, function(err) { chain.tip.height = 0; assert.ifError(err); @@ -225,6 +224,7 @@ describe('Mempool', function() { t1.setLocktime(200); chain.tip.height = 200 - 1; t1.inputs[0].script = new bcoin.script([t1.createSignature(0, prev, kp.privateKey, 'all', 0)]), + t1 = t1.toTX(); mempool.addTX(t1, function(err) { chain.tip.height = 0; assert(err); diff --git a/test/wallet-test.js b/test/wallet-test.js index 317d1f86..7c15d04f 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -206,6 +206,7 @@ describe('Wallet', function() { // balance: 51000 w.sign(t1, function(err) { assert.ifError(err); + t1 = t1.toTX(); var t2 = bcoin.mtx().addInput(t1, 0) // 50000 .addOutput(w, 24000) .addOutput(w, 24000); @@ -213,12 +214,14 @@ describe('Wallet', function() { // balance: 49000 w.sign(t2, function(err) { assert.ifError(err); + t2 = t2.toTX(); var t3 = bcoin.mtx().addInput(t1, 1) // 1000 .addInput(t2, 0) // 24000 .addOutput(w, 23000); // balance: 47000 w.sign(t3, function(err) { assert.ifError(err); + t3 = t3.toTX(); var t4 = bcoin.mtx().addInput(t2, 1) // 24000 .addInput(t3, 0) // 23000 .addOutput(w, 11000) @@ -226,11 +229,13 @@ describe('Wallet', function() { // balance: 22000 w.sign(t4, function(err) { assert.ifError(err); + t4 = t4.toTX(); var f1 = bcoin.mtx().addInput(t4, 1) // 11000 .addOutput(f, 10000); // balance: 11000 w.sign(f1, function(err) { assert.ifError(err); + f1 = f1.toTX(); var fake = bcoin.mtx().addInput(t1, 1) // 1000 (already redeemed) .addOutput(w, 500); // Script inputs but do not sign @@ -240,6 +245,7 @@ describe('Wallet', function() { fake.inputs[0].script.set(0, FAKE_SIG); fake.inputs[0].script.compile(); // balance: 11000 + fake = fake.toTX(); // Fake TX should temporarly change output walletdb.addTX(fake, function(err) { @@ -319,6 +325,7 @@ describe('Wallet', function() { assert.ifError(err); dw.sign(t1, function(err) { assert.ifError(err); + t1 = t1.toTX(); dw.getBalance(function(err, balance) { assert.ifError(err); assert.equal(balance.total, 11000); @@ -361,6 +368,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -371,6 +379,7 @@ describe('Wallet', function() { assert.ifError(err); w1.sign(t2, function(err) { assert.ifError(err); + t2 = t2.toTX(); assert(t2.verify()); @@ -409,6 +418,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -419,6 +429,7 @@ describe('Wallet', function() { assert.ifError(err); w1.sign(t2, function(err) { assert.ifError(err); + t2 = t2.toTX(); assert(t2.verify()); assert.equal(t2.getInputValue(), 16380); @@ -472,6 +483,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); // Coinbase var t2 = bcoin.mtx() @@ -481,6 +493,7 @@ describe('Wallet', function() { .addOutput(w2, 5460); t2.addInput(dummyInput); + t2 = t2.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -644,6 +657,7 @@ describe('Wallet', function() { utx.addOutput({ address: addr, value: 5460 * 10 }); utx.addInput(dummyInput); + utx = utx.toTX(); // Simulate a confirmation utx.ps = 0; @@ -682,6 +696,7 @@ describe('Wallet', function() { w2.sign(send, function(err) { assert.ifError(err); + send = send.toTX(); assert(send.verify(flags)); assert.equal(w1.changeDepth, 1); @@ -776,6 +791,7 @@ describe('Wallet', function() { .addOutput(account.receiveAddress, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -843,6 +859,7 @@ describe('Wallet', function() { .addOutput(account.receiveAddress, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -864,6 +881,7 @@ describe('Wallet', function() { t1.ps = 0xdeadbeef; t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -896,6 +914,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -934,6 +953,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err); @@ -973,6 +993,7 @@ describe('Wallet', function() { .addOutput(w1, 5460); t1.addInput(dummyInput); + t1 = t1.toTX(); walletdb.addTX(t1, function(err) { assert.ifError(err);