diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 82736f25..c07dac81 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -1429,20 +1429,26 @@ RPC.prototype.getwork = co(function* getwork(args) { }); RPC.prototype.submitblock = co(function* submitblock(args) { - var block; + var block, tx; if (args.help || args.length < 1 || args.length > 2) throw new RPCError('submitblock "hexdata" ( "jsonparametersobject" )'); block = Block.fromRaw(toString(args[0]), 'hex'); + // Fix eloipool bug (witness nonce is not present). if (block.getCommitmentHash()) { - if (!block.txs[0].hasWitness()) { + tx = block.txs[0]; + if (!tx.hasWitness()) { this.logger.warning('Submitted block had no witness nonce.'); - this.logger.debug(block.txs[0]); - block.txs[0].inputs[0].witness.set(0, constants.ZERO_HASH); - block.txs[0].clearCache(); - block.clearCache(); + this.logger.debug(tx); + + // Recreate witness nonce (all zeroes). + tx.inputs[0].witness.set(0, constants.ZERO_HASH); + tx.inputs[0].witness.compile(); + + tx.refresh(); + block.refresh(); } } @@ -1460,8 +1466,6 @@ RPC.prototype._submitblock = co(function* submitblock(block) { RPC.prototype.__submitblock = co(function* submitblock(block) { this.logger.info('Handling submitted block: %s.', block.rhash()); - this.logger.debug('Coinbase:'); - this.logger.debug(block.txs[0]); try { yield this.chain.add(block); diff --git a/lib/net/bip152.js b/lib/net/bip152.js index c47c24ad..821d6c13 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -35,7 +35,7 @@ function CompactBlock(options) { if (!(this instanceof CompactBlock)) return new CompactBlock(options); - AbstractBlock.call(this, options); + AbstractBlock.call(this); this.keyNonce = null; this.ids = []; @@ -59,6 +59,8 @@ CompactBlock.prototype._verify = function _verify(ret) { }; CompactBlock.prototype.fromOptions = function fromOptions(options) { + this.parseOptions(options); + assert(Buffer.isBuffer(options.keyNonce)); assert(Array.isArray(options.ids)); assert(Array.isArray(options.ptx)); diff --git a/lib/net/peer.js b/lib/net/peer.js index 273da7f1..9df241f1 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -917,7 +917,6 @@ Peer.prototype.needsDrain = function needsDrain(size) { this.hostname); this.destroy(); this.error('Peer stalled (drain).'); - return; } }; diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index 66b704b4..4f94cd66 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -21,7 +21,6 @@ var InvItem = require('./invitem'); * @exports AbstractBlock * @constructor * @abstract - * @param {NakedBlock} options * @property {Number} version - Block version. Note * that BCoin reads versions as unsigned despite * them being signed on the protocol level. This @@ -36,9 +35,9 @@ var InvItem = require('./invitem'); * @property {ReversedHash} rhash - Reversed block hash (uint256le). */ -function AbstractBlock(options) { +function AbstractBlock() { if (!(this instanceof AbstractBlock)) - return new AbstractBlock(options); + return new AbstractBlock(); this.version = 1; this.prevBlock = constants.NULL_HASH; @@ -56,13 +55,17 @@ function AbstractBlock(options) { this._hash = null; this._hhash = null; - this._size = null; - this._witness = null; - - if (options) - this.parseOptions(options); + this._size = -1; + this._witness = -1; } +/** + * Memory flag. + * @const {Boolean} + * @default + * @memberof AbstractBlock# + */ + AbstractBlock.prototype.memory = false; /** @@ -125,6 +128,42 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) { return this; }; +/** + * Clear any cached values (abstract). + * @param {Boolean?} all - Clear transactions. + */ + +AbstractBlock.prototype._refresh = function refresh(all) { + var i, tx; + + this._valid = null; + this._validHeaders = null; + this._hash = null; + this._hhash = null; + this._size = -1; + this._witness = -1; + + if (!all) + return; + + if (!this.txs) + return; + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + tx.refresh(); + } +}; + +/** + * Clear any cached values. + * @param {Boolean?} all - Clear transactions. + */ + +AbstractBlock.prototype.refresh = function refresh(all) { + return this._refresh(all); +}; + /** * Hash the block headers. * @param {String?} enc - Can be `'hex'` or `null`. diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 88410fd0..623876ec 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -34,7 +34,7 @@ function Block(options) { if (!(this instanceof Block)) return new Block(options); - AbstractBlock.call(this, options); + AbstractBlock.call(this); this.txs = []; @@ -60,7 +60,10 @@ util.inherits(Block, AbstractBlock); Block.prototype.fromOptions = function fromOptions(options) { var i; + this.parseOptions(options); + if (options.txs) { + assert(Array.isArray(options.txs)); for (i = 0; i < options.txs.length; i++) this.addTX(options.txs[i]); } @@ -78,16 +81,12 @@ Block.fromOptions = function fromOptions(options) { /** * Clear any cached values. + * @param {Boolean?} all - Clear transactions. */ -Block.prototype.clearCache = function clearCache() { - this._valid = null; - this._validHeaders = null; - this._hash = null; - this._hhash = null; +Block.prototype.refresh = function refresh(all) { this._raw = null; - this._size = -1; - this._witness = -1; + this._refresh(all); }; /** @@ -271,13 +270,14 @@ Block.prototype.hasTX = function hasTX(hash) { */ Block.prototype.indexOf = function indexOf(hash) { - var i; + var i, tx; if (hash instanceof TX) hash = hash.hash('hex'); for (i = 0; i < this.txs.length; i++) { - if (this.txs[i].hash('hex') === hash) + tx = this.txs[i]; + if (tx.hash('hex') === hash) return i; } @@ -293,10 +293,12 @@ Block.prototype.indexOf = function indexOf(hash) { Block.prototype.createMerkleRoot = function createMerkleRoot(enc) { var leaves = []; - var i, root; + var i, tx, root; - for (i = 0; i < this.txs.length; i++) - leaves.push(this.txs[i].hash()); + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + leaves.push(tx.hash()); + } root = crypto.createMerkleRoot(leaves); @@ -327,14 +329,16 @@ Block.prototype.createWitnessNonce = function createWitnessNonce() { Block.prototype.createCommitmentHash = function createCommitmentHash(enc) { var nonce = this.getWitnessNonce(); var leaves = []; - var i, root, data, hash; + var i, tx, root, data, hash; assert(nonce, 'No witness nonce present.'); leaves.push(constants.ZERO_HASH); - for (i = 1; i < this.txs.length; i++) - leaves.push(this.txs[i].witnessHash()); + for (i = 1; i < this.txs.length; i++) { + tx = this.txs[i]; + leaves.push(tx.witnessHash()); + } root = crypto.createMerkleRoot(leaves); @@ -695,7 +699,6 @@ Block.prototype.fromJSON = function fromJSON(json) { var i; assert(json, 'Block data is required.'); - assert.equal(json.type, 'block'); assert(Array.isArray(json.txs)); this.parseJSON(json); diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index 79a1036a..2dd86ab7 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -25,7 +25,10 @@ function Headers(options) { if (!(this instanceof Headers)) return new Headers(options); - AbstractBlock.call(this, options); + AbstractBlock.call(this); + + if (options) + this.parseOptions(options); } util.inherits(Headers, AbstractBlock); diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index ca0d0f89..4b286544 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -45,6 +45,13 @@ function MemBlock() { util.inherits(MemBlock, AbstractBlock); +/** + * Memory flag. + * @const {Boolean} + * @default + * @memberof MemBlock# + */ + MemBlock.prototype.memory = true; /** diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 7f6f95e7..cbee19db 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -32,7 +32,7 @@ function MerkleBlock(options) { if (!(this instanceof MerkleBlock)) return new MerkleBlock(options); - AbstractBlock.call(this, options); + AbstractBlock.call(this); this.hashes = []; this.flags = DUMMY; @@ -60,6 +60,8 @@ util.inherits(MerkleBlock, AbstractBlock); MerkleBlock.prototype.fromOptions = function fromOptions(options) { var i, hash; + this.parseOptions(options); + assert(options, 'MerkleBlock data is required.'); assert(Array.isArray(options.hashes)); assert(Buffer.isBuffer(options.flags)); @@ -89,6 +91,18 @@ MerkleBlock.fromOptions = function fromOptions(data) { return new MerkleBlock().fromOptions(data); }; +/** + * Clear any cached values. + * @param {Boolean?} all - Clear transactions. + */ + +MerkleBlock.prototype.refresh = function refresh(all) { + this.map = {}; + this.matches.length = 0; + this._validPartial = null; + this._refresh(all); +}; + /** * Add a transaction to the block's tx vector. * @param {TX} tx @@ -243,7 +257,6 @@ MerkleBlock.prototype.extractTree = function extractTree() { if (flags.length * 8 < hashes.length) return; - height = 0; while (width(height) > 1) height++; @@ -531,10 +544,7 @@ MerkleBlock.fromBlock = function fromBlock(block, filter) { for (i = 0; i < block.txs.length; i++) { tx = block.txs[i]; - if (tx.isWatched(filter)) - matches.push(1); - else - matches.push(0); + matches.push(tx.isWatched(filter) ? 1 : 0); } return MerkleBlock.fromMatches(block, matches); @@ -555,17 +565,15 @@ MerkleBlock.fromHashes = function fromHashes(block, hashes) { for (i = 0; i < hashes.length; i++) { hash = hashes[i]; - if (typeof hash === 'string') - hash = new Buffer(hash, 'hex'); - filter[hash.toString('hex')] = true; + if (Buffer.isBuffer(hash)) + hash = hash.toString('hex'); + filter[hash] = true; } for (i = 0; i < block.txs.length; i++) { tx = block.txs[i]; - if (filter[tx.hash('hex')]) - matches.push(1); - else - matches.push(0); + hash = tx.hash('hex'); + matches.push(filter[hash] ? 1 : 0); } return MerkleBlock.fromMatches(block, matches); @@ -584,7 +592,9 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) { var leaves = []; var bits = []; var hashes = []; - var i, tx, totalTX, height, flags, p, merkle, buf; + var totalTX = block.txs.length; + var height = 0; + var i, p, tx, flags, merkle, buf; for (i = 0; i < block.txs.length; i++) { tx = block.txs[i]; @@ -593,8 +603,6 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) { leaves.push(tx.hash()); } - totalTX = leaves.length; - function width(height) { return (totalTX + (1 << height) - 1) >>> height; } @@ -638,7 +646,6 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) { traverse(height - 1, pos * 2 + 1, leaves, matches); } - height = 0; while (width(height) > 1) height++; diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index cab23d6a..ad562f20 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -60,6 +60,7 @@ function TX(options) { this.inputs = []; this.outputs = []; this.locktime = 0; + this.mutable = false; this._hash = null; @@ -144,7 +145,7 @@ TX.prototype.clone = function clone() { * Clear any cached values. */ -TX.prototype.clearCache = function clearCache() { +TX.prototype.refresh = function refresh() { this._hash = null; this._hhash = null; this._whash = null; @@ -155,6 +156,7 @@ TX.prototype.clearCache = function clearCache() { this._outputValue = -1; this._inputValue = -1; + this._hashPrevouts = null; this._hashSequence = null; this._hashOutputs = null; diff --git a/test/block-test.js b/test/block-test.js index 791e32c2..d584679d 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -194,15 +194,11 @@ describe('Block', function() { var ret = {}; block2.hash(); block2.merkleRoot = constants.NULL_HASH; - block2._valid = null; - block2._validHeaders = null; + block2.refresh(); assert(!block2.verify(ret)); assert.equal(ret.reason, 'bad-txnmrklroot'); - block2._valid = null; - block2._validHeaders = null; - block2._hash = null; - block2._hhash = null; block2.merkleRoot = block.merkleRoot; + block2.refresh(); assert(block2.verify()); }); @@ -213,12 +209,8 @@ describe('Block', function() { mblock2.merkleRoot = constants.NULL_HASH; assert(!mblock2.verify(ret)); assert.equal(ret.reason, 'bad-txnmrklroot'); - mblock2._valid = null; - mblock2._validHeaders = null; - mblock2._validPartial = null; - mblock2._hash = null; - mblock2._hhash = null; mblock2.merkleRoot = mblock.merkleRoot; + mblock2.refresh(); assert(mblock2.verify()); }); @@ -229,11 +221,8 @@ describe('Block', function() { block2.bits = 403014710; assert(!block2.verify(ret)); assert.equal(ret.reason, 'high-hash'); - block2._valid = null; - block2._validHeaders = null; - block2._hash = null; - block2._hhash = null; block2.bits = block.bits; + block2.refresh(); assert(block2.verify()); }); diff --git a/test/script-test.js b/test/script-test.js index fb47eee2..061a1183 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -308,27 +308,8 @@ describe('Script', function() { }); if (noCache) { - tx._raw = null; - tx._size = -1; - tx._witnessSize = -1; - tx._hash = null; - tx._hhash = null; - tx._whash = null; - tx._inputValue = -1; - tx._outputValue = -1; - tx._hashPrevouts = null; - tx._hashSequence = null; - tx._hashOutputs = null; - - prev._raw = null; - prev._size = -1; - prev._witnessSize = -1; - prev._hash = null; - prev._inputValue = -1; - prev._outputValue = -1; - prev._hashPrevouts = null; - prev._hashSequence = null; - prev._hashOutputs = null; + prev.refresh(); + tx.refresh(); } try { diff --git a/test/tx-test.js b/test/tx-test.js index 8a05857c..390d14fc 100644 --- a/test/tx-test.js +++ b/test/tx-test.js @@ -46,18 +46,7 @@ function clearCache(tx, noCache) { assert.equal(tx.hash('hex'), tx.clone().hash('hex')); return; } - - tx._raw = null; - tx._size = -1; - tx._witnessSize = -1; - tx._hash = null; - tx._hhash = null; - tx._whash = null; - tx._inputValue = -1; - tx._outputValue = -1; - tx._hashPrevouts = null; - tx._hashSequence = null; - tx._hashOutputs = null; + tx.refresh(); } function parseTest(data) { @@ -542,11 +531,13 @@ describe('TX', function() { raw = tx.toRaw(); assert(encoding.readU64(raw, 47) === 0xdeadbeef); raw[54] = 0x7f; + assert.throws(function() { TX.fromRaw(raw); }); - tx._raw = null; + tx.outputs[0].value = 0; + tx.refresh(); raw = tx.toRaw(); assert(encoding.readU64(raw, 47) === 0x00);