diff --git a/lib/blockchain/coins.js b/lib/blockchain/coins.js index 20264c81..c7212732 100644 --- a/lib/blockchain/coins.js +++ b/lib/blockchain/coins.js @@ -317,8 +317,9 @@ Coins.prototype.toRaw = function toRaw(writer) { Coins.prototype.fromRaw = function fromRaw(data, hash, index) { var p = new BufferReader(data); - var i = 0; - var bits, coin, offset, size, fstart, flen, bit, oct, spent; + var pos = 0; + var fstart, flen, bit, oct; + var bits, coin, spent; this.version = p.readVarint(); @@ -339,72 +340,25 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) { while (p.left()) { // Read a single bit out of the spent field. - bit = i % 8; - oct = (i - bit) / 8; + bit = pos % 8; + oct = (pos - bit) / 8; spent = (p.data[fstart + oct] >>> (7 - bit)) & 1; // Already spent. if (spent) { - // Don't bother pushing outputs on if - // we're seeking to a specific index. - if (index != null) { - if (i === index) - return; - i++; - continue; - } - this.outputs.push(null); - i++; - continue; - } - - offset = p.offset; - - // Skip past the compressed scripts. - switch (p.readU8()) { - case 0: - p.seek(p.readVarint()); - break; - case 1: - case 2: - p.seek(20); - break; - case 3: - p.seek(33); - break; - default: - throw new Error('Bad prefix.'); - } - - // Skip past the value. - p.readVarint(); - - size = p.offset - offset; - - // Keep going if we're seeking - // to a specific index. - if (index != null && i !== index) { - i++; + pos++; continue; } // Store the offset and size // in the compressed coin object. - coin = new CompressedCoin(offset, size, data); - - // We found our coin. - if (index != null) - return coin.toCoin(this, i); + coin = CompressedCoin.fromRaw(p); this.outputs.push(coin); - i++; + pos++; } - // We couldn't find our coin. - if (index != null) - return; - return this; }; @@ -417,8 +371,56 @@ Coins.prototype.fromRaw = function fromRaw(data, hash, index) { */ Coins.parseCoin = function parseCoin(data, hash, index) { - assert(index != null, 'Bad coin index.'); - return new Coins().fromRaw(data, hash, index); + var p = new BufferReader(data); + var coin = new Coin(); + var pos = 0; + var fstart, flen, bit, oct; + var spent, bits; + + coin.version = p.readVarint(); + + bits = p.readU32(); + + coin.hash = hash; + coin.index = index; + coin.height = bits >>> 1; + coin.hash = hash; + coin.coinbase = (bits & 1) !== 0; + + if (coin.height === 0x7fffffff) + coin.height = -1; + + // Mark the start of the spent field and + // seek past it to avoid reading a buffer. + flen = p.readVarint(); + fstart = p.offset; + p.seek(flen); + + while (p.left()) { + // Read a single bit out of the spent field. + bit = pos % 8; + oct = (pos - bit) / 8; + spent = (p.data[fstart + oct] >>> (7 - bit)) & 1; + + // We found our coin. + if (pos === index) { + if (spent) + return; + decompress.script(p, coin.script); + coin.value = p.readVarint(); + return coin; + } + + // Already spent. + if (spent) { + pos++; + continue; + } + + // Skip passed the compressed coin. + skipCoin(p); + pos++; + } }; /** @@ -439,7 +441,7 @@ Coins.fromRaw = function fromRaw(data, hash) { */ Coins.prototype.fromTX = function fromTX(tx) { - var i; + var i, output; this.version = tx.version; this.hash = tx.hash('hex'); @@ -447,10 +449,13 @@ Coins.prototype.fromTX = function fromTX(tx) { this.coinbase = tx.isCoinbase(); for (i = 0; i < tx.outputs.length; i++) { - if (tx.outputs[i].script.isUnspendable()) { + output = tx.outputs[i]; + + if (output.script.isUnspendable()) { this.outputs.push(null); continue; } + this.outputs.push(Coin.fromTX(tx, i)); } @@ -487,9 +492,6 @@ Coins.fromTX = function fromTX(tx) { */ function CompressedCoin(offset, size, raw) { - if (!(this instanceof CompressedCoin)) - return new CompressedCoin(offset, size, raw); - this.offset = offset; this.size = size; this.raw = raw; @@ -534,6 +536,47 @@ CompressedCoin.prototype.toRaw = function toRaw() { return this.raw.slice(this.offset, this.offset + this.size); }; +/** + * Instantiate compressed coin from reader. + * @param {BufferReader} p + * @returns {CompressedCoin} + */ + +CompressedCoin.fromRaw = function fromRaw(p) { + var offset = p.offset; + var size = skipCoin(p); + return new CompressedCoin(offset, size, p.data); +}; + +/* + * Helpers + */ + +function skipCoin(p) { + var start = p.offset; + + // Skip past the compressed scripts. + switch (p.readU8()) { + case 0: + p.seek(p.readVarint()); + break; + case 1: + case 2: + p.seek(20); + break; + case 3: + p.seek(33); + break; + default: + throw new Error('Bad prefix.'); + } + + // Skip past the value. + p.readVarint(); + + return p.offset - start; +} + /* * Expose */