diff --git a/lib/blockchain/coins.js b/lib/blockchain/coins.js index f75c7b4f..cd18e75e 100644 --- a/lib/blockchain/coins.js +++ b/lib/blockchain/coins.js @@ -221,37 +221,24 @@ Coins.prototype.isEmpty = function isEmpty() { Coins.prototype.toRaw = function toRaw() { var bw = new BufferWriter(); var len = this.outputs.length; - var first = len > 0 && this.outputs[0]; - var second = len > 1 && this.outputs[1]; - var size = 0; - var nonzero = 0; + var size = Math.floor((len + 5) / 8); + var first = this.has(0); + var second = this.has(1); + var offset = 0; var i, j, code, ch, output; // Throw if we're fully spent. assert(len !== 0, 'Cannot serialize fully-spent coins.'); - // Calculate number of unspents and spent field size. - // size = number of bytes required for the bit field. - // nonzero = number of non-zero bytes required. - for (i = 0; 2 + i * 8 < len; i++) { - for (j = 0; j < 8 && 2 + i * 8 + j < len; j++) { - if (this.outputs[2 + i * 8 + j]) { - size = i + 1; - nonzero++; - break; - } - } - } - // First and second bits // have a double meaning. if (!first && !second) { - assert(nonzero !== 0); - nonzero -= 1; + assert(size !== 0); + offset = 1; } // Calculate header code. - code = 8 * nonzero; + code = 8 * (size - offset); if (this.coinbase) code += 1; @@ -300,8 +287,11 @@ Coins.prototype.toRaw = function toRaw() { Coins.prototype.fromRaw = function fromRaw(data, hash) { var br = new BufferReader(data); - var i, code, field, nonzero, ch, unspent, coin; + var first = null; + var second = null; + var i, j, code, size, offset, ch; + // Inject hash (passed by caller). this.hash = hash; // Read headers. @@ -310,41 +300,36 @@ Coins.prototype.fromRaw = function fromRaw(data, hash) { code = br.readVarint(); this.coinbase = (code & 1) !== 0; - // Setup spent field. - field = [ - (code & 2) !== 0, - (code & 4) !== 0 - ]; - // Recalculate number of non-zero bytes. - nonzero = code / 8 | 0; + size = code / 8 | 0; if ((code & 6) === 0) - nonzero += 1; + size += 1; - // Read spent field. - while (nonzero > 0) { - ch = br.readU8(); - for (i = 0; i < 8; i++) { - unspent = (ch & (1 << i)) !== 0; - field.push(unspent); - } - if (ch !== 0) - nonzero--; - } + // Setup spent field. + offset = br.offset; + br.seek(size); + + // Read first two outputs. + if ((code & 2) !== 0) + first = CoinEntry.fromReader(br); + + if ((code & 4) !== 0) + second = CoinEntry.fromReader(br); + + this.outputs.push(first); + this.outputs.push(second); // Read outputs. - for (i = 0; i < field.length; i++) { - if (!field[i]) { - this.outputs.push(null); - continue; + for (i = 0; i < size; i++) { + ch = br.data[offset++]; + for (j = 0; j < 8; j++) { + if ((ch & (1 << j)) === 0) { + this.outputs.push(null); + continue; + } + this.outputs.push(CoinEntry.fromReader(br)); } - - // Store the offset and size - // in the compressed coin object. - coin = CoinEntry.fromReader(br); - - this.outputs.push(coin); } this.cleanup(); @@ -363,8 +348,9 @@ Coins.prototype.fromRaw = function fromRaw(data, hash) { Coins.parseCoin = function parseCoin(data, hash, index) { var br = new BufferReader(data); var coin = new Coin(); - var i, code, field, nonzero, ch, unspent; + var i, j, code, size, offset, ch; + // Inject outpoint (passed by caller). coin.hash = hash; coin.index = index; @@ -374,56 +360,49 @@ Coins.parseCoin = function parseCoin(data, hash, index) { code = br.readVarint(); coin.coinbase = (code & 1) !== 0; - // Setup spent field. - field = [ - (code & 2) !== 0, - (code & 4) !== 0 - ]; - // Recalculate number of non-zero bytes. - nonzero = code / 8 | 0; + size = code / 8 | 0; if ((code & 6) === 0) - nonzero += 1; + size += 1; - // Read spent field. - while (nonzero > 0 && field.length <= index) { - ch = br.readU8(); - for (i = 0; i < 8; i++) { - unspent = (ch & (1 << i)) !== 0; - field.push(unspent); - } - if (ch !== 0) - nonzero--; - } - - if (field.length <= index) + if (index >= 2 + size * 8) return; - while (nonzero > 0) { - if (br.readU8() !== 0) - nonzero--; - } + // Read spent field. + offset = br.offset; + br.seek(size); - // Read outputs. - for (i = 0; i < field.length; i++) { - if (i === index) { - if (!field[i]) + for (i = 2; i <= 4; i += 2) { + if ((code & i) !== 0) { + if (index === 0) { + decompress.coin(coin, br); + return coin; + } + decompress.skip(br); + } else { + if (index === 0) return; - - // Read compressed output. - decompress.coin(coin, br); - - break; } - - if (!field[i]) - continue; - - decompress.skip(br); + index -= 1; } - return coin; + for (i = 0; i < size; i++) { + ch = br.data[offset++]; + for (j = 0; j < 8; j++) { + if ((ch & (1 << j)) !== 0) { + if (index === 0) { + decompress.coin(coin, br); + return coin; + } + decompress.skip(br); + } else { + if (index === 0) + return; + } + index -= 1; + } + } }; /**