diff --git a/lib/bcoin/abstractblock.js b/lib/bcoin/abstractblock.js index b4bb15e8..9867bc72 100644 --- a/lib/bcoin/abstractblock.js +++ b/lib/bcoin/abstractblock.js @@ -7,6 +7,7 @@ var bcoin = require('../bcoin'); var utils = require('./utils'); var network = bcoin.protocol.network; +var BufferWriter = require('./writer'); /** * AbstractBlock @@ -50,20 +51,21 @@ AbstractBlock.prototype.hash = function hash(enc) { }; AbstractBlock.prototype.abbr = function abbr() { - var res; + var p; if (this._raw) return this._raw.slice(0, 80); - res = new Buffer(80); - utils.write32(res, this.version, 0); - utils.copy(new Buffer(this.prevBlock, 'hex'), res, 4); - utils.copy(new Buffer(this.merkleRoot, 'hex'), res, 36); - utils.writeU32(res, this.ts, 68); - utils.writeU32(res, this.bits, 72); - utils.writeU32(res, this.nonce, 76); + p = new BufferWriter(); - return res; + p.write32(this.version); + p.writeHash(this.prevBlock); + p.writeHash(this.merkleRoot); + p.writeU32(this.ts); + p.writeU32(this.bits); + p.writeU32(this.nonce); + + return p.render(); }; AbstractBlock.prototype.getSize = function getSize() { diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index f3949b86..c3c112bd 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -341,60 +341,46 @@ Block.fromRaw = function fromRaw(data, enc, type) { }; Block.prototype.toCompact = function toCompact() { - var block = this.abbr(); + var p = new BufferWriter(); var height = this.height; - var off = 0; - var buf; - - buf = new Buffer( - block.length + 4 - + utils.sizeIntv(this.txs.length) - + this.txs.length * 32); if (height === -1) height = 0x7fffffff; - off += utils.copy(block, buf, off); - off += utils.writeU32(buf, height, off); + p.writeBytes(this.abbr()); + p.writeU32(height); + p.writeIntv(this.txs.length); - off += utils.writeIntv(buf, this.txs.length, off); this.txs.forEach(function(tx) { - off += utils.copy(tx.hash(), buf, off); + p.writeBytes(tx.hash()); }); - return buf; + return p.render(); }; Block.fromCompact = function fromCompact(buf) { - var off = 0; + var p = new BufferReader(buf); var hashes = []; + var version = p.read32(); + var prevBlock = p.readHash('hex'); + var merkleRoot = p.readHash('hex'); + var ts = p.readU32(); + var bits = p.readU32(); + var nonce = p.readU32(); + var height = p.readU32(); + var txCount = p.readIntv(); var i; - var version = utils.read32(buf, 0); - var prevBlock = buf.slice(4, 36); - var merkleRoot = buf.slice(36, 68); - var ts = utils.readU32(buf, 68); - var bits = utils.readU32(buf, 72); - var nonce = utils.readU32(buf, 76); - var height = utils.readU32(buf, 80); - var txCount = utils.readIntv(buf, 84); - off = txCount.off; - txCount = txCount.r; - - for (i = 0; i < txCount; i++) { - if (off + 32 > buf.length) - throw new Error('Bad TX count.'); - hashes.push(utils.toHex(buf.slice(off, off + 32))); - off += 32; - } + for (i = 0; i < txCount; i++) + hashes.push(p.readHash('hex')); if (height === 0x7fffffff) height = -1; return { version: version, - prevBlock: utils.toHex(prevBlock), - merkleRoot: utils.toHex(merkleRoot), + prevBlock: prevBlock, + merkleRoot: merkleRoot, ts: ts, bits: bits, nonce: nonce, diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index aa0d0650..da3f5fac 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -12,6 +12,7 @@ var constants = bcoin.protocol.constants; var network = bcoin.protocol.network; var utils = require('./utils'); var assert = utils.assert; +var BufferReader = require('./reader'); /** * Chain @@ -254,6 +255,21 @@ Chain.prototype._preload = function _preload(callback) { utils.debug('Loading %s', url); + function parseHeader(buf) { + var p = new BufferReader(buf); + var hash = utils.dsha256(buf.slice(0, 80)); + + return { + hash: utils.toHex(hash), + version: p.read32(), + prevBlock: p.readHash('hex'), + merkleRoot: p.readHash('hex'), + ts: p.readU32(), + bits: p.readU32(), + nonce: p.readU32() + }; + } + this.db.getChainHeight(function(err, chainHeight) { if (err) return callback(err); @@ -305,15 +321,19 @@ Chain.prototype._preload = function _preload(callback) { return; blocks.forEach(function(data) { - var entry = bcoin.chainblock.fromRaw(self, data); - var block, start; + var block, entry, start; - entry.height = height; + data = parseHeader(data); + data.height = height; - block = bcoin.headers(entry); + // Make sure the genesis block is correct. + if (data.height === 0 && data.hash !== network.genesis.hash) { + stream.destroy(); + return callback(new Error('Bad genesis block.'), 0); + } // Do some paranoid checks. - if (lastEntry && entry.prevBlock !== lastEntry.hash) { + if (lastEntry && data.prevBlock !== lastEntry.hash) { start = Math.max(0, height - 2); stream.destroy(); return self.reset(start, function(err) { @@ -323,6 +343,9 @@ Chain.prototype._preload = function _preload(callback) { }); } + // Create headers object for validation. + block = new bcoin.headers(data); + // Verify the block headers. We don't want to // trust an external centralized source completely. if (!block.verifyHeaders()) { @@ -335,31 +358,23 @@ Chain.prototype._preload = function _preload(callback) { }); } - // Calculate chainwork. - delete entry.chainwork; - entry.chainwork = entry.getChainwork(lastEntry); - - lastEntry = entry; - - // Make sure the genesis block is correct. - if (height === 0 && entry.hash !== network.genesis.hash) { - stream.destroy(); - return callback(new Error('Bad genesis block.'), 0); - } + // Create a chain entry. + entry = new bcoin.chainblock(self, data, lastEntry); // Filthy hack to avoid writing // redundant blocks to disk! - if (height <= chainHeight) { + if (entry.height <= chainHeight) { self.db.addCache(entry); // self.db.bloom(entry.hash, 'hex'); } else { self.db.save(entry); } - height++; - if ((height + 1) % 50000 === 0) utils.debug('Received %d headers from electrum.org.', height + 1); + + lastEntry = entry; + height++; }); }); diff --git a/lib/bcoin/chainblock.js b/lib/bcoin/chainblock.js index ed791c2a..365260be 100644 --- a/lib/bcoin/chainblock.js +++ b/lib/bcoin/chainblock.js @@ -10,6 +10,8 @@ var constants = bcoin.protocol.constants; var network = bcoin.protocol.network; var utils = require('./utils'); var assert = utils.assert; +var BufferWriter = require('./writer'); +var BufferReader = require('./reader'); /** * ChainBlock @@ -241,31 +243,34 @@ ChainBlock.prototype.getMedianTimeAsync = function getMedianTimeAsync(callback) }; ChainBlock.prototype.toRaw = function toRaw() { - var res = new Buffer(116); + var p = new BufferWriter(); - utils.write32(res, this.version, 0); - utils.copy(new Buffer(this.prevBlock, 'hex'), res, 4); - utils.copy(new Buffer(this.merkleRoot, 'hex'), res, 36); - utils.writeU32(res, this.ts, 68); - utils.writeU32(res, this.bits, 72); - utils.writeU32(res, this.nonce, 76); - utils.writeU32(res, this.height, 80); - utils.copy(new Buffer(this.chainwork.toArray('le', 32)), res, 84); + p.write32(this.version); + p.writeHash(this.prevBlock); + p.writeHash(this.merkleRoot); + p.writeU32(this.ts); + p.writeU32(this.bits); + p.writeU32(this.nonce); + p.writeU32(this.height); + p.writeBytes(new Buffer(this.chainwork.toArray('le', 32))); - return res; + return p.render(); }; -ChainBlock.fromRaw = function fromRaw(chain, p) { +ChainBlock.fromRaw = function fromRaw(chain, buf) { + var p = new BufferReader(buf); + var hash = utils.dsha256(buf.slice(0, 80)); + return new ChainBlock(chain, { - hash: utils.toHex(utils.dsha256(p.slice(0, 80))), - version: utils.read32(p, 0), - prevBlock: utils.toHex(p.slice(4, 36)), - merkleRoot: utils.toHex(p.slice(36, 68)), - ts: utils.readU32(p, 68), - bits: utils.readU32(p, 72), - nonce: utils.readU32(p, 76), - height: utils.readU32(p, 80), - chainwork: new bn(p.slice(84, 116), 'le') + hash: utils.toHex(hash), + version: p.read32(), + prevBlock: p.readHash('hex'), + merkleRoot: p.readHash('hex'), + ts: p.readU32(), + bits: p.readU32(), + nonce: p.readU32(), + height: p.readU32(), + chainwork: new bn(p.readBytes(32), 'le') }); }; diff --git a/lib/bcoin/reader.js b/lib/bcoin/reader.js index fbccced1..e74d32a3 100644 --- a/lib/bcoin/reader.js +++ b/lib/bcoin/reader.js @@ -172,7 +172,9 @@ BufferReader.prototype.readString = function readString(enc, size) { return ret; }; -BufferReader.prototype.readHash = function readHash() { +BufferReader.prototype.readHash = function readHash(enc) { + if (enc) + return this.readBytes(32).toString(enc); return this.readBytes(32); }; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 15ec54a3..57e7d040 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -1182,6 +1182,52 @@ utils.writeU64BE = function writeU64BE(dst, num, off) { return 8; }; +utils.writeU256 = function writeU256(dst, num, off) { + var i; + + if (!(num instanceof bn)) + num = new bn(+num); + + off = off >>> 0; + + // We shouldn't think of + // this as negative. + if (num.isNeg()) + num = num.neg(); + + num = num.toArray('le', 32); + + assert.equal(num.length, 32); + + for (i = 0; i < num.length; i++) + dst[off++] = num[i] & 0xff; + + return 32; +}; + +utils.writeU256BE = function writeU256BE(dst, num, off) { + var i; + + if (!(num instanceof bn)) + num = new bn(+num); + + off = off >>> 0; + + // We shouldn't think of + // this as negative. + if (num.isNeg()) + num = num.neg(); + + num = num.toArray('be', 32); + + assert.equal(num.length, 32); + + for (i = 0; i < num.length; i++) + dst[off++] = num[i] & 0xff; + + return 32; +}; + utils.write8 = function write8(dst, num, off) { num = +num; off = off >>> 0;