From bf8475afe101dd31cea0395b483886e9113b49ec Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 14 Mar 2016 15:54:38 -0700 Subject: [PATCH] buffer writer. buffer reader. --- lib/bcoin/peer.js | 1 + lib/bcoin/protocol/framer.js | 200 ++++++++++ lib/bcoin/protocol/parser.js | 745 ++++++++++++++--------------------- lib/bcoin/script.js | 10 +- 4 files changed, 496 insertions(+), 460 deletions(-) diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 439ca067..56c94be8 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -138,6 +138,7 @@ Peer.prototype._init = function init() { }); this.parser.on('error', function(err) { + utils.debug(err.stack + ''); self._error(err); // Something is wrong here. // Ignore this peer. diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index a106d8f4..06ca41b5 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -818,6 +818,206 @@ Framer.mempool = function mempool() { return new Buffer([]); }; +function BufferWriter(size, offset) { + this.buffers = []; + if (Buffer.isBuffer(size)) { + this.offset = offset || 0; + this.size = 100; + this.buffers.push(size); + } else { + this.offset = 0; + this.size = size || 100; + } + this.data = new Buffer(this.size); +} + +BufferWriter.prototype.render = function render() { + this.buffers.push(this.data.slice(this.offset)); + var ret = Buffer.concat(this.buffers); + this.buffers.length = 0; + delete this.size; + delete this.offset; + delete this.buffers; + delete this.data; + return ret; +}; + +BufferWriter.prototype._realloc = function _realloc(size) { + if (this.data.length >= size) + return; + + var len = this.data.length; + var newSize = len + (len / 2 | 0); + + if (size > newSize) + newSize = size + (size / 2 | 0); + + this.buffers.push(this.data.slice(this.offset)); + this.data = new Buffer(newSize); +}; + +BufferWriter.prototype.writeU8 = function writeU8(value) { + this._realloc(this.offset + 1); + var ret = utils.writeU8(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU16 = function writeU16(value) { + this._realloc(this.offset + 2); + var ret = utils.writeU16(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU16BE = function writeU16BE(value) { + this._realloc(this.offset + 2); + var ret = utils.writeU16BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU32 = function writeU32(value) { + this._realloc(this.offset + 4); + var ret = utils.writeU32(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU32BE = function writeU32BE(value) { + this._realloc(this.offset + 4); + var ret = utils.writeU32BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU64 = function writeU64(value) { + this._realloc(this.offset + 8); + var ret = utils.writeU64(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeU64BE = function writeU64BE(value) { + this._realloc(this.offset + 8); + var ret = utils.writeU64BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write8 = function write8(value) { + this._realloc(this.offset + 1); + var ret = utils.write8(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write16 = function write16(value) { + this._realloc(this.offset + 2); + var ret = utils.write16(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write16BE = function write16BE(value) { + this._realloc(this.offset + 2); + var ret = utils.write16BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write32 = function write32(value) { + this._realloc(this.offset + 4); + var ret = utils.write32(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write32BE = function write32BE(value) { + this._realloc(this.offset + 4); + var ret = utils.write32BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write64 = function write64(value) { + this._realloc(this.offset + 8); + var ret = utils.write64(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.write64BE = function write64BE(value) { + this._realloc(this.offset + 8); + var ret = utils.write64BE(this.data, value, this.offset); + this.offset += ret; + return ret; +}; + +BufferWriter.prototype.writeBytes = function writeBytes(value) { + assert(Buffer.isBuffer(value)); + if (this.left() > value.length) + return utils.copy(value, this.data, 0); + this.buffers.push(this.data.slice(this.offset)); + this.buffers.push(value); + this.data = new Buffer(100); + this.offset += value.length; + return value.length; +}; + +BufferWriter.prototype.left = function left() { + return this.data.length - this.offset; +}; + +BufferWriter.prototype.getSize = function getSize() { + return this.offset; +}; + +BufferWriter.prototype.writeString = function writeString(value, enc) { + if (typeof value === 'string') + value = new Buffer(value, enc); + return this.writeBytes(value); +}; + +BufferWriter.prototype.writeHash = function writeHash(value) { + if (typeof value === 'string') + value = new Buffer(value, 'hex'); + assert(value.length === 32 || value.length === 20); + return this.writeBytes(value); +}; + +BufferWriter.prototype.writeVarString = function writeVarString(value, enc) { + var len = typeof value === 'string' + ? Buffer.byteLength(value, enc) + : value.length; + var ret = this.writeUIntv(len); + return this.writeString(value, enc) + ret; +}; + +BufferWriter.prototype.writeVarBytes = function writeVarBytes(value) { + var ret = this.writeUIntv(value.length); + return this.writeBytes(value) + ret; +}; + +BufferWriter.prototype.writeNullString = function writeNullString(value, enc) { + var ret = this.writeString(value, enc); + this.writeU8(0); + return ret + 1; +}; + +BufferWriter.prototype.writeIntv = function writeIntv(value) { + this._realloc(this.offset + utils.sizeIntv(value)); + return utils.writeIntv(this.data, value, this.offset); +}; + +BufferWriter.prototype.writeUIntv = function writeUIntv(value) { + if (value instanceof bn) + assert(value.cmpn(0) >= 0); + else + assert(value >= 0); + return this.writeIntv(value); +}; + /** * Expose */ diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index ae97fdc2..2b21a681 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -153,69 +153,60 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) { return p; }; -Parser.parsePing = function parsePing(p, off) { - off = off || 0; +Parser.parsePing = function parsePing(p) { + p = new BufferReader(p); - if (p.length - off < 8) + if (p.left() < 8) throw new Error('pong packet is too small'); return { - nonce: utils.readU64(p, off) + nonce: p.readU64() }; }; -Parser.parsePong = function parsePong(p, off) { - off = off || 0; - if (p.length - off < 8) +Parser.parsePong = function parsePong(p) { + p = new BufferReader(p); + + if (p.left() < 8) throw new Error('ping packet is too small'); return { - nonce: utils.readU64(p, off) + nonce: p.readU64() }; }; -Parser.parseVersion = function parseVersion(p, off) { - var v, services, ts, recv, from, nonce, result, off, agent, height, relay; - off = off || 0; +Parser.parseVersion = function parseVersion(p) { + var v, services, ts, recv, from, nonce, agent, height, relay; - if (p.length < 85) - throw new Error('version packet is too small'); + p = new BufferReader(p); - v = utils.readU32(p, off); - off += 4; - services = utils.readU64(p, off); - off += 8; + v = p.readU32(); + services = p.readU64(); // Timestamp - ts = utils.read64(p, off); - off += 8; + ts = p.read64(); // Our address (recv) - recv = Parser.parseAddress(p, off); - off += 26; + recv = Parser.parseAddress(p); // Their Address (from) - from = Parser.parseAddress(p, off); - off += 26; + from = Parser.parseAddress(p); // Nonce, very dramatic - nonce = utils.readU64(p, off); - off += 8; + nonce = p.readU64(); // User agent length - result = utils.readIntv(p, off); - off = result.off; - agent = p.toString('ascii', off, off + result.r); - off += result.r; + agent = p.readVarString('ascii'); // Start height - height = utils.readU32(p, off); - off += 4; + height = p.readU32(); // Relay - relay = p.length > off ? p[off] === 1 : true; - if (p.length > off) - off += 1; + try { + relay = p.readU8() === 1; + } catch (e) { + relay = true; + } try { ts = ts.toNumber(); @@ -246,72 +237,47 @@ Parser.parseVersion = function parseVersion(p, off) { }; }; -Parser.parseInvList = function parseInvList(p, off) { +Parser.parseInvList = function parseInvList(p) { var items = []; var i, count; - off = off || 0; - count = utils.readIntv(p, off); - off = count.off; - count = count.r; + p = new BufferReader(p); - if (p.length < count * 36) - throw new Error('Invalid getdata size'); + count = p.readUIntv(); - for (i = 0; i < count; i++, off += 36) { + for (i = 0; i < count; i++) { items.push({ - type: constants.invByVal[utils.readU32(p, off)], - hash: utils.slice(p, off + 4, off + 36) + type: constants.invByVal[p.readU32()], + hash: p.readHash() }); } return items; }; -Parser.parseMerkleBlock = function parseMerkleBlock(p, off) { - var i, hashCount, hashes, flagCount, flags; - off = off || 0; - var start = off; +Parser.parseMerkleBlock = function parseMerkleBlock(p) { + var version, prevBlock, merkleRoot, ts, bits, nonce, totalTX; + var i, hashCount, hashes, flags; - if (p.length - off < 86) - throw new Error('Invalid merkleblock size'); + p = new BufferReader(p); + p.start(); - var version = utils.read32(p, off); - off += 4; - var prevBlock = utils.slice(p, off, off + 32); - off += 32; - var merkleRoot = utils.slice(p, off, off + 32); - off += 32; - var ts = utils.readU32(p, off); - off += 4; - var bits = utils.readU32(p, off); - off += 4; - var nonce = utils.readU32(p, off); - off += 4; - var totalTX = utils.readU32(p, off); - off += 4; + version = p.read32(); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); + ts = p.readU32(); + bits = p.readU32(); + nonce = p.readU32(); + totalTX = p.readU32(); - hashCount = utils.readIntv(p, off); - off = hashCount.off; - hashCount = hashCount.r; - - if (off + 32 * hashCount + 1 > p.length) - throw new Error('Invalid hash count'); + hashCount = p.readUIntv(); hashes = new Array(hashCount); for (i = 0; i < hashCount; i++) - hashes[i] = utils.slice(p, off + i * 32, off + (i + 1) * 32); + hashes[i] = p.readHash(); - off = off + 32 * hashCount; - flagCount = utils.readIntv(p, off); - off = flagCount.off; - flagCount = flagCount.r; - - if (off + flagCount > p.length) - throw new Error('Invalid flag count'); - - flags = utils.slice(p, off, off + flagCount); + flags = p.readVarBytes(); return { version: version, @@ -323,82 +289,52 @@ Parser.parseMerkleBlock = function parseMerkleBlock(p, off) { totalTX: totalTX, hashes: hashes, flags: flags, - _size: off - start + _size: p.end() }; }; -Parser.parseHeaders = function parseHeaders(p, off) { +Parser.parseHeaders = function parseHeaders(p) { var headers = []; - var i, result, count, header, start, r; - off = off || 0; + var i, count; - if (p.length - off < 81) - throw new Error('Invalid headers size'); + p = new BufferReader(p); - result = utils.readIntv(p, off); - off = result.off; - count = result.r; - - if (p.length < count * 80) - throw new Error('Invalid headers size'); + count = p.readUIntv(); for (i = 0; i < count; i++) { - header = {}; - start = off; - header.version = utils.read32(p, off); - off += 4; - header.prevBlock = utils.slice(p, off, off + 32); - off += 32; - header.merkleRoot = utils.slice(p, off, off + 32); - off += 32; - header.ts = utils.readU32(p, off); - off += 4; - header.bits = utils.readU32(p, off); - off += 4; - header.nonce = utils.readU32(p, off); - off += 4; - r = utils.readIntv(p, off); - header.totalTX = r.r; - off = r.off; - headers.push(header); + headers.push({ + version: p.read32(), + prevBlock: p.readHash(), + merkleRoot: p.readHash(), + ts: p.readU32(), + bits: p.readU32(), + nonce: p.readU32(), + totalTX: p.readUIntv() + }); } return headers; }; -Parser.parseBlock = function parseBlock(p, off) { +Parser.parseBlock = function parseBlock(p) { var txs = []; var witnessSize = 0; - var i, result, off, totalTX, tx; - off = off || 0; - var start = off; + var version, prevBlock, merkleRoot, ts, bits, nonce; + var i, totalTX, tx; - if (p.length - off < 81) - throw new Error('Invalid block size'); + p = new BufferReader(p); + p.start(); - var version = utils.read32(p, off); - off += 4; - var prevBlock = utils.slice(p, off, off + 32); - off += 32; - var merkleRoot = utils.slice(p, off, off + 32); - off += 32; - var ts = utils.readU32(p, off); - off += 4; - var bits = utils.readU32(p, off); - off += 4; - var nonce = utils.readU32(p, off); - off += 4; - - result = utils.readIntv(p, off); - off = result.off; - totalTX = result.r; + version = p.read32(); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); + ts = p.readU32(); + bits = p.readU32(); + nonce = p.readU32(); + totalTX = p.readUIntv(); for (i = 0; i < totalTX; i++) { - tx = Parser.parseTX(p, off); - if (!tx) - throw new Error('Invalid tx count for block'); - tx._offset = off; - off += tx._size; + tx = Parser.parseTX(p); witnessSize += tx._witnessSize; txs.push(tx); } @@ -411,60 +347,47 @@ Parser.parseBlock = function parseBlock(p, off) { bits: bits, nonce: nonce, txs: txs, - _size: off - start, + _size: p.end(), _witnessSize: witnessSize }; }; -Parser.parseBlockCompact = function parseBlockCompact(p, off) { +Parser.parseBlockCompact = function parseBlockCompact(p) { var height = -1; - var i, result, off, totalTX, tx; - var inCount, input, s, version; - off = off || 0; - var start = off; + var version, prevBlock, merkleRoot, ts, bits, nonce; + var i, totalTX, tx; + var inCount, input, s, raw; - if (p.length - off < 81) - throw new Error('Invalid block size'); + p = new BufferReader(p); + p.start(); - version = utils.read32(p, off); - off += 4; + version = p.read32(); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); + ts = p.readU32(); + bits = p.readU32(); + nonce = p.readU32(); - var prevBlock = utils.slice(p, off, off + 32); - off += 32; - var merkleRoot = utils.slice(p, off, off + 32); - off += 32; - var ts = utils.readU32(p, off); - off += 4; - var bits = utils.readU32(p, off); - off += 4; - var nonce = utils.readU32(p, off); - off += 4; - - result = utils.readIntv(p, off); - off = result.off; - totalTX = result.r; + totalTX = p.readUIntv(); if (version > 1 && totalTX > 0) { - if (p.length < off + 10) - throw new Error('Invalid tx size'); + p.read32(); + inCount = p.readUIntv(); - inCount = utils.readIntv(p, off + 4); - off = inCount.off; - inCount = inCount.r; - - if (inCount > 0) { - input = Parser.parseInput(p, off); - if (!input) - throw new Error('Invalid tx count for block'); - } + if (inCount > 0) + input = Parser.parseInput(p); } if (input) { - s = bcoin.script.decode(input.script); + s = input.script; if (Buffer.isBuffer(s[0])) height = s[0]; } + raw = p.data; + + p.end(); + return { version: version, prevBlock: prevBlock, @@ -475,39 +398,24 @@ Parser.parseBlockCompact = function parseBlockCompact(p, off) { totalTX: totalTX, coinbaseHeight: height, txs: [], - _raw: p, - _size: off - start + _raw: raw, + _size: raw.length }; }; -Parser.parseInput = function parseInput(p, off) { - var scriptLen; +Parser.parseInput = function parseInput(p) { + var hash, index, script, sequence; - off = off || 0; - var start = off; + p = new BufferReader(p); + p.start(); - if (p.length - off < 41) - throw new Error('Invalid tx_in size'); - - var hash = utils.slice(p, off, off + 32); - off += 32; - var index = utils.readU32(p, off); - off += 4; - - scriptLen = utils.readIntv(p, off); - off = scriptLen.off; - scriptLen = scriptLen.r; - - if (off + scriptLen + 4 > p.length) - throw new Error('Invalid tx_in script length'); - - var script = bcoin.script.decode(utils.slice(p, off, off + scriptLen)); - off += scriptLen; - var sequence = utils.readU32(p, off); - off += 4; + hash = p.readHash(); + index = p.readU32(); + script = bcoin.script.decode(p.readVarBytes()); + sequence = p.readU32(); return { - _size: off - start, + _size: p.end(), prevout: { hash: hash, index: index @@ -517,71 +425,40 @@ Parser.parseInput = function parseInput(p, off) { }; }; -Parser.parseOutput = function parseOutput(p, off) { - var scriptLen; - off = off || 0; - var start = off; +Parser.parseOutput = function parseOutput(p) { + var value, script; - if (p.length - off < 9) - throw new Error('Invalid tx_out size'); + p = new BufferReader(p); + p.start(); - var value = utils.read64(p, off); - off += 8; - - scriptLen = utils.readIntv(p, off); - off = scriptLen.off; - scriptLen = scriptLen.r; - - if (off + scriptLen > p.length) - throw new Error('Invalid tx_out script length'); - - var script = bcoin.script.decode(utils.slice(p, off, off + scriptLen)); - off += scriptLen; + value = p.read64(); + script = bcoin.script.decode(p.readVarBytes()); return { - _size: off - start, + _size: p.end(), value: value, script: script }; }; -Parser.parseCoin = function parseCoin(p, extended, off) { - var version, height, value, script, hash, index, spent, scriptLen; - off = off || 0; +Parser.parseCoin = function parseCoin(p, extended) { + var version, height, value, script, hash, index, spent; + p = new BufferReader(p); - if (p.length < 17 + (extended ? 37 : 0)) - throw new Error('Invalid utxo size'); + version = p.readU32(); + height = p.readU32(); - version = utils.readU32(p, off); - off += 4; - - height = utils.readU32(p, off); if (height === 0x7fffffff) height = -1; - off += 4; - value = utils.read64(p, off); - off += 8; + value = p.read64(); - scriptLen = utils.readIntv(p, off); - off = scriptLen.off; - scriptLen = scriptLen.r; - - if (off + scriptLen > p.length - (extended ? 37 : 0)) - throw new Error('Invalid utxo script length'); - - script = bcoin.script.decode(utils.slice(p, off, off + scriptLen)); - off += scriptLen; + script = bcoin.script.decode(p.readVarBytes()); if (extended) { - hash = utils.slice(p, off, off + 32); - off += 32; - - index = utils.readU32(p, off); - off += 4; - - spent = utils.readU8(p, off) === 1; - off += 1; + hash = p.readHash(); + index = p.readU32(); + spent = p.readU8(); } else { hash = utils.slice(constants.zeroHash); index = 0xffffffff; @@ -599,80 +476,39 @@ Parser.parseCoin = function parseCoin(p, extended, off) { }; }; -Parser.parseTX = function parseTX(p, off) { +Parser.parseTX = function parseTX(p) { var inCount, txIn, tx; var outCount, txOut; var version, locktime, i; - var raw; - off = off || 0; - var start = off; - var block = off !== 0; + var raw, block; - if (p.length - off < 10) - throw new Error('Invalid tx size'); + p = new BufferReader(p); + p.start(); - if (Parser.isWitnessTX(p, off)) - return Parser.parseWitnessTX(p, off); + block = p.offset !== 0; - version = utils.readU32(p, off); - off += 4; + if (Parser.isWitnessTX(p)) + return Parser.parseWitnessTX(p); - inCount = utils.readIntv(p, off); - off = inCount.off; - inCount = inCount.r; - - if (inCount < 0) - throw new Error('Invalid tx_in count (negative)'); - - if (off + 41 * inCount + 5 > p.length) - throw new Error('Invalid tx_in count (too big)'); + version = p.readU32(); + inCount = p.readUIntv(); txIn = new Array(inCount); for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p, off); - - if (!tx) - return; + tx = Parser.parseInput(p); txIn[i] = tx; txIn[i].witness = []; - tx._offset = off; - off += tx._size; - - if (off + 5 > p.length) - throw new Error('Invalid tx_in offset'); } - outCount = utils.readIntv(p, off); - off = outCount.off; - outCount = outCount.r; - if (outCount < 0) - throw new Error('Invalid tx_out count (negative)'); - if (off + 9 * outCount + 4 > p.length) - throw new Error('Invalid tx_out count (too big)'); - + outCount = p.readUIntv(); txOut = new Array(outCount); for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p, off); - - if (!tx) - return; - + tx = Parser.parseOutput(p); txOut[i] = tx; - tx._offset = off; - off += tx._size; - - if (off + 4 > p.length) - throw new Error('Invalid tx_out offset'); } - locktime = utils.readU32(p, off); - off += 4; - - // raw = p.length !== off ? p.slice(0, off) : p; - - // if (block) - // raw = utils.slice(raw); + locktime = p.readU32(); return { version: version, @@ -681,39 +517,35 @@ Parser.parseTX = function parseTX(p, off) { locktime: locktime, _witnessSize: 0, // _raw: raw, - _size: off - start + _size: p.end() }; }; -Parser.isWitnessTX = function isWitnessTX(p, off) { - off = off || 0; +Parser.isWitnessTX = function isWitnessTX(p) { + p = new BufferReader(p); - if (p.length - off < 12) + if (p.left() < 12) return false; - return p[off + 4] === 0 && p[off + 5] !== 0; + return p[p.offset + 4] === 0 && p[p.offset + 5] !== 0; }; -Parser.parseWitnessTX = function parseWitnessTX(p, off) { +Parser.parseWitnessTX = function parseWitnessTX(p) { var inCount, txIn, tx; var outCount, txOut; var marker, flag; var version, locktime, i; var witnessSize = 0; - var raw; - off = off || 0; - var start = off; - var block = off !== 0; + var raw, block; - if (p.length - off < 12) - throw new Error('Invalid witness tx size'); + p = new BufferReader(p); + p.start(); - version = utils.readU32(p, off); - off += 4; - marker = utils.readU8(p, off); - off += 1; - flag = utils.readU8(p, off); - off += 1; + block = p.offset !== 0; + + version = p.readU32(); + marker = p.readU8(); + flag = p.readU8(); if (marker !== 0) throw new Error('Invalid witness tx (marker != 0)'); @@ -721,77 +553,29 @@ Parser.parseWitnessTX = function parseWitnessTX(p, off) { if (flag === 0) throw new Error('Invalid witness tx (flag == 0)'); - inCount = utils.readIntv(p, off); - off = inCount.off; - inCount = inCount.r; - - if (inCount < 0) - throw new Error('Invalid witness tx_in count (negative)'); - - if (off + 41 * inCount + 5 > p.length) - throw new Error('Invalid witness tx_in count (too big)'); + inCount = p.readUIntv(); txIn = new Array(inCount); for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p, off); - - if (!tx) - return; - + tx = Parser.parseInput(p); txIn[i] = tx; - tx._offset = off; - off += tx._size; - - if (off + 5 > p.length) - throw new Error('Invalid witness tx_in offset'); } - outCount = utils.readIntv(p, off); - off = outCount.off; - outCount = outCount.r; - if (outCount < 0) - throw new Error('Invalid witness tx_out count (negative)'); - if (off + 9 * outCount + 4 > p.length) - throw new Error('Invalid witness tx_out count (too big)'); + outCount = p.readUIntv(); txOut = new Array(outCount); for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p, off); - - if (!tx) - return; - + tx = Parser.parseOutput(p); txOut[i] = tx; - tx._offset = off; - off += tx._size; - - if (off + 4 > p.length) - throw new Error('Invalid tx_out offset'); } for (i = 0; i < inCount; i++) { - tx = Parser.parseWitness(p, off); - - if (!tx) - return; - + tx = Parser.parseWitness(p); txIn[i].witness = tx.witness; - txIn[i]._witnessSize = tx._size; - txIn[i]._witnessOffset = off; - off += tx._size; witnessSize += tx._size; - - if (off + 4 > p.length) - throw new Error('Invalid witness offset'); } - locktime = utils.readU32(p, off); - off += 4; - - // raw = p.length !== off ? p.slice(0, off) : p; - - // if (block) - // raw = utils.slice(raw); + locktime = p.readU32(); return { version: version, @@ -801,70 +585,38 @@ Parser.parseWitnessTX = function parseWitnessTX(p, off) { outputs: txOut, locktime: locktime, // _raw: raw, - _size: off - start, + _size: p.end(), _witnessSize: witnessSize + 2 }; }; -Parser.parseWitness = function parseWitness(p, off) { +Parser.parseWitness = function parseWitness(p) { var witness = []; - var chunkCount, chunkSize, item, i; - off = off || 0; - var start = off; + var chunkCount, item, i; - chunkCount = utils.readIntv(p, off); - off = chunkCount.off; - chunkCount = chunkCount.r; + p = new BufferReader(p); + p.start(); - for (i = 0; i < chunkCount; i++) { - chunkSize = utils.readIntv(p, off); - off = chunkSize.off; - chunkSize = chunkSize.r; - item = utils.slice(p, off, off + chunkSize); - off += chunkSize; - witness.push(item); - if (off > p.length) - throw new Error('Invalid witness offset'); - } + chunkCount = p.readUIntv(); + + for (i = 0; i < chunkCount; i++) + witness.push(p.readVarBytes()); return { - _size: off - start, + _size: p.end(), witness: witness }; }; -Parser.parseReject = function parseReject(p, off) { - var messageLen, off, message, ccode, reasonLen, reason, data; - off = off || 0; +Parser.parseReject = function parseReject(p) { + var message, ccode, reason, data; - if (p.length < 3) - throw new Error('Invalid reject size'); + p = new BufferReader(p); - messageLen = utils.readIntv(p, off); - off = messageLen.off; - messageLen = messageLen.r; - - if (off + messageLen + 2 > p.length) - throw new Error('Invalid reject message'); - - message = p.toString('ascii', off, off + messageLen); - off += messageLen; - - ccode = utils.readU8(p, off); - off++; - - reasonLen = utils.readIntv(p, off); - off = reasonLen.off; - reasonLen = reasonLen.r; - - if (off + reasonLen > p.length) - throw new Error('Invalid reject reason'); - - reason = p.toString('ascii', off, off + reasonLen); - - off += reasonLen; - - data = utils.slice(p, off, off + 32); + message = p.readVarString('ascii'); + ccode = p.readU8(); + reason = p.readVarString('ascii'); + data = p.readHash(); return { message: message, @@ -874,27 +626,22 @@ Parser.parseReject = function parseReject(p, off) { }; }; -Parser.parseAddress = function parseAddress(p, off, full) { +Parser.parseAddress = function parseAddress(p, full) { var ts, services, ip, port; - if (!off) - off = 0; + p = new BufferReader(p); if (full) { - ts = utils.readU32(p, off); - off += 4; + ts = p.readU32(); } else { ts = 0; } - services = utils.readU64(p, off); - off += 8; + services = p.readU64(); - ip = utils.slice(p, off, off + 16); - off += 16; + ip = p.readBytes(16); - port = utils.readU16BE(p, off); - off += 2; + port = p.readU16BE(); try { services = services.toNumber(); @@ -915,134 +662,222 @@ Parser.parseAddress = function parseAddress(p, off, full) { }; }; -Parser.parseAddr = function parseAddr(p, off) { - if (p.length < 31) - throw new Error('Invalid addr size'); - +Parser.parseAddr = function parseAddr(p) { var addrs = []; var i, count; - off = off || 0; - count = utils.readIntv(p, off); - off = count.off; - count = count.r; + p = new BufferReader(p); - for (i = 0; i < count && off < p.length; i++) { - addrs.push(Parser.parseAddress(p, off, true)); - off += 30; - } + count = p.readUIntv(); + + for (i = 0; i < count; i++) + addrs.push(Parser.parseAddress(p, true)); return addrs; }; -Parser.parseMempool = function parseMempool(p, off) { - off = off || 0; - - if (p.length - off > 0) - throw new Error('Invalid mempool size'); - +Parser.parseMempool = function parseMempool(p) { return {}; }; function BufferReader(data, offset) { + if (data instanceof BufferReader) + return data; this.data = data; this.offset = offset || 0; + this.stack = []; } +BufferReader.prototype.start = function start() { + this.stack.push(this.offset); +}; + +BufferReader.prototype.end = function end() { + assert(this.stack.length > 0); + var start = this.stack.pop(); + var end = this.offset; + if (this.stack.length === 0) { + delete this.offset; + delete this.stack; + delete this.data; + } + return end - start; +}; + +BufferReader.prototype.endData = function endData() { + assert(this.stack.length > 0); + var start = this.stack.pop(); + var end = this.offset; + var size = end - start; + var data = this.data; + + if (this.stack.length === 0) { + delete this.offset; + delete this.stack; + delete this.data; + } + + if (size === data.length) + return data; + + return utils.slice(data, start, end); +}; + BufferReader.prototype.readU8 = function readU8() { + assert(this.offset + 1 <= this.data.length); var ret = utils.readU8(this.data, this.offset); this.offset += 1; return ret; }; BufferReader.prototype.readU16 = function readU16() { + assert(this.offset + 2 <= this.data.length); var ret = utils.readU16(this.data, this.offset); this.offset += 2; return ret; }; BufferReader.prototype.readU16BE = function readU16BE() { + assert(this.offset + 2 <= this.data.length); var ret = utils.readU16BE(this.data, this.offset); this.offset += 2; return ret; }; BufferReader.prototype.readU32 = function readU32() { + assert(this.offset + 4 <= this.data.length); var ret = utils.readU32(this.data, this.offset); this.offset += 4; return ret; }; BufferReader.prototype.readU32BE = function readU32BE() { + assert(this.offset + 4 <= this.data.length); var ret = utils.readU32BE(this.data, this.offset); this.offset += 4; return ret; }; BufferReader.prototype.readU64 = function readU64() { + assert(this.offset + 8 <= this.data.length); var ret = utils.readU64(this.data, this.offset); this.offset += 8; return ret; }; BufferReader.prototype.readU64BE = function readU64BE() { + assert(this.offset + 8 <= this.data.length); var ret = utils.readU64BE(this.data, this.offset); this.offset += 8; return ret; }; BufferReader.prototype.read8 = function read8() { + assert(this.offset + 1 <= this.data.length); var ret = utils.read8(this.data, this.offset); this.offset += 1; return ret; }; BufferReader.prototype.read16 = function read16() { + assert(this.offset + 2 <= this.data.length); var ret = utils.read16(this.data, this.offset); this.offset += 2; return ret; }; BufferReader.prototype.read16BE = function read16BE() { + assert(this.offset + 2 <= this.data.length); var ret = utils.read16BE(this.data, this.offset); this.offset += 2; return ret; }; BufferReader.prototype.read32 = function read32() { + assert(this.offset + 4 <= this.data.length); var ret = utils.read32(this.data, this.offset); this.offset += 4; return ret; }; BufferReader.prototype.read32BE = function read32BE() { + assert(this.offset + 4 <= this.data.length); var ret = utils.read32BE(this.data, this.offset); this.offset += 4; return ret; }; BufferReader.prototype.read64 = function read64() { + assert(this.offset + 8 <= this.data.length); var ret = utils.read64(this.data, this.offset); this.offset += 8; return ret; }; BufferReader.prototype.read64BE = function read64BE() { + assert(this.offset + 8 <= this.data.length); var ret = utils.read64BE(this.data, this.offset); this.offset += 8; return ret; }; -BufferReader.prototype.slice = function slice(size) { - if (size == null) - size = this.data.length - this.offset; +BufferReader.prototype.readBytes = function readBytes(size) { + assert(size > 0) + assert(this.offset + size <= this.data.length); var ret = utils.slice(this.data, this.offset, this.offset + size); this.offset += size; return ret; }; +BufferReader.prototype.readString = function readString(enc, size) { + assert(size > 0) + assert(this.offset + size <= this.data.length); + var ret = this.data.toString(enc, this.offset, this.offset + size); + this.offset += size; + return ret; +}; + BufferReader.prototype.readHash = function readHash() { - return this.slice(32); + return this.readBytes(32); +}; + +BufferReader.prototype.readVarString = function readVarString(enc) { + return this.readString(enc, this.readUIntv()); +}; + +BufferReader.prototype.readVarBytes = function readVarBytes() { + return this.readBytes(this.readUIntv()); +}; + +BufferReader.prototype.readNullString = function readNullString(enc) { + assert(this.offset + 1 <= this.data.length); + for (var i = this.offset; i < this.data.length; i++) { + if (this.data[i] === 0) + break; + } + assert(i !== this.data.length); + var ret = this.readString(enc, i - this.offset); + this.offset = i + 1; + return ret; +}; + +BufferReader.prototype.left = function left() { + assert(this.offset <= this.data.length); + return this.data.length - this.offset; +}; + +BufferReader.prototype.readIntv = function readIntv() { + assert(this.offset + 1 <= this.data.length); + var result = utils.readIntv(this.data, this.offset); + assert(result.off <= this.data.length); + this.offset = result.off; + return result.r; +}; + +BufferReader.prototype.readUIntv = function readUIntv() { + var result = this.readIntv(); + assert(result >= 0); + return result; }; /** diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 8917f8ff..09fe4189 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -28,7 +28,7 @@ script.decode = function decode(buf) { // Next `b` bytes should be pushed to stack if (b >= 0x01 && b <= 0x4b) { - opcodes.push(buf.slice(i, i + b)); + opcodes.push(utils.slice(buf, i, i + b)); i += b; utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { opcode: null, @@ -63,7 +63,7 @@ script.decode = function decode(buf) { if (opcode === 'pushdata1') { len = buf[i]; i += 1; - opcodes.push(buf.slice(i, i + len)); + opcodes.push(utils.slice(buf, i, i + len)); i += len; utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { opcode: opcode, @@ -72,7 +72,7 @@ script.decode = function decode(buf) { } else if (opcode === 'pushdata2') { len = utils.readU16(buf, i); i += 2; - opcodes.push(buf.slice(i, i + len)); + opcodes.push(utils.slice(buf, i, i + len)); i += len; utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { opcode: opcode, @@ -81,7 +81,7 @@ script.decode = function decode(buf) { } else if (opcode === 'pushdata4') { len = utils.readU32(buf, i); i += 4; - opcodes.push(buf.slice(i, i + len)); + opcodes.push(utils.slice(buf, i, i + len)); i += len; utils.hidden(opcodes[opcodes.length - 1], 'pushdata', { opcode: opcode, @@ -92,7 +92,7 @@ script.decode = function decode(buf) { } } - utils.hidden(opcodes, '_raw', buf); + utils.hidden(opcodes, '_raw', utils.slice(buf)); return opcodes; };