diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index ceeaa926..ae97fdc2 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -54,7 +54,6 @@ Parser.prototype.feed = function feed(data) { this.pending.shift(); else this.pending[0] = this.pending[0].slice(len); - //this.pending[0] = this.pending[0].slice(len); off += len; } @@ -154,47 +153,57 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) { return p; }; -Parser.parsePing = function parsePing(p) { - if (p.length < 8) +Parser.parsePing = function parsePing(p, off) { + off = off || 0; + + if (p.length - off < 8) throw new Error('pong packet is too small'); return { - nonce: utils.readU64(p, 0) + nonce: utils.readU64(p, off) }; }; -Parser.parsePong = function parsePong(p) { - if (p.length < 8) +Parser.parsePong = function parsePong(p, off) { + off = off || 0; + if (p.length - off < 8) throw new Error('ping packet is too small'); return { - nonce: utils.readU64(p, 0) + nonce: utils.readU64(p, off) }; }; -Parser.parseVersion = function parseVersion(p) { +Parser.parseVersion = function parseVersion(p, off) { var v, services, ts, recv, from, nonce, result, off, agent, height, relay; + off = off || 0; if (p.length < 85) throw new Error('version packet is too small'); - v = utils.readU32(p, 0); - services = utils.readU64(p, 4); + v = utils.readU32(p, off); + off += 4; + services = utils.readU64(p, off); + off += 8; // Timestamp - ts = utils.read64(p, 12); + ts = utils.read64(p, off); + off += 8; // Our address (recv) - recv = Parser.parseAddress(p, 20); + recv = Parser.parseAddress(p, off); + off += 26; // Their Address (from) - from = Parser.parseAddress(p, 46); + from = Parser.parseAddress(p, off); + off += 26; // Nonce, very dramatic - nonce = utils.readU64(p, 72); + nonce = utils.readU64(p, off); + off += 8; // User agent length - result = utils.readIntv(p, 80); + result = utils.readIntv(p, off); off = result.off; agent = p.toString('ascii', off, off + result.r); off += result.r; @@ -205,6 +214,8 @@ Parser.parseVersion = function parseVersion(p) { // Relay relay = p.length > off ? p[off] === 1 : true; + if (p.length > off) + off += 1; try { ts = ts.toNumber(); @@ -235,18 +246,19 @@ Parser.parseVersion = function parseVersion(p) { }; }; -Parser.parseInvList = function parseInvList(p) { +Parser.parseInvList = function parseInvList(p, off) { var items = []; - var i, off, count; + var i, count; + off = off || 0; - count = utils.readIntv(p, 0); - p = p.slice(count.off); + count = utils.readIntv(p, off); + off = count.off; count = count.r; if (p.length < count * 36) throw new Error('Invalid getdata size'); - for (i = 0, off = 0; i < count; i++, off += 36) { + for (i = 0; i < count; i++, off += 36) { items.push({ type: constants.invByVal[utils.readU32(p, off)], hash: utils.slice(p, off + 4, off + 36) @@ -256,13 +268,30 @@ Parser.parseInvList = function parseInvList(p) { return items; }; -Parser.parseMerkleBlock = function parseMerkleBlock(p) { - var i, hashCount, off, hashes, flagCount, flags; +Parser.parseMerkleBlock = function parseMerkleBlock(p, off) { + var i, hashCount, hashes, flagCount, flags; + off = off || 0; + var start = off; - if (p.length < 86) + if (p.length - off < 86) throw new Error('Invalid merkleblock size'); - hashCount = utils.readIntv(p, 84); + 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; + + hashCount = utils.readIntv(p, off); off = hashCount.off; hashCount = hashCount.r; @@ -285,27 +314,28 @@ Parser.parseMerkleBlock = function parseMerkleBlock(p) { flags = utils.slice(p, off, off + flagCount); return { - version: utils.read32(p, 0), - prevBlock: utils.slice(p, 4, 36), - merkleRoot: utils.slice(p, 36, 68), - ts: utils.readU32(p, 68), - bits: utils.readU32(p, 72), - nonce: utils.readU32(p, 76), - totalTX: utils.readU32(p, 80), + version: version, + prevBlock: prevBlock, + merkleRoot: merkleRoot, + ts: ts, + bits: bits, + nonce: nonce, + totalTX: totalTX, hashes: hashes, flags: flags, - _size: p.length + _size: off - start }; }; -Parser.parseHeaders = function parseHeaders(p) { +Parser.parseHeaders = function parseHeaders(p, off) { var headers = []; - var i, result, off, count, header, start, r; + var i, result, count, header, start, r; + off = off || 0; - if (p.length < 81) + if (p.length - off < 81) throw new Error('Invalid headers size'); - result = utils.readIntv(p, 0); + result = utils.readIntv(p, off); off = result.off; count = result.r; @@ -336,20 +366,35 @@ Parser.parseHeaders = function parseHeaders(p) { return headers; }; -Parser.parseBlock = function parseBlock(p) { +Parser.parseBlock = function parseBlock(p, off) { var txs = []; var witnessSize = 0; var i, result, off, totalTX, tx; + off = off || 0; + var start = off; - if (p.length < 81) + if (p.length - off < 81) throw new Error('Invalid block size'); - result = utils.readIntv(p, 80); + 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; for (i = 0; i < totalTX; i++) { - tx = Parser.parseTX(p.slice(off), true); + tx = Parser.parseTX(p, off); if (!tx) throw new Error('Invalid tx count for block'); tx._offset = off; @@ -359,30 +404,43 @@ Parser.parseBlock = function parseBlock(p) { } return { - version: utils.read32(p, 0), - prevBlock: utils.slice(p, 4, 36), - merkleRoot: utils.slice(p, 36, 68), - ts: utils.readU32(p, 68), - bits: utils.readU32(p, 72), - nonce: utils.readU32(p, 76), - totalTX: totalTX, + version: version, + prevBlock: prevBlock, + merkleRoot: merkleRoot, + ts: ts, + bits: bits, + nonce: nonce, txs: txs, - _size: p.length, + _size: off - start, _witnessSize: witnessSize }; }; -Parser.parseBlockCompact = function parseBlockCompact(p) { +Parser.parseBlockCompact = function parseBlockCompact(p, off) { var height = -1; var i, result, off, totalTX, tx; var inCount, input, s, version; + off = off || 0; + var start = off; - if (p.length < 81) + if (p.length - off < 81) throw new Error('Invalid block size'); - version = utils.read32(p, 0); + version = utils.read32(p, off); + off += 4; - result = utils.readIntv(p, 80); + 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; @@ -395,7 +453,7 @@ Parser.parseBlockCompact = function parseBlockCompact(p) { inCount = inCount.r; if (inCount > 0) { - input = Parser.parseInput(p.slice(off)); + input = Parser.parseInput(p, off); if (!input) throw new Error('Invalid tx count for block'); } @@ -409,66 +467,87 @@ Parser.parseBlockCompact = function parseBlockCompact(p) { return { version: version, - prevBlock: utils.slice(p, 4, 36), - merkleRoot: utils.slice(p, 36, 68), - ts: utils.readU32(p, 68), - bits: utils.readU32(p, 72), - nonce: utils.readU32(p, 76), + prevBlock: prevBlock, + merkleRoot: merkleRoot, + ts: ts, + bits: bits, + nonce: nonce, totalTX: totalTX, coinbaseHeight: height, txs: [], _raw: p, - _size: p.length + _size: off - start }; }; -Parser.parseInput = function parseInput(p) { - var scriptLen, off; +Parser.parseInput = function parseInput(p, off) { + var scriptLen; - if (p.length < 41) + off = off || 0; + var start = off; + + if (p.length - off < 41) throw new Error('Invalid tx_in size'); - scriptLen = utils.readIntv(p, 36); + 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; + return { - _size: off + scriptLen + 4, + _size: off - start, prevout: { - hash: utils.slice(p, 0, 32), - index: utils.readU32(p, 32) + hash: hash, + index: index }, - script: bcoin.script.decode(utils.slice(p, off, off + scriptLen)), - sequence: utils.readU32(p, off + scriptLen) + script: script, + sequence: sequence }; }; -Parser.parseOutput = function parseOutput(p) { - var scriptLen, off; +Parser.parseOutput = function parseOutput(p, off) { + var scriptLen; + off = off || 0; + var start = off; - if (p.length < 9) + if (p.length - off < 9) throw new Error('Invalid tx_out size'); - scriptLen = utils.readIntv(p, 8); + 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; + return { - _size: off + scriptLen, - value: utils.read64(p, 0), - script: bcoin.script.decode(utils.slice(p, off, off + scriptLen)) + _size: off - start, + value: value, + script: script }; }; -Parser.parseCoin = function parseCoin(p, extended) { - var off = 0; +Parser.parseCoin = function parseCoin(p, extended, off) { var version, height, value, script, hash, index, spent, scriptLen; + off = off || 0; if (p.length < 17 + (extended ? 37 : 0)) throw new Error('Invalid utxo size'); @@ -520,18 +599,20 @@ Parser.parseCoin = function parseCoin(p, extended) { }; }; -Parser.parseTX = function parseTX(p, block) { - var off = 0; +Parser.parseTX = function parseTX(p, off) { var inCount, txIn, tx; var outCount, txOut; var version, locktime, i; var raw; + off = off || 0; + var start = off; + var block = off !== 0; - if (p.length < 10) + if (p.length - off < 10) throw new Error('Invalid tx size'); - if (Parser.isWitnessTX(p)) - return Parser.parseWitnessTX(p, block); + if (Parser.isWitnessTX(p, off)) + return Parser.parseWitnessTX(p, off); version = utils.readU32(p, off); off += 4; @@ -548,7 +629,7 @@ Parser.parseTX = function parseTX(p, block) { txIn = new Array(inCount); for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p.slice(off)); + tx = Parser.parseInput(p, off); if (!tx) return; @@ -572,7 +653,7 @@ Parser.parseTX = function parseTX(p, block) { txOut = new Array(outCount); for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p.slice(off)); + tx = Parser.parseOutput(p, off); if (!tx) return; @@ -600,27 +681,31 @@ Parser.parseTX = function parseTX(p, block) { locktime: locktime, _witnessSize: 0, // _raw: raw, - _size: off + _size: off - start }; }; -Parser.isWitnessTX = function isWitnessTX(p) { - if (p.length < 12) +Parser.isWitnessTX = function isWitnessTX(p, off) { + off = off || 0; + + if (p.length - off < 12) return false; - return p[4] === 0 && p[5] !== 0; + return p[off + 4] === 0 && p[off + 5] !== 0; }; -Parser.parseWitnessTX = function parseWitnessTX(p, block) { - var off = 0; +Parser.parseWitnessTX = function parseWitnessTX(p, off) { 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; - if (p.length < 12) + if (p.length - off < 12) throw new Error('Invalid witness tx size'); version = utils.readU32(p, off); @@ -648,7 +733,7 @@ Parser.parseWitnessTX = function parseWitnessTX(p, block) { txIn = new Array(inCount); for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p.slice(off)); + tx = Parser.parseInput(p, off); if (!tx) return; @@ -671,7 +756,7 @@ Parser.parseWitnessTX = function parseWitnessTX(p, block) { txOut = new Array(outCount); for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p.slice(off)); + tx = Parser.parseOutput(p, off); if (!tx) return; @@ -685,7 +770,7 @@ Parser.parseWitnessTX = function parseWitnessTX(p, block) { } for (i = 0; i < inCount; i++) { - tx = Parser.parseWitness(p.slice(off)); + tx = Parser.parseWitness(p, off); if (!tx) return; @@ -716,15 +801,16 @@ Parser.parseWitnessTX = function parseWitnessTX(p, block) { outputs: txOut, locktime: locktime, // _raw: raw, - _size: off, + _size: off - start, _witnessSize: witnessSize + 2 }; }; -Parser.parseWitness = function parseWitness(p) { +Parser.parseWitness = function parseWitness(p, off) { var witness = []; - var off = 0; var chunkCount, chunkSize, item, i; + off = off || 0; + var start = off; chunkCount = utils.readIntv(p, off); off = chunkCount.off; @@ -742,25 +828,26 @@ Parser.parseWitness = function parseWitness(p) { } return { - _size: off, + _size: off - start, witness: witness }; }; -Parser.parseReject = function parseReject(p) { +Parser.parseReject = function parseReject(p, off) { var messageLen, off, message, ccode, reasonLen, reason, data; + off = off || 0; if (p.length < 3) throw new Error('Invalid reject size'); - messageLen = utils.readIntv(p, 0); + 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.slice(off, off + messageLen).toString('ascii'); + message = p.toString('ascii', off, off + messageLen); off += messageLen; ccode = utils.readU8(p, off); @@ -773,7 +860,7 @@ Parser.parseReject = function parseReject(p) { if (off + reasonLen > p.length) throw new Error('Invalid reject reason'); - reason = p.slice(off, off + reasonLen).toString('ascii'); + reason = p.toString('ascii', off, off + reasonLen); off += reasonLen; @@ -828,14 +915,15 @@ Parser.parseAddress = function parseAddress(p, off, full) { }; }; -Parser.parseAddr = function parseAddr(p) { +Parser.parseAddr = function parseAddr(p, off) { if (p.length < 31) throw new Error('Invalid addr size'); var addrs = []; - var i, off, count; + var i, count; + off = off || 0; - count = utils.readIntv(p, 0); + count = utils.readIntv(p, off); off = count.off; count = count.r; @@ -847,13 +935,116 @@ Parser.parseAddr = function parseAddr(p) { return addrs; }; -Parser.parseMempool = function parseMempool(p) { - if (p.length > 0) +Parser.parseMempool = function parseMempool(p, off) { + off = off || 0; + + if (p.length - off > 0) throw new Error('Invalid mempool size'); return {}; }; +function BufferReader(data, offset) { + this.data = data; + this.offset = offset || 0; +} + +BufferReader.prototype.readU8 = function readU8() { + var ret = utils.readU8(this.data, this.offset); + this.offset += 1; + return ret; +}; + +BufferReader.prototype.readU16 = function readU16() { + var ret = utils.readU16(this.data, this.offset); + this.offset += 2; + return ret; +}; + +BufferReader.prototype.readU16BE = function readU16BE() { + var ret = utils.readU16BE(this.data, this.offset); + this.offset += 2; + return ret; +}; + +BufferReader.prototype.readU32 = function readU32() { + var ret = utils.readU32(this.data, this.offset); + this.offset += 4; + return ret; +}; + +BufferReader.prototype.readU32BE = function readU32BE() { + var ret = utils.readU32BE(this.data, this.offset); + this.offset += 4; + return ret; +}; + +BufferReader.prototype.readU64 = function readU64() { + var ret = utils.readU64(this.data, this.offset); + this.offset += 8; + return ret; +}; + +BufferReader.prototype.readU64BE = function readU64BE() { + var ret = utils.readU64BE(this.data, this.offset); + this.offset += 8; + return ret; +}; + +BufferReader.prototype.read8 = function read8() { + var ret = utils.read8(this.data, this.offset); + this.offset += 1; + return ret; +}; + +BufferReader.prototype.read16 = function read16() { + var ret = utils.read16(this.data, this.offset); + this.offset += 2; + return ret; +}; + +BufferReader.prototype.read16BE = function read16BE() { + var ret = utils.read16BE(this.data, this.offset); + this.offset += 2; + return ret; +}; + +BufferReader.prototype.read32 = function read32() { + var ret = utils.read32(this.data, this.offset); + this.offset += 4; + return ret; +}; + +BufferReader.prototype.read32BE = function read32BE() { + var ret = utils.read32BE(this.data, this.offset); + this.offset += 4; + return ret; +}; + +BufferReader.prototype.read64 = function read64() { + var ret = utils.read64(this.data, this.offset); + this.offset += 8; + return ret; +}; + +BufferReader.prototype.read64BE = function read64BE() { + 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; + var ret = utils.slice(this.data, this.offset, this.offset + size); + this.offset += size; + return ret; +}; + +BufferReader.prototype.readHash = function readHash() { + return this.slice(32); +}; + /** * Expose */