diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 03902ff3..ddf62384 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -911,6 +911,63 @@ Framer.renderTX = function renderTX(tx, useWitness, writer) { return p; }; +/** + * Serialize a transaction to BCoin "extended format". + * This is the serialization format BCoin uses internally + * to store transactions in the database. The extended + * serialization includes the height, block hash, index, + * timestamp, pending-since time, and optionally a vector + * for the serialized coins. + * @param {NakedTX|TX} tx + * @param {Boolean?} saveCoins - Whether to serialize the coins. + * @param {String?} enc - One of `"hex"` or `null`. + * @returns {Buffer} + */ + +Framer.extendedTX = function extendedTX(tx, saveCoins, writer) { + var height = tx.height; + var index = tx.index; + var changeIndex = tx.changeIndex != null ? tx.changeIndex : -1; + var p = new BufferWriter(writer); + var i, input, tmp; + + if (height === -1) + height = 0x7fffffff; + + if (index === -1) + index = 0x7fffffff; + + if (changeIndex === -1) + changeIndex = 0x7fffffff; + + Framer.renderTX(tx, true, p); + p.writeU32(height); + p.writeHash(tx.block || constants.ZERO_HASH); + p.writeU32(index); + p.writeU32(tx.ts); + p.writeU32(tx.ps); + // p.writeU32(changeIndex); + + if (saveCoins) { + p.writeVarint(tx.inputs.length); + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + + if (!input.coin) { + p.writeVarint(0); + continue; + } + + p.writeVarBytes(Framer.coin(input.coin, false)); + } + } + + if (!writer) + p = p.render(); + + return p; +}; + Framer._block = function _block(block, useWitness, writer) { var p = new BufferWriter(writer); var witnessSize = 0; diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 869f9d37..db4a056d 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -692,6 +692,57 @@ Parser.parseBlockHeaders = function parseBlockHeaders(p) { }; }; +/** + * Parse a transaction in "extended" serialization format. + * @param {Buffer} p + * @param {Boolean?} saveCoins - If true, the function will + * attempt to parse the coins. + * @param {String?} enc - One of `"hex"` or `null`. + * @returns {NakedTX} - A "naked" transaction object. + */ + +Parser.parseExtendedTX = function parseExtendedTX(p, saveCoins) { + var tx, coinCount, coin, i, tmp; + + p = new BufferReader(p); + + tx = Parser.parseTX(p); + + tx.height = p.readU32(); + tx.block = p.readHash('hex'); + tx.index = p.readU32(); + tx.ts = p.readU32(); + tx.ps = p.readU32(); + // tx.changeIndex = p.readU32(); + + if (tx.block === constants.NULL_HASH) + tx.block = null; + + if (tx.height === 0x7fffffff) + tx.height = -1; + + if (tx.index === 0x7fffffff) + tx.index = -1; + + if (tx.changeIndex === 0x7fffffff) + tx.changeIndex = -1; + + if (saveCoins) { + coinCount = p.readVarint(); + for (i = 0; i < coinCount; i++) { + coin = p.readVarBytes(); + if (coin.length === 0) + continue; + coin = Parser.parseCoin(coin, false); + coin.hash = tx.inputs[i].prevout.hash; + coin.index = tx.inputs[i].prevout.index; + tx.inputs[i].coin = coin; + } + } + + return tx; +}; + /** * Parse block packet. * @param {Buffer|BufferReader} p diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index ddfcf367..a8a5d135 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1761,11 +1761,7 @@ TX.fromRaw = function fromRaw(data, enc) { */ TX.prototype.toExtended = function toExtended(saveCoins, enc) { - var height = this.height; - var index = this.index; - var changeIndex = this.changeIndex != null ? this.changeIndex : -1; - var p = new BufferWriter(); - var i, input, tmp; + var data; if (typeof saveCoins === 'string') { tmp = saveCoins; @@ -1773,57 +1769,24 @@ TX.prototype.toExtended = function toExtended(saveCoins, enc) { enc = tmp; } - if (height === -1) - height = 0x7fffffff; - - if (index === -1) - index = 0x7fffffff; - - if (changeIndex === -1) - changeIndex = 0x7fffffff; - - bcoin.protocol.framer.renderTX(this, true, p); - p.writeU32(height); - p.writeHash(this.block || constants.ZERO_HASH); - p.writeU32(index); - p.writeU32(this.ts); - p.writeU32(this.ps); - // p.writeU32(changeIndex); - - if (saveCoins) { - p.writeVarint(this.inputs.length); - for (i = 0; i < this.inputs.length; i++) { - input = this.inputs[i]; - - if (!input.coin) { - p.writeVarint(0); - continue; - } - - p.writeVarBytes(bcoin.protocol.framer.coin(input.coin, false)); - } - } - - p = p.render(); + data = bcoin.protocol.framer.extendedTX(this, saveCoins); if (enc === 'hex') - p = p.toString('hex'); + data = data.toString('hex'); - return p; + return data; }; /** * Parse a transaction in "extended" serialization format. - * @param {Buffer} buf + * @param {Buffer} data * @param {Boolean?} saveCoins - If true, the function will * attempt to parse the coins. * @param {String?} enc - One of `"hex"` or `null`. * @returns {NakedTX} - A "naked" transaction object. */ -TX.parseExtended = function parseExtended(buf, saveCoins, enc) { - var p, tx, coinCount, coin, i, tmp; - +TX.parseExtended = function parseExtended(data, saveCoins, enc) { if (typeof saveCoins === 'string') { tmp = saveCoins; saveCoins = enc; @@ -1831,59 +1794,23 @@ TX.parseExtended = function parseExtended(buf, saveCoins, enc) { } if (enc === 'hex') - buf = new Buffer(buf, 'hex'); + data = new Buffer(data, 'hex'); - p = new BufferReader(buf); - - tx = bcoin.protocol.parser.parseTX(p); - - tx.height = p.readU32(); - tx.block = p.readHash('hex'); - tx.index = p.readU32(); - tx.ts = p.readU32(); - tx.ps = p.readU32(); - // tx.changeIndex = p.readU32(); - - if (tx.block === constants.NULL_HASH) - tx.block = null; - - if (tx.height === 0x7fffffff) - tx.height = -1; - - if (tx.index === 0x7fffffff) - tx.index = -1; - - if (tx.changeIndex === 0x7fffffff) - tx.changeIndex = -1; - - if (saveCoins) { - coinCount = p.readVarint(); - for (i = 0; i < coinCount; i++) { - coin = p.readVarBytes(); - if (coin.length === 0) - continue; - coin = bcoin.protocol.parser.parseCoin(coin, false); - coin.hash = tx.inputs[i].prevout.hash; - coin.index = tx.inputs[i].prevout.index; - tx.inputs[i].coin = coin; - } - } - - return tx; + return bcoin.protocol.parser.parseExtendedTX(data, saveCoins); }; /** * Instantiate a transaction from a Buffer * in "extended" serialization format. - * @param {Buffer} buf + * @param {Buffer} data * @param {Boolean?} saveCoins - If true, the function will * attempt to parse the coins. * @param {String?} enc - One of `"hex"` or `null`. * @returns {TX} */ -TX.fromExtended = function fromExtended(buf, saveCoins, enc) { - return new TX(TX.parseExtended(buf, saveCoins, enc)); +TX.fromExtended = function fromExtended(data, saveCoins, enc) { + return new TX(TX.parseExtended(data, saveCoins, enc)); }; /** diff --git a/lib/bcoin/workers.js b/lib/bcoin/workers.js index 44652aa4..33256c46 100644 --- a/lib/bcoin/workers.js +++ b/lib/bcoin/workers.js @@ -672,13 +672,13 @@ Framer.item = function _item(item, p) { } else { if (item instanceof bcoin.block) { p.writeU8(40); - p.writeVarBytes(item.render()); + bcoin.protocol.framer.witnessBlock(item, p); } else if (item instanceof bcoin.tx) { p.writeU8(41); - p.writeVarBytes(item.toExtended(true)); + bcoin.protocol.framer.extendedTX(item, true, p); } else if (item instanceof bcoin.coin) { p.writeU8(42); - p.writeVarBytes(item.toExtended()); + bcoin.protocol.framer.coin(item, true, p); } else if (bn.isBN(item)) { p.writeU8(43); p.writeVarBytes(item.toBuffer()); @@ -830,11 +830,11 @@ Parser.parseItem = function parseItem(p) { items[p.readVarString('utf8')] = Parser.parseItem(p); return items; case 40: - return bcoin.block.fromRaw(p.readVarBytes()); + return bcoin.block.fromRaw(p); case 41: - return bcoin.tx.fromExtended(p.readVarBytes(), true); + return bcoin.tx.fromExtended(p, true); case 42: - return bcoin.coin.fromExtended(p.readVarBytes()); + return bcoin.coin.fromExtended(p); case 43: return new bn(p.readVarBytes()); default: