From a95aba92fb23cd32e89a2bbb6c9efc65a5599be1 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 3 Dec 2016 18:02:10 -0800 Subject: [PATCH] serialization: less polymorphism. --- lib/bip70/payment.js | 9 +- lib/bip70/paymentack.js | 9 +- lib/bip70/paymentdetails.js | 9 +- lib/bip70/paymentrequest.js | 9 +- lib/blockchain/chainentry.js | 9 +- lib/blockchain/undocoins.js | 51 ++-- lib/hd/mnemonic.js | 47 +++- lib/hd/private.js | 105 ++++++--- lib/hd/public.js | 44 +++- lib/http/rpc.js | 2 +- lib/mempool/fees.js | 18 +- lib/net/bip152.js | 115 ++++++--- lib/net/packets.js | 343 ++++++++++++--------------- lib/primitives/abstractblock.js | 12 +- lib/primitives/address.js | 6 +- lib/primitives/block.js | 247 ++++++++++++-------- lib/primitives/coin.js | 51 ++-- lib/primitives/headers.js | 133 +++++++---- lib/primitives/input.js | 90 +++---- lib/primitives/invitem.js | 46 +++- lib/primitives/keyring.js | 50 +++- lib/primitives/memblock.js | 13 +- lib/primitives/merkleblock.js | 56 +++-- lib/primitives/mtx.js | 8 + lib/primitives/netaddress.js | 52 +++-- lib/primitives/outpoint.js | 46 +++- lib/primitives/output.js | 47 ++-- lib/primitives/tx.js | 401 +++++++++++++++++--------------- lib/script/opcode.js | 168 ++++++++++--- lib/script/script.js | 195 +++++----------- lib/script/witness.js | 49 ++-- lib/utils/asn1.js | 333 ++++++++++++++------------ lib/utils/bloom.js | 11 +- lib/utils/protobuf.js | 160 ++++++++++--- lib/utils/reader.js | 9 +- lib/utils/writer.js | 7 +- lib/wallet/account.js | 9 +- lib/wallet/masterkey.js | 14 +- lib/wallet/path.js | 9 +- lib/wallet/records.js | 102 ++++---- lib/wallet/txdb.js | 35 +-- lib/wallet/wallet.js | 9 +- lib/workers/framer.js | 2 +- lib/workers/packets.js | 131 ++++++----- lib/workers/workerpool.js | 12 +- migrate/chaindb1to2.js | 2 +- migrate/walletdb5to6.js | 9 +- test/protocol-test.js | 8 +- test/script-test.js | 14 +- 49 files changed, 1894 insertions(+), 1422 deletions(-) diff --git a/lib/bip70/payment.js b/lib/bip70/payment.js index bc34b141..eebfaaf9 100644 --- a/lib/bip70/payment.js +++ b/lib/bip70/payment.js @@ -95,8 +95,8 @@ Payment.fromRaw = function fromRaw(data, enc) { return new Payment().fromRaw(data); }; -Payment.prototype.toRaw = function toRaw(writer) { - var bw = new ProtoWriter(writer); +Payment.prototype.toRaw = function toRaw() { + var bw = new ProtoWriter(); var i, tx, op, output; if (this.merchantData) @@ -118,10 +118,7 @@ Payment.prototype.toRaw = function toRaw(writer) { if (this.memo != null) bw.writeFieldString(4, this.memo); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; module.exports = Payment; diff --git a/lib/bip70/paymentack.js b/lib/bip70/paymentack.js index 320b995b..47350b95 100644 --- a/lib/bip70/paymentack.js +++ b/lib/bip70/paymentack.js @@ -54,18 +54,15 @@ PaymentACK.fromRaw = function fromRaw(data, enc) { return new PaymentACK().fromRaw(data); }; -PaymentACK.prototype.toRaw = function toRaw(writer) { - var bw = new ProtoWriter(writer); +PaymentACK.prototype.toRaw = function toRaw() { + var bw = new ProtoWriter(); bw.writeFieldBytes(1, this.payment.toRaw()); if (this.memo != null) bw.writeFieldString(2, this.memo); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; module.exports = PaymentACK; diff --git a/lib/bip70/paymentdetails.js b/lib/bip70/paymentdetails.js index 51baaf38..3745f336 100644 --- a/lib/bip70/paymentdetails.js +++ b/lib/bip70/paymentdetails.js @@ -147,8 +147,8 @@ PaymentDetails.fromRaw = function fromRaw(data, enc) { return new PaymentDetails().fromRaw(data); }; -PaymentDetails.prototype.toRaw = function toRaw(writer) { - var bw = new ProtoWriter(writer); +PaymentDetails.prototype.toRaw = function toRaw() { + var bw = new ProtoWriter(); var i, op, output; if (this.network != null) @@ -176,10 +176,7 @@ PaymentDetails.prototype.toRaw = function toRaw(writer) { if (this.merchantData) bw.writeFieldString(7, this.merchantData); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; module.exports = PaymentDetails; diff --git a/lib/bip70/paymentrequest.js b/lib/bip70/paymentrequest.js index 483f9ac2..fdbca703 100644 --- a/lib/bip70/paymentrequest.js +++ b/lib/bip70/paymentrequest.js @@ -83,8 +83,8 @@ PaymentRequest.fromRaw = function fromRaw(data, enc) { return new PaymentRequest().fromRaw(data); }; -PaymentRequest.prototype.toRaw = function toRaw(writer) { - var bw = new ProtoWriter(writer); +PaymentRequest.prototype.toRaw = function toRaw() { + var bw = new ProtoWriter(); if (this.version !== -1) bw.writeFieldU32(1, this.version); @@ -100,10 +100,7 @@ PaymentRequest.prototype.toRaw = function toRaw(writer) { if (this.signature) bw.writeFieldBytes(5, this.signature); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; PaymentRequest.prototype.getAlgorithm = function getAlgorithm() { diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index 585f64d5..d6ec8132 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -432,8 +432,8 @@ ChainEntry.fromBlock = function fromBlock(chain, block, prev) { * @returns {Buffer} */ -ChainEntry.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +ChainEntry.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU32(this.version); bw.writeHash(this.prevBlock); @@ -444,10 +444,7 @@ ChainEntry.prototype.toRaw = function toRaw(writer) { bw.writeU32(this.height); bw.writeBytes(this.chainwork.toArrayLike(Buffer, 'le', 32)); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/blockchain/undocoins.js b/lib/blockchain/undocoins.js index 8ee754e8..183eee0b 100644 --- a/lib/blockchain/undocoins.js +++ b/lib/blockchain/undocoins.js @@ -56,7 +56,7 @@ UndoCoins.prototype.toRaw = function toRaw() { for (i = 0; i < this.items.length; i++) { coin = this.items[i]; - coin.toRaw(bw); + coin.toWriter(bw); } return bw.render(); @@ -75,7 +75,7 @@ UndoCoins.prototype.fromRaw = function fromRaw(data) { var i; for (i = 0; i < count; i++) - this.items.push(UndoCoin.fromRaw(br)); + this.items.push(UndoCoin.fromReader(br)); return this; }; @@ -187,12 +187,11 @@ UndoCoin.prototype.toOutput = function toOutput() { }; /** - * Serialize the undo coin. - * @returns {Buffer} + * Write the undo coin to a buffer writer. + * @param {BufferWriter} bw */ -UndoCoin.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +UndoCoin.prototype.toWriter = function toWriter(bw) { var height = this.height; assert(height !== 0); @@ -214,21 +213,26 @@ UndoCoin.prototype.toRaw = function toRaw(writer) { compress.output(this.output, bw); } - if (!writer) - bw = bw.render(); - return bw; }; /** - * Inject properties from serialized data. + * Serialize the undo coin. + * @returns {Buffer} + */ + +UndoCoin.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +/** + * Inject properties from buffer reader. * @private - * @param {Buffer} data + * @param {BufferReader} br * @returns {UndoCoin} */ -UndoCoin.prototype.fromRaw = function fromRaw(data) { - var br = new BufferReader(data); +UndoCoin.prototype.fromReader = function fromReader(br) { var code = br.readVarint(); this.output = new Output(); @@ -248,6 +252,27 @@ UndoCoin.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @returns {UndoCoin} + */ + +UndoCoin.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate undo coin from serialized data. + * @param {Buffer} data + * @returns {UndoCoin} + */ + +UndoCoin.fromReader = function fromReader(br) { + return new UndoCoin().fromReader(br); +}; + /** * Instantiate undo coin from serialized data. * @param {Buffer} data diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index 8b4bbd37..6f9629a3 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -414,12 +414,11 @@ Mnemonic.fromJSON = function fromJSON(json) { }; /** - * Serialize mnemonic. - * @returns {Buffer} + * Write the mnemonic to a buffer writer. + * @params {BufferWriter} bw */ -Mnemonic.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +Mnemonic.prototype.toWriter = function toWriter(bw) { var lang = Mnemonic.languages.indexOf(this.language); assert(lang !== -1); @@ -430,21 +429,25 @@ Mnemonic.prototype.toRaw = function toRaw(writer) { bw.writeVarString(this.getPhrase(), 'utf8'); bw.writeVarString(this.passphrase, 'utf8'); - if (!writer) - bw = bw.render(); - return bw; }; /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data + * Serialize mnemonic. + * @returns {Buffer} */ -Mnemonic.prototype.fromRaw = function fromRaw(data) { - var br = new BufferReader(data); +Mnemonic.prototype.toRaw = function toRaw(writer) { + return this.toWriter(new BufferWriter()).render(); +}; +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ + +Mnemonic.prototype.fromReader = function fromReader(br) { this.bits = br.readU16(); this.language = Mnemonic.languages[br.readU8()]; this.entropy = br.readBytes(this.bits / 8); @@ -459,6 +462,26 @@ Mnemonic.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Mnemonic.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate mnemonic from buffer reader. + * @param {BufferReader} br + * @returns {Mnemonic} + */ + +Mnemonic.fromReader = function fromReader(br) { + return new Mnemonic().fromReader(br); +}; + /** * Instantiate mnemonic from serialized data. * @param {Buffer} data diff --git a/lib/hd/private.js b/lib/hd/private.js index 732b6d18..f184e073 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -623,8 +623,7 @@ HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey) { * @param {Buffer} raw */ -HDPrivateKey.prototype.fromRaw = function fromRaw(raw) { - var br = new BufferReader(raw); +HDPrivateKey.prototype.fromReader = function fromReader(br) { var i, version, type, prefix; version = br.readU32BE(); @@ -651,6 +650,16 @@ HDPrivateKey.prototype.fromRaw = function fromRaw(raw) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} raw + */ + +HDPrivateKey.prototype.fromRaw = function fromRaw(raw) { + return this.fromReader(new BufferReader(raw)); +}; + /** * Serialize key to a base58 string. * @param {(Network|NetworkType)?} network @@ -662,14 +671,12 @@ HDPrivateKey.prototype.toBase58 = function toBase58(network) { }; /** - * Serialize the key. + * Write the key to a buffer writer. + * @param {BufferWriter} bw * @param {(Network|NetworkType)?} network - * @returns {Buffer} */ -HDPrivateKey.prototype.toRaw = function toRaw(network, writer) { - var bw = new BufferWriter(writer); - +HDPrivateKey.prototype.toWriter = function toWriter(bw, network) { if (!network) network = this.network; @@ -684,8 +691,35 @@ HDPrivateKey.prototype.toRaw = function toRaw(network, writer) { bw.writeBytes(this.privateKey); bw.writeChecksum(); - if (!writer) - bw = bw.render(); + return bw; +}; + +/** + * Serialize the key. + * @param {(Network|NetworkType)?} network + * @returns {Buffer} + */ + +HDPrivateKey.prototype.toRaw = function toRaw(network) { + return this.toWriter(new BufferWriter(), network).render(); +}; + +/** + * Write the key in "extended" + * format to a buffer writer. + * @param {BufferWriter} bw + * @param {(Network|NetworkType)?} network + */ + +HDPrivateKey.prototype.toExtendedWriter = function toExtendedWriter(bw, network) { + this.toWriter(bw, network); + + if (this.mnemonic) { + bw.writeU8(1); + this.mnemonic.toWriter(bw); + } else { + bw.writeU8(0); + } return bw; }; @@ -697,22 +731,21 @@ HDPrivateKey.prototype.toRaw = function toRaw(network, writer) { * @returns {Buffer} */ -HDPrivateKey.prototype.toExtended = function toExtended(network, writer) { - var bw = new BufferWriter(writer); +HDPrivateKey.prototype.toExtended = function toExtended(network) { + return this.toExtendedWriter(new BufferWriter(), network).render(); +}; - this.toRaw(network, bw); +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ - if (this.mnemonic) { - bw.writeU8(1); - this.mnemonic.toRaw(bw); - } else { - bw.writeU8(0); - } - - if (!writer) - bw = bw.render(); - - return bw; +HDPrivateKey.prototype.fromExtendedReader = function fromExtendedReader(br) { + this.fromReader(br); + if (br.readU8() === 1) + this.mnemonic = Mnemonic.fromReader(br); + return this; }; /** @@ -722,11 +755,17 @@ HDPrivateKey.prototype.toExtended = function toExtended(network, writer) { */ HDPrivateKey.prototype.fromExtended = function fromExtended(data) { - var br = new BufferReader(data); - this.fromRaw(br); - if (br.readU8() === 1) - this.mnemonic = Mnemonic.fromRaw(br); - return this; + return this.fromExtendedReader(new BufferReader(data)); +}; + +/** + * Instantiate key from "extended" buffer reader. + * @param {BufferReader} br + * @returns {HDPrivateKey} + */ + +HDPrivateKey.fromExtendedReader = function fromExtendedReader(br) { + return new HDPrivateKey().fromExtendedReader(br); }; /** @@ -749,6 +788,16 @@ HDPrivateKey.fromBase58 = function fromBase58(xkey) { return new HDPrivateKey().fromBase58(xkey); }; +/** + * Instantiate key from buffer reader. + * @param {BufferReader} br + * @returns {HDPrivateKey} + */ + +HDPrivateKey.fromReader = function fromReader(br) { + return new HDPrivateKey().fromReader(br); +}; + /** * Instantiate key from serialized data. * @param {Buffer} raw diff --git a/lib/hd/public.js b/lib/hd/public.js index b9d223ee..2c9fe5ca 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -485,8 +485,7 @@ HDPublicKey.prototype.fromBase58 = function fromBase58(xkey) { * @param {Buffer} raw */ -HDPublicKey.prototype.fromRaw = function fromRaw(raw) { - var br = new BufferReader(raw); +HDPublicKey.prototype.fromReader = function fromReader(br) { var i, version, type, prefix; version = br.readU32BE(); @@ -511,6 +510,16 @@ HDPublicKey.prototype.fromRaw = function fromRaw(raw) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} raw + */ + +HDPublicKey.prototype.fromRaw = function fromRaw(raw) { + return this.fromReader(new BufferReader(raw)); +}; + /** * Serialize key data to base58 extended key. * @param {Network|String} network @@ -522,14 +531,12 @@ HDPublicKey.prototype.toBase58 = function toBase58(network) { }; /** - * Serialize the key. + * Write the key to a buffer writer. + * @param {BufferWriter} bw * @param {Network|NetworkType} network - * @returns {Buffer} */ -HDPublicKey.prototype.toRaw = function toRaw(network, writer) { - var bw = new BufferWriter(writer); - +HDPublicKey.prototype.toWriter = function toWriter(bw, network) { if (!network) network = this.network; @@ -543,12 +550,19 @@ HDPublicKey.prototype.toRaw = function toRaw(network, writer) { bw.writeBytes(this.publicKey); bw.writeChecksum(); - if (!writer) - bw = bw.render(); - return bw; }; +/** + * Serialize the key. + * @param {Network|NetworkType} network + * @returns {Buffer} + */ + +HDPublicKey.prototype.toRaw = function toRaw(network) { + return this.toWriter(new BufferWriter(), network).render(); +}; + /** * Instantiate an HD public key from a base58 string. * @param {Base58String} xkey @@ -559,6 +573,16 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) { return new HDPublicKey().fromBase58(xkey); }; +/** + * Instantiate key from serialized data. + * @param {BufferReader} br + * @returns {HDPublicKey} + */ + +HDPublicKey.fromReader = function fromReader(br) { + return new HDPublicKey().fromReader(br); +}; + /** * Instantiate key from serialized data. * @param {Buffer} raw diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 83e1de5e..5883a3fb 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -2163,7 +2163,7 @@ RPC.prototype.signrawtransaction = co(function* signrawtransaction(args) { txs = []; while (br.left()) - txs.push(MTX.fromRaw(br)); + txs.push(MTX.fromReader(br)); merged = txs[0]; diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index 5c5dcfe6..26df3704 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -320,8 +320,8 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc * @returns {Buffer} */ -ConfirmStats.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +ConfirmStats.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; function writeArray(buckets) { @@ -342,10 +342,7 @@ ConfirmStats.prototype.toRaw = function toRaw(writer) { for (i = 0; i < this.maxConfirms; i++) writeArray(this.confAvg[i]); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -805,18 +802,15 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s * @returns {Buffer} */ -PolicyEstimator.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +PolicyEstimator.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU32(this.network.magic); bw.writeU32(this.bestHeight); bw.writeVarBytes(this.feeStats.toRaw()); bw.writeVarBytes(this.priStats.toRaw()); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/net/bip152.js b/lib/net/bip152.js index d486c12c..df53d9f8 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -116,8 +116,8 @@ CompactBlock.prototype.fromRaw = function fromRaw(data) { index = br.readVarint(); assert(index <= 0xffff); assert(index < this.totalTX); - tx = TX.fromRaw(br); - this.ptx.push([index, tx]); + tx = TX.fromReader(br); + this.ptx.push(new PrefilledTX(index, tx)); } this.init(); @@ -131,16 +131,27 @@ CompactBlock.fromRaw = function fromRaw(data, enc) { return new CompactBlock().fromRaw(data); }; -CompactBlock.prototype.toRaw = function toRaw(writer) { - return this.frame(true, writer); +CompactBlock.prototype.toRaw = function toRaw() { + return this.frame(true); }; -CompactBlock.prototype.toNormal = function toNormal(writer) { - return this.frame(false, writer); +CompactBlock.prototype.toNormal = function toNormal() { + return this.frame(false); }; -CompactBlock.prototype.frame = function frame(witness, writer) { - var bw = BufferWriter(writer); +CompactBlock.prototype.toWriter = function toWriter(bw) { + return this.frameWriter(bw, true); +}; + +CompactBlock.prototype.toNormalWriter = function toNormalWriter(bw) { + return this.frameWriter(bw, false); +}; + +CompactBlock.prototype.frame = function frame(witness) { + return this.frameWriter(new BufferWriter(), witness).render(); +}; + +CompactBlock.prototype.frameWriter = function frameWriter(bw, witness) { var i, id, lo, hi, ptx; bw.writeU32(this.version); @@ -167,16 +178,13 @@ CompactBlock.prototype.frame = function frame(witness, writer) { for (i = 0; i < this.ptx.length; i++) { ptx = this.ptx[i]; - bw.writeVarint(ptx[0]); + bw.writeVarint(ptx.index); if (witness) - ptx[1].toRaw(bw); + ptx.tx.toWriter(bw); else - ptx[1].toNormal(bw); + ptx.tx.toNormalWriter(bw); } - if (!writer) - bw = bw.render(); - return bw; }; @@ -286,10 +294,10 @@ CompactBlock.prototype.init = function init() { for (i = 0; i < this.ptx.length; i++) { ptx = this.ptx[i]; assert(ptx); - last += ptx[0] + 1; + last += ptx.index + 1; assert(last <= 0xffff); assert(last <= this.ids.length + i); - this.available[last] = ptx[1]; + this.available[last] = ptx.tx; this.count++; } @@ -364,7 +372,7 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) { this.ids.push(id); } - this.ptx.push([0, block.txs[0]]); + this.ptx.push(new PrefilledTX(0, block.txs[0])); return this; }; @@ -455,8 +463,7 @@ TXRequest.fromCompact = function fromCompact(block) { return new TXRequest().fromCompact(block); }; -TXRequest.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +TXRequest.prototype.fromReader = function fromReader(br) { var i, count, index, offset; this.hash = br.readHash('hex'); @@ -482,12 +489,19 @@ TXRequest.prototype.fromRaw = function fromRaw(data) { return this; }; +TXRequest.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +TXRequest.fromReader = function fromReader(br) { + return new TXRequest().fromReader(br); +}; + TXRequest.fromRaw = function fromRaw(data) { return new TXRequest().fromRaw(data); }; -TXRequest.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +TXRequest.prototype.toWriter = function toWriter(bw) { var i, index; bw.writeHash(this.hash); @@ -499,12 +513,13 @@ TXRequest.prototype.toRaw = function toRaw(writer) { bw.writeVarint(index); } - if (!writer) - bw = bw.render(); - return bw; }; +TXRequest.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + /** * Represents BlockTransactions (bip152): `blocktxn` packet. * @see https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki @@ -535,8 +550,7 @@ TXResponse.fromOptions = function fromOptions(options) { return new TXResponse().fromOptions(options); }; -TXResponse.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +TXResponse.prototype.fromReader = function fromReader(br) { var i, count; this.hash = br.readHash('hex'); @@ -544,11 +558,19 @@ TXResponse.prototype.fromRaw = function fromRaw(data) { count = br.readVarint(); for (i = 0; i < count; i++) - this.txs.push(TX.fromRaw(br)); + this.txs.push(TX.fromReader(br)); return this; }; +TXResponse.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +TXResponse.fromReader = function fromReader(br) { + return new TXResponse().fromReader(br); +}; + TXResponse.fromRaw = function fromRaw(data) { return new TXResponse().fromRaw(data); }; @@ -572,16 +594,23 @@ TXResponse.fromBlock = function fromBlock(block, req) { return new TXResponse().fromBlock(block, req); }; -TXResponse.prototype.toRaw = function toRaw(writer) { - return this.frame(true, writer); +TXResponse.prototype.toRaw = function toRaw() { + return this.frame(true); }; -TXResponse.prototype.toNormal = function toNormal(writer) { - return this.frame(false, writer); +TXResponse.prototype.toNormal = function toNormal() { + return this.frame(false); }; -TXResponse.prototype.frame = function frame(witness, writer) { - var bw = BufferWriter(writer); +TXResponse.prototype.toWriter = function toWriter(bw) { + return this.frameWriter(bw, true); +}; + +TXResponse.prototype.toNormalWriter = function toNormalWriter(bw) { + return this.frameWriter(bw, false); +}; + +TXResponse.prototype.frameWriter = function frameWriter(bw, witness) { var i, tx; bw.writeHash(this.hash); @@ -591,17 +620,27 @@ TXResponse.prototype.frame = function frame(witness, writer) { for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; if (witness) - tx.toRaw(bw); + tx.toWriter(bw); else - tx.toNormal(bw); + tx.toNormalWriter(bw); } - if (!writer) - bw = bw.render(); - return bw; }; +TXResponse.prototype.frame = function frame(witness) { + return this.frameWriter(new BufferWriter(), witness).render(); +}; + +/* + * Helpers + */ + +function PrefilledTX(index, tx) { + this.index = index; + this.tx = tx; +} + /* * Expose */ diff --git a/lib/net/packets.js b/lib/net/packets.js index c4f9fe6a..ded300cb 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -186,23 +186,20 @@ VersionPacket.fromOptions = function fromOptions(options) { * @returns {Buffer} */ -VersionPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +VersionPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.write32(this.version); bw.writeU64(this.services); bw.write64(this.ts); - this.recv.toRaw(false, bw); - this.from.toRaw(false, bw); + this.recv.toWriter(bw, false); + this.from.toWriter(bw, false); bw.writeBytes(this.nonce); bw.writeVarString(this.agent, 'ascii'); bw.write32(this.height); bw.writeU8(this.relay ? 1 : 0); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -272,10 +269,10 @@ VersionPacket.prototype.fromRaw = function fromRaw(data) { this.version = br.read32(); this.services = br.readU53(); this.ts = br.read53(); - this.recv.fromRaw(br, false); + this.recv.fromReader(br, false); if (br.left() > 0) { - this.from.fromRaw(br, false); + this.from.fromReader(br, false); this.nonce = br.readBytes(8); } @@ -332,8 +329,8 @@ VerackPacket.prototype.type = exports.types.VERACK; * @returns {Buffer} */ -VerackPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +VerackPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -386,16 +383,13 @@ PingPacket.prototype.type = exports.types.PING; * @returns {Buffer} */ -PingPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +PingPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); if (this.nonce) bw.writeBytes(this.nonce); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -451,15 +445,10 @@ PongPacket.prototype.type = exports.types.PONG; * @returns {Buffer} */ -PongPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +PongPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.nonce); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -650,20 +639,24 @@ AlertPacket.prototype.verify = function verify(key) { }; /** - * Serialize the alert packet (includes payload _and_ signature). + * Write the alert packet to a buffer writer. + * @param {BufferWriter} bw + */ + +AlertPacket.prototype.toWriter = function toWriter(bw) { + bw.writeVarBytes(this.toPayload()); + bw.writeVarBytes(this.signature); + return bw; +}; + +/** + * Serialize the alert packet (includes + * payload _and_ signature). * @returns {Buffer} */ -AlertPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - - bw.writeVarBytes(this.toPayload()); - bw.writeVarBytes(this.signature); - - if (!writer) - bw = bw.render(); - - return bw; +AlertPacket.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); }; /** @@ -672,8 +665,8 @@ AlertPacket.prototype.toRaw = function toRaw(writer) { * @returns {Buffer} */ -AlertPacket.prototype.framePayload = function framePayload(writer) { - var bw = BufferWriter(writer); +AlertPacket.prototype.framePayload = function framePayload() { + var bw = new BufferWriter(); var i; bw.write32(this.version); @@ -698,26 +691,22 @@ AlertPacket.prototype.framePayload = function framePayload(writer) { bw.writeVarString(this.statusBar, 'ascii'); bw.writeVarString(this.reserved, 'ascii'); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** - * Inject properties from serialized data. + * Inject properties from buffer reader. * @private - * @param {Buffer} data + * @param {BufferReader} br */ -AlertPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +AlertPacket.prototype.fromReader = function fromReader(br) { var i, count; this._payload = br.readVarBytes(); this.signature = br.readVarBytes(); - br = BufferReader(this._payload); + br = new BufferReader(this._payload); this.version = br.read32(); this.relayUntil = br.read53(); @@ -744,6 +733,26 @@ AlertPacket.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AlertPacket.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate alert packet from buffer reader. + * @param {BufferReader} br + * @returns {AlertPacket} + */ + +AlertPacket.fromReader = function fromReader(br) { + return new AlertPacket().fromReader(br); +}; + /** * Instantiate alert packet from serialized data. * @param {Buffer} data @@ -780,8 +789,8 @@ GetAddrPacket.prototype.type = exports.types.GETADDR; * @returns {Buffer} */ -GetAddrPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +GetAddrPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -834,19 +843,16 @@ AddrPacket.prototype.type = exports.types.ADDR; * @returns {Buffer} */ -AddrPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +AddrPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeVarint(this.items.length); for (i = 0; i < this.items.length; i++) - this.items[i].toRaw(true, bw); + this.items[i].toWriter(bw, true); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -862,7 +868,7 @@ AddrPacket.prototype.fromRaw = function fromRaw(data) { count = br.readVarint(); for (i = 0; i < count; i++) - this.items.push(NetworkAddress.fromRaw(br, true)); + this.items.push(NetworkAddress.fromReader(br, true)); return this; }; @@ -907,19 +913,16 @@ InvPacket.prototype.type = exports.types.INV; * @returns {Buffer} */ -InvPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +InvPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeVarint(this.items.length); for (i = 0; i < this.items.length; i++) - this.items[i].toRaw(bw); + this.items[i].toWriter(bw); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -929,13 +932,13 @@ InvPacket.prototype.toRaw = function toRaw(writer) { */ InvPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, count; count = br.readVarint(); for (i = 0; i < count; i++) - this.items.push(InvItem.fromRaw(br)); + this.items.push(InvItem.fromReader(br)); return this; }; @@ -1050,8 +1053,8 @@ GetBlocksPacket.prototype.type = exports.types.GETBLOCKS; * @returns {Buffer} */ -GetBlocksPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +GetBlocksPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeU32(this.version); @@ -1062,10 +1065,7 @@ GetBlocksPacket.prototype.toRaw = function toRaw(writer) { bw.writeHash(this.stop || constants.ZERO_HASH); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -1167,19 +1167,16 @@ HeadersPacket.prototype.type = exports.types.HEADERS; * @returns {Buffer} */ -HeadersPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +HeadersPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeVarint(this.items.length); for (i = 0; i < this.items.length; i++) - this.items[i].toRaw(bw); + this.items[i].toWriter(bw); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -1195,7 +1192,7 @@ HeadersPacket.prototype.fromRaw = function fromRaw(data) { count = br.readVarint(); for (i = 0; i < count; i++) - this.items.push(Headers.fromRaw(br)); + this.items.push(Headers.fromReader(br)); return this; }; @@ -1236,8 +1233,8 @@ SendHeadersPacket.prototype.type = exports.types.SENDHEADERS; * @returns {Buffer} */ -SendHeadersPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +SendHeadersPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -1293,10 +1290,10 @@ BlockPacket.prototype.type = exports.types.BLOCK; * @returns {Buffer} */ -BlockPacket.prototype.toRaw = function toRaw(writer) { +BlockPacket.prototype.toRaw = function toRaw() { if (this.witness) - return this.block.toRaw(writer); - return this.block.toNormal(writer); + return this.block.toRaw(); + return this.block.toNormal(); }; /** @@ -1353,10 +1350,10 @@ TXPacket.prototype.type = exports.types.TX; * @returns {Buffer} */ -TXPacket.prototype.toRaw = function toRaw(writer) { +TXPacket.prototype.toRaw = function toRaw() { if (this.witness) - return this.tx.toRaw(writer); - return this.tx.toNormal(writer); + return this.tx.toRaw(); + return this.tx.toNormal(); }; /** @@ -1460,8 +1457,8 @@ RejectPacket.fromOptions = function fromOptions(options) { * @returns {Buffer} */ -RejectPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +RejectPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); assert(this.message.length <= 12); assert(this.reason.length <= 111); @@ -1473,10 +1470,7 @@ RejectPacket.prototype.toRaw = function toRaw(writer) { if (this.data) bw.writeHash(this.data); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -1603,8 +1597,8 @@ MempoolPacket.prototype.type = exports.types.MEMPOOL; * @returns {Buffer} */ -MempoolPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +MempoolPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -1656,8 +1650,8 @@ FilterLoadPacket.prototype.type = exports.types.FILTERLOAD; * @returns {Buffer} */ -FilterLoadPacket.prototype.toRaw = function toRaw(writer) { - return this.filter.toRaw(writer); +FilterLoadPacket.prototype.toRaw = function toRaw() { + return this.filter.toRaw(); }; /** @@ -1726,15 +1720,10 @@ FilterAddPacket.prototype.type = exports.types.FILTERADD; * @returns {Buffer} */ -FilterAddPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +FilterAddPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeVarBytes(this.data); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -1785,8 +1774,8 @@ FilterClearPacket.prototype.type = exports.types.FILTERCLEAR; * @returns {Buffer} */ -FilterClearPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +FilterClearPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -1839,8 +1828,8 @@ MerkleBlockPacket.prototype.type = exports.types.MERKLEBLOCK; * @returns {Buffer} */ -MerkleBlockPacket.prototype.toRaw = function toRaw(writer) { - return this.block.toRaw(writer); +MerkleBlockPacket.prototype.toRaw = function toRaw() { + return this.block.toRaw(); }; /** @@ -1897,20 +1886,17 @@ GetUTXOsPacket.prototype.type = exports.types.GETUTXOS; * @returns {Buffer} */ -GetUTXOsPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +GetUTXOsPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeU8(this.mempool ? 1 : 0); bw.writeVarint(this.prevout.length); for (i = 0; i < this.prevout.length; i++) - this.prevout[i].toRaw(bw); + this.prevout[i].toWriter(bw); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -1928,7 +1914,7 @@ GetUTXOsPacket.prototype.fromRaw = function fromRaw(data) { count = br.readVarint(); for (i = 0; i < count; i++) - this.prevout.push(Outpoint.fromRaw(br)); + this.prevout.push(Outpoint.fromReader(br)); return this; }; @@ -2023,8 +2009,8 @@ UTXOsPacket.fromOptions = function fromOptions(options) { * @returns {Buffer} */ -UTXOsPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +UTXOsPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var map = new Buffer((this.hits.length + 7) / 8 | 0); var i, bit, oct, coin, height; @@ -2052,10 +2038,7 @@ UTXOsPacket.prototype.toRaw = function toRaw(writer) { bw.writeVarBytes(coin.script.toRaw()); } - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2089,7 +2072,7 @@ UTXOsPacket.prototype.fromRaw = function fromRaw(data) { if (height === 0x7fffffff) height = -1; - output = Output.fromRaw(br); + output = Output.fromReader(br); coin.version = version; coin.height = height; @@ -2138,8 +2121,8 @@ HaveWitnessPacket.prototype.type = exports.types.HAVEWITNESS; * @returns {Buffer} */ -HaveWitnessPacket.prototype.toRaw = function toRaw(writer) { - return writer || DUMMY; +HaveWitnessPacket.prototype.toRaw = function toRaw() { + return DUMMY; }; /** @@ -2192,15 +2175,10 @@ FeeFilterPacket.prototype.type = exports.types.FEEFILTER; * @returns {Buffer} */ -FeeFilterPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +FeeFilterPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.write64(this.rate); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2258,16 +2236,11 @@ SendCmpctPacket.prototype.type = exports.types.SENDCMPCT; * @returns {Buffer} */ -SendCmpctPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +SendCmpctPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU8(this.mode); bw.writeU64(this.version); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2326,10 +2299,10 @@ CmpctBlockPacket.prototype.type = exports.types.CMPCTBLOCK; * @returns {Buffer} */ -CmpctBlockPacket.prototype.toRaw = function toRaw(writer) { +CmpctBlockPacket.prototype.toRaw = function toRaw() { if (this.witness) - return this.block.toRaw(writer); - return this.block.toNormal(writer); + return this.block.toRaw(); + return this.block.toNormal(); }; /** @@ -2383,8 +2356,8 @@ GetBlockTxnPacket.prototype.type = exports.types.GETBLOCKTXN; * @returns {Buffer} */ -GetBlockTxnPacket.prototype.toRaw = function toRaw(writer) { - return this.request.toRaw(writer); +GetBlockTxnPacket.prototype.toRaw = function toRaw() { + return this.request.toRaw(); }; /** @@ -2441,10 +2414,10 @@ BlockTxnPacket.prototype.type = exports.types.BLOCKTXN; * @returns {Buffer} */ -BlockTxnPacket.prototype.toRaw = function toRaw(writer) { +BlockTxnPacket.prototype.toRaw = function toRaw() { if (this.witness) - return this.response.toRaw(writer); - return this.response.toNormal(writer); + return this.response.toRaw(); + return this.response.toNormal(); }; /** @@ -2501,16 +2474,11 @@ EncinitPacket.prototype.type = exports.types.ENCINIT; * @returns {Buffer} */ -EncinitPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +EncinitPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.publicKey); bw.writeU8(this.cipher); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2566,15 +2534,10 @@ EncackPacket.prototype.type = exports.types.ENCACK; * @returns {Buffer} */ -EncackPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +EncackPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.publicKey); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2629,15 +2592,10 @@ AuthChallengePacket.prototype.type = exports.types.AUTHCHALLENGE; * @returns {Buffer} */ -AuthChallengePacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +AuthChallengePacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.hash); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2692,15 +2650,10 @@ AuthReplyPacket.prototype.type = exports.types.AUTHREPLY; * @returns {Buffer} */ -AuthReplyPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +AuthReplyPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.signature); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2755,15 +2708,10 @@ AuthProposePacket.prototype.type = exports.types.AUTHPROPOSE; * @returns {Buffer} */ -AuthProposePacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +AuthProposePacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.hash); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2820,15 +2768,10 @@ UnknownPacket.prototype.type = exports.types.UNKNOWN; * @returns {Buffer} */ -UnknownPacket.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +UnknownPacket.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeBytes(this.data); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index 6a4d32f6..1733742d 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -52,6 +52,7 @@ function AbstractBlock(options) { this.txs = null; this.mutable = false; + this.memory = false; this._valid = null; this._validHeaders = null; @@ -165,20 +166,15 @@ AbstractBlock.prototype.hash = function hash(enc) { * @returns {Buffer} */ -AbstractBlock.prototype.abbr = function abbr(writer) { - var bw = BufferWriter(writer); - +AbstractBlock.prototype.abbr = function abbr() { + var bw = new BufferWriter(); bw.writeU32(this.version); bw.writeHash(this.prevBlock); bw.writeHash(this.merkleRoot); bw.writeU32(this.ts); bw.writeU32(this.bits); bw.writeU32(this.nonce); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/primitives/address.js b/lib/primitives/address.js index 78b3c8f0..f052caf8 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -176,11 +176,9 @@ Address.prototype.inspect = function inspect() { */ Address.prototype.fromRaw = function fromRaw(data) { - var i, br, prefix, network, type, version, hash; + var br = new BufferReader(data, true); + var i, prefix, network, type, version, hash; - assert(Buffer.isBuffer(data)); - - br = new BufferReader(data, true); prefix = br.readU8(); for (i = 0; i < networks.types.length; i++) { diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 9abc2182..4404aba5 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -43,7 +43,6 @@ function Block(options) { this._raw = null; this._size = -1; this._witnessSize = -1; - this._lastWitnessSize = 0; if (options) this.fromOptions(options); @@ -81,13 +80,8 @@ Block.fromOptions = function fromOptions(options) { * @returns {Buffer} */ -Block.prototype.toRaw = function toRaw(writer) { - var raw = this.getRaw(); - if (writer) { - writer.writeBytes(raw); - return writer; - } - return raw; +Block.prototype.toRaw = function toRaw() { + return this.getRaw().data; }; /** @@ -95,51 +89,82 @@ Block.prototype.toRaw = function toRaw(writer) { * @returns {Buffer} */ -Block.prototype.toNormal = function toNormal(writer) { - var raw; - if (!this.hasWitness()) { - raw = this.getRaw(); - if (writer) { - writer.writeBytes(raw); - return writer; - } - return raw; - } - return this.frameNormal(writer); +Block.prototype.toNormal = function toNormal() { + if (this.hasWitness()) + return this.frameNormal().data; + return this.toRaw(); }; /** * Serialize the block. Include witnesses if present. - * @returns {Buffer} + * @param {BufferWriter} bw */ -Block.prototype.toWitness = function toWitness(writer) { - return this.toRaw(writer); +Block.prototype.toWriter = function toWriter(bw) { + this.writeRaw(bw); + return bw; +}; + +/** + * Serialize the block, do not include witnesses. + * @param {BufferWriter} bw + */ + +Block.prototype.toNormalWriter = function toNormalWriter(bw) { + if (this.hasWitness()) { + this.frameNormalWriter(bw); + return bw; + } + return this.toWriter(bw); }; /** * Get the raw block serialization. * Include witnesses if present. - * @returns {Buffer} + * @private + * @returns {RawBlock} */ Block.prototype.getRaw = function getRaw() { var raw; + if (this.mutable) { + assert(!this._raw); + return this.frameNormal(); + } + if (this._raw) { assert(this._size > 0); assert(this._witnessSize >= 0); - this._lastWitnessSize = this._witnessSize; - return this._raw; + raw = new RawBlock(this._size, this._witnessSize); + raw.data = this._raw; + return raw; } raw = this.frameWitness(); - if (!this.mutable) { - this._size = raw.length; - this._witnessSize = this._lastWitnessSize; - this._raw = raw; - } + this._raw = raw.data; + this._size = raw.total; + this._witnessSize = raw.witness; + + return raw; +}; + +/** + * Write the raw block serialization + * to a buffer writer. Include the + * witnesses if present. + * @returns {RawBlock} + */ + +Block.prototype.writeRaw = function writeRaw(bw) { + var raw; + + if (this.mutable) + return this.frameWitnessWriter(bw); + + raw = this.getRaw(); + bw.writeBytes(raw.data); return raw; }; @@ -150,31 +175,9 @@ Block.prototype.getRaw = function getRaw() { */ Block.prototype.getSizes = function getSizes() { - var sizes = new BlockSizes(); - var writer; - - if (this._size !== -1) { - sizes.total = this._size; - sizes.witness = this._witnessSize; - return sizes; - } - - if (!this.mutable) { - assert(!this._raw); - this.getRaw(); - sizes.total = this._size; - sizes.witness = this._witnessSize; - return sizes; - } - - writer = new BufferWriter(); - - this.toRaw(writer); - - sizes.total = writer.written; - sizes.witness = this._lastWitnessSize; - - return sizes; + if (this.mutable) + return this.writeRaw(new BufferWriter()); + return this.getRaw(); }; /** @@ -224,13 +227,14 @@ Block.prototype.getBaseSize = function getBaseSize() { */ Block.prototype.hasWitness = function hasWitness() { - var i; + var i, tx; if (this._witnessSize !== -1) return this._witnessSize !== 0; for (i = 0; i < this.txs.length; i++) { - if (this.txs[i].hasWitness()) + tx = this.txs[i]; + if (tx.hasWitness()) return true; } @@ -239,7 +243,7 @@ Block.prototype.hasWitness = function hasWitness() { /** * Add a transaction to the block's tx vector. - * @param {TX|NakedTX} tx + * @param {TX} tx * @returns {TX} */ @@ -443,7 +447,7 @@ Block.prototype._verify = function _verify(ret) { if (!this.verifyHeaders(ret)) return false; - // Check merkle root + // Check merkle root. merkle = this.createMerkleRoot('hex'); // If the merkle is mutated, @@ -460,6 +464,7 @@ Block.prototype._verify = function _verify(ret) { return false; } + // Check base size. if (this.txs.length === 0 || this.txs.length > constants.block.MAX_SIZE || this.getBaseSize() > constants.block.MAX_SIZE) { @@ -468,29 +473,29 @@ Block.prototype._verify = function _verify(ret) { return false; } - // First TX must be a coinbase + // First TX must be a coinbase. if (this.txs.length === 0 || !this.txs[0].isCoinbase()) { ret.reason = 'bad-cb-missing'; ret.score = 100; return false; } - // Test all txs + // Test all transactions. for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; - // The rest of the txs must not be coinbases + // The rest of the txs must not be coinbases. if (i > 0 && tx.isCoinbase()) { ret.reason = 'bad-cb-multiple'; ret.score = 100; return false; } - // Sanity checks + // Sanity checks. if (!tx.isSane(ret)) return false; - // Count legacy sigops (do not count scripthash or witness) + // Count legacy sigops (do not count scripthash or witness). sigops += tx.getLegacySigops(); if (sigops * scale > constants.block.MAX_SIGOPS_WEIGHT) { ret.reason = 'bad-blk-sigops'; @@ -685,9 +690,9 @@ Block.fromJSON = function fromJSON(json) { * @param {Buffer} data */ -Block.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); - var i, tx, witnessSize; +Block.prototype.fromReader = function fromReader(br) { + var witnessSize = 0; + var i, tx; br.start(); @@ -699,10 +704,8 @@ Block.prototype.fromRaw = function fromRaw(data) { this.nonce = br.readU32(); this.totalTX = br.readVarint(); - witnessSize = 0; - for (i = 0; i < this.totalTX; i++) { - tx = TX.fromRaw(br); + tx = TX.fromReader(br); witnessSize += tx._witnessSize; this.addTX(tx); } @@ -716,6 +719,27 @@ Block.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Block.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate a block from a serialized Buffer. + * @param {Buffer} data + * @param {String?} enc - Encoding, can be `'hex'` or null. + * @returns {Block} + */ + +Block.fromReader = function fromReader(data) { + return new Block().fromReader(data); +}; + /** * Instantiate a block from a serialized Buffer. * @param {Buffer} data @@ -749,9 +773,7 @@ Block.prototype.toMerkle = function toMerkle(filter) { * @returns {Buffer} */ -Block.prototype.frame = function frame(witness, writer) { - var bw = BufferWriter(writer); - var witnessSize = 0; +Block.prototype.frameNormalWriter = function frameNormalWriter(bw) { var i, tx; bw.writeU32(this.version); @@ -764,20 +786,54 @@ Block.prototype.frame = function frame(witness, writer) { for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; - if (witness) { - tx.toRaw(bw); - witnessSize += tx._lastWitnessSize; - } else { - tx.toNormal(bw); - } + tx.toNormalWriter(bw); } - this._lastWitnessSize = witnessSize; + return new RawBlock(bw.written, 0); +}; - if (!writer) - bw = bw.render(); +/** + * Serialze block with or without witness data. + * @private + * @param {Boolean} witness + * @param {BufferWriter?} writer + * @returns {Buffer} + */ - return bw; +Block.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { + var witnessSize = 0; + var i, tx, raw; + + bw.writeU32(this.version); + bw.writeHash(this.prevBlock); + bw.writeHash(this.merkleRoot); + bw.writeU32(this.ts); + bw.writeU32(this.bits); + bw.writeU32(this.nonce); + bw.writeVarint(this.txs.length); + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + raw = tx.writeRaw(bw); + witnessSize += raw.witness; + } + + return new RawBlock(bw.written, witnessSize); +}; + +/** + * Serialze block with or without witness data. + * @private + * @param {Boolean} witness + * @param {BufferWriter?} writer + * @returns {Buffer} + */ + +Block.prototype.frameNormal = function frameNormal() { + var bw = new BufferWriter(); + var raw = this.frameNormalWriter(bw); + raw.data = bw.render(); + return raw; }; /** @@ -787,19 +843,11 @@ Block.prototype.frame = function frame(witness, writer) { * @returns {Buffer} */ -Block.prototype.frameNormal = function frameNormal(writer) { - return this.frame(false, writer); -}; - -/** - * Serialze block with witness data. - * @private - * @param {BufferWriter?} writer - * @returns {Buffer} - */ - -Block.prototype.frameWitness = function frameWitness(writer) { - return this.frame(true, writer); +Block.prototype.frameWitness = function frameWitness() { + var bw = new BufferWriter(); + var raw = this.frameWitnessWriter(bw); + raw.data = bw.render(); + return raw; }; /** @@ -827,9 +875,10 @@ Block.isBlock = function isBlock(obj) { * Helpers */ -function BlockSizes() { - this.total = 0; - this.witness = 0; +function RawBlock(total, witness) { + this.data = null; + this.total = total; + this.witness = witness; } /* diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index 80fce7a2..85741b6e 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -194,13 +194,11 @@ Coin.prototype.fromJSON = function fromJSON(json) { }; /** - * Serialize the coin. - * @param {String?} enc - Encoding, can be `'hex'` or null. - * @returns {Buffer|String} + * Write the coin to a buffer writer. + * @param {BufferWriter} bw */ -Coin.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Coin.prototype.toWriter = function toWriter(bw) { var height = this.height; if (height === -1) @@ -212,21 +210,25 @@ Coin.prototype.toRaw = function toRaw(writer) { bw.writeVarBytes(this.script.toRaw()); bw.writeU8(this.coinbase ? 1 : 0); - if (!writer) - bw = bw.render(); - return bw; }; /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data + * Serialize the coin. + * @returns {Buffer|String} */ -Coin.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +Coin.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; +/** + * Inject properties from serialized buffer writer. + * @private + * @param {BufferReader} br + */ + +Coin.prototype.fromReader = function fromReader(br) { this.version = br.readU32(); this.height = br.readU32(); this.value = br.read64(); @@ -240,7 +242,27 @@ Coin.prototype.fromRaw = function fromRaw(data) { }; /** - * Instantiate an coin from a serialized Buffer. + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Coin.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate a coin from a buffer reader. + * @param {BufferReader} br + * @returns {Coin} + */ + +Coin.fromReader = function fromReader(br) { + return new Coin().fromReader(br); +}; + +/** + * Instantiate a coin from a serialized Buffer. * @param {Buffer} data * @param {String?} enc - Encoding, can be `'hex'` or null. * @returns {Coin} @@ -260,6 +282,7 @@ Coin.fromRaw = function fromRaw(data, enc) { Coin.prototype.fromTX = function fromTX(tx, index) { assert(util.isNumber(index)); + assert(index < tx.outputs.length); this.version = tx.version; this.height = tx.height; this.value = tx.outputs[index].value; diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index d3bd09d6..950308e4 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -47,42 +47,15 @@ Headers.prototype._verify = function _verify(ret) { */ Headers.prototype.getSize = function getSize() { - var writer = new BufferWriter(); - this.toRaw(writer); - return writer.written; + return this.toWriter(new BufferWriter()).written; }; /** - * Inspect the headers and return a more - * user-friendly representation of the data. - * @returns {Object} + * Serialize the headers to a buffer writer. + * @param {BufferWriter} bw */ -Headers.prototype.inspect = function inspect() { - return { - type: 'headers', - hash: this.rhash(), - height: this.height, - date: util.date(this.ts), - version: util.hex32(this.version), - prevBlock: util.revHex(this.prevBlock), - merkleRoot: util.revHex(this.merkleRoot), - ts: this.ts, - bits: this.bits, - nonce: this.nonce, - totalTX: this.totalTX - }; -}; - -/** - * Serialize the headers. - * @param {String?} enc - Encoding, can be `'hex'` or null. - * @returns {Buffer|String} - */ - -Headers.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - +Headers.prototype.toWriter = function toWriter(bw) { bw.writeU32(this.version); bw.writeHash(this.prevBlock); bw.writeHash(this.merkleRoot); @@ -90,13 +63,35 @@ Headers.prototype.toRaw = function toRaw(writer) { bw.writeU32(this.bits); bw.writeU32(this.nonce); bw.writeVarint(this.totalTX); - - if (!writer) - bw = bw.render(); - return bw; }; +/** + * Serialize the headers. + * @returns {Buffer|String} + */ + +Headers.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +/** + * Inject properties from buffer reader. + * @private + * @param {Buffer} data + */ + +Headers.prototype.fromReader = function fromReader(br) { + this.version = br.readU32(); // Technically signed + this.prevBlock = br.readHash('hex'); + this.merkleRoot = br.readHash('hex'); + this.ts = br.readU32(); + this.bits = br.readU32(); + this.nonce = br.readU32(); + this.totalTX = br.readVarint(); + return this; +}; + /** * Inject properties from serialized data. * @private @@ -104,17 +99,17 @@ Headers.prototype.toRaw = function toRaw(writer) { */ Headers.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + return this.fromReader(new BufferReader(data)); +}; - this.version = br.readU32(); // Technically signed - this.prevBlock = br.readHash('hex'); - this.merkleRoot = br.readHash('hex'); - this.ts = br.readU32(); - this.bits = br.readU32(); - this.nonce = br.readU32(); - this.totalTX = br.readVarint(); +/** + * Instantiate headers from buffer reader. + * @param {BufferReader} br + * @returns {Headers} + */ - return this; +Headers.fromReader = function fromReader(br) { + return new Headers().fromReader(br); }; /** @@ -131,24 +126,41 @@ Headers.fromRaw = function fromRaw(data, enc) { }; /** - * Inject properties from serialized data. + * Inject properties from buffer reader. * @private - * @param {Buffer} data + * @param {BufferReader} br */ -Headers.prototype.fromAbbr = function fromAbbr(data) { - var br = BufferReader(data); - +Headers.prototype.fromAbbrReader = function fromAbbrReader(br) { this.version = br.readU32(); // Technically signed this.prevBlock = br.readHash('hex'); this.merkleRoot = br.readHash('hex'); this.ts = br.readU32(); this.bits = br.readU32(); this.nonce = br.readU32(); - return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Headers.prototype.fromAbbr = function fromAbbr(data) { + return this.fromAbbrReader(new BufferReader(data)); +}; + +/** + * Instantiate headers from buffer reader. + * @param {BufferReader} br + * @returns {Headers} + */ + +Headers.fromAbbrReader = function fromAbbrReader(br) { + return new Headers().fromAbbrReader(br); +}; + /** * Instantiate headers from serialized data. * @param {Buffer} data @@ -197,6 +209,27 @@ Headers.fromBlock = function fromBlock(block) { return headers; }; +/** + * Inspect the headers and return a more + * user-friendly representation of the data. + * @returns {Object} + */ + +Headers.prototype.inspect = function inspect() { + return { + hash: this.rhash(), + height: this.height, + date: util.date(this.ts), + version: util.hex32(this.version), + prevBlock: util.revHex(this.prevBlock), + merkleRoot: util.revHex(this.merkleRoot), + ts: this.ts, + bits: this.bits, + nonce: this.nonce, + totalTX: this.totalTX + }; +}; + /** * Test an object to see if it is a Headers object. * @param {Object} obj diff --git a/lib/primitives/input.js b/lib/primitives/input.js index 6f4098eb..deff08c7 100644 --- a/lib/primitives/input.js +++ b/lib/primitives/input.js @@ -316,32 +316,52 @@ Input.fromJSON = function fromJSON(json) { * @returns {Buffer|String} */ -Input.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Input.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; - this.prevout.toRaw(bw); +/** + * Write the input to a buffer writer. + * @param {BufferWriter} bw + */ + +Input.prototype.toWriter = function toWriter(bw) { + this.prevout.toWriter(bw); bw.writeVarBytes(this.script.toRaw()); bw.writeU32(this.sequence); - - if (!writer) - bw = bw.render(); - return bw; }; +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ + +Input.prototype.fromReader = function fromReader(br) { + this.prevout.fromReader(br); + this.script.fromRaw(br.readVarBytes()); + this.sequence = br.readU32(); + return this; +}; + /** * Inject properties from serialized data. * @param {Buffer} data */ Input.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + return this.fromReader(new BufferReader(data)); +}; - this.prevout.fromRaw(br); - this.script.fromRaw(br.readVarBytes()); - this.sequence = br.readU32(); +/** + * Instantiate an input from a buffer reader. + * @param {BufferReader} br + * @returns {Input} + */ - return this; +Input.fromReader = function fromReader(br) { + return new Input().fromReader(br); }; /** @@ -357,52 +377,6 @@ Input.fromRaw = function fromRaw(data, enc) { return new Input().fromRaw(data); }; -/** - * Serialize the input to an "extended" format, - * including both the input and the witness. - * @param {String?} enc - Encoding, can be `'hex'` or null. - * @returns {Buffer|String} - */ - -Input.prototype.toExtended = function toExtended(writer) { - var bw = BufferWriter(writer); - - this.toRaw(bw); - this.witness.toRaw(bw); - - if (!writer) - bw = bw.render(); - - return bw; -}; - -/** - * Inject properties from extended serialized data. - * @private - * @param {Buffer} data - */ - -Input.prototype.fromExtended = function fromExtended(data) { - var br = BufferReader(data); - this.fromRaw(br); - this.witness.fromRaw(br); - return this; -}; - -/** - * Instantiate an input from a Buffer - * in "extended" serialization format. - * @param {Buffer} data - * @param {String?} enc - Encoding, can be `'hex'` or null. - * @returns {TX} - */ - -Input.fromExtended = function fromExtended(data, enc) { - if (typeof data === 'string') - data = new Buffer(data, enc); - return new Input().fromExtended(data); -}; - /** * Inject properties from coin. * @private diff --git a/lib/primitives/invitem.js b/lib/primitives/invitem.js index 9f1cce8a..4c5defc5 100644 --- a/lib/primitives/invitem.js +++ b/lib/primitives/invitem.js @@ -27,21 +27,36 @@ function InvItem(type, hash) { this.hash = hash; } +/** + * Write inv item to buffer writer. + * @param {BufferWriter} bw + */ + +InvItem.prototype.toWriter = function toWriter(bw) { + bw.writeU32(this.type); + bw.writeHash(this.hash); + return bw; +}; + /** * Serialize inv item. * @returns {Buffer} */ -InvItem.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +InvItem.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; - bw.writeU32(this.type); - bw.writeHash(this.hash); +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ - if (!writer) - bw = bw.render(); - - return bw; +InvItem.prototype.fromReader = function fromReader(br) { + this.type = br.readU32(); + this.hash = br.readHash('hex'); + return this; }; /** @@ -50,10 +65,17 @@ InvItem.prototype.toRaw = function toRaw(writer) { */ InvItem.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); - this.type = br.readU32(); - this.hash = br.readHash('hex'); - return this; + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate inv item from buffer reader. + * @param {BufferReader} br + * @returns {InvItem} + */ + +InvItem.fromReader = function fromReader(br) { + return new InvItem().fromReader(br); }; /** diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index c445fe58..74ed75e7 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -811,12 +811,11 @@ KeyRing.fromJSON = function fromJSON(json) { }; /** - * Serialize the keyring. - * @returns {Buffer} + * Write the keyring to a buffer writer. + * @param {BufferWriter} bw */ -KeyRing.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +KeyRing.prototype.toWriter = function toWriter(bw) { var field = 0; if (this.witness) @@ -839,20 +838,26 @@ KeyRing.prototype.toRaw = function toRaw(writer) { else bw.writeVarint(0); - if (!writer) - bw = bw.render(); - return bw; }; /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data + * Serialize the keyring. + * @returns {Buffer} */ -KeyRing.prototype.fromRaw = function fromRaw(data, network) { - var br = new BufferReader(data); +KeyRing.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + * @param {Network?} network + */ + +KeyRing.prototype.fromReader = function fromReader(br, network) { var field, compressed, key, script; this.network = Network.get(network); @@ -881,6 +886,27 @@ KeyRing.prototype.fromRaw = function fromRaw(data, network) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @param {Network?} network + */ + +KeyRing.prototype.fromRaw = function fromRaw(data, network) { + return this.fromReader(new BufferReader(data), network); +}; + +/** + * Instantiate a keyring from buffer reader. + * @param {BufferReader} br + * @returns {KeyRing} + */ + +KeyRing.fromReader = function fromReader(br) { + return new KeyRing().fromReader(br); +}; + /** * Instantiate a keyring from serialized data. * @param {Buffer} data diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index 22464b9c..b8e3ae59 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -83,15 +83,8 @@ MemBlock.fromOptions = function fromOptions(options) { * @returns {Buffer} */ -MemBlock.prototype.abbr = function abbr(writer) { - var data = this.raw.slice(0, 80); - - if (writer) { - writer.writeBytes(data); - return writer; - } - - return data; +MemBlock.prototype.abbr = function abbr() { + return this.raw.slice(0, 80); }; /** @@ -132,7 +125,7 @@ MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() { */ MemBlock.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data, true); + var br = new BufferReader(data, true); var height = -1; var inCount, input; diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 836f3a18..8fa1fc58 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -94,14 +94,12 @@ MerkleBlock.fromOptions = function fromOptions(data) { */ MerkleBlock.prototype.getSize = function getSize() { - var writer = new BufferWriter(); - this.toRaw(writer); - return writer.written; + return this.toWriter(new BufferWriter()).written; }; /** * Add a transaction to the block's tx vector. - * @param {TX|NakedTX} tx + * @param {TX} tx * @returns {TX} */ @@ -338,13 +336,11 @@ MerkleBlock.prototype.inspect = function inspect() { }; /** - * Serialize the merkleblock. - * @param {String?} enc - Encoding, can be `'hex'` or null. - * @returns {Buffer|String} + * Write the merkleblock to a buffer writer. + * @param {BufferWriter} bw */ -MerkleBlock.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +MerkleBlock.prototype.toWriter = function toWriter(bw) { var i; bw.writeU32(this.version); @@ -362,20 +358,26 @@ MerkleBlock.prototype.toRaw = function toRaw(writer) { bw.writeVarBytes(this.flags); - if (!writer) - bw = bw.render(); - return bw; }; /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data + * Serialize the merkleblock. + * @param {String?} enc - Encoding, can be `'hex'` or null. + * @returns {Buffer|String} */ -MerkleBlock.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +MerkleBlock.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ + +MerkleBlock.prototype.fromReader = function fromReader(br) { var i, count; this.version = br.readU32(); @@ -396,6 +398,26 @@ MerkleBlock.prototype.fromRaw = function fromRaw(data) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +MerkleBlock.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate a merkleblock from a buffer reader. + * @param {BufferReader} br + * @returns {MerkleBlock} + */ + +MerkleBlock.fromReader = function fromReader(br) { + return new MerkleBlock().fromReader(br); +}; + /** * Instantiate a merkleblock from a serialized data. * @param {Buffer} data diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index 13ce237e..d9ccb114 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -1386,6 +1386,14 @@ MTX.fromJSON = function fromJSON(json) { return new MTX().fromJSON(JSON)._mutable(); }; +/** + * @see TX.fromReader + */ + +MTX.fromReader = function fromReader(br) { + return new MTX().fromReader(br)._mutable(); +}; + /** * @see TX.fromRaw */ diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index e52635a3..87885f80 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -247,15 +247,13 @@ NetworkAddress.fromSocket = function fromSocket(hostname, network) { }; /** - * Inject properties from serialized data. + * Inject properties from buffer reader. * @private - * @param {Buffer} data + * @param {BufferReader} br * @param {Boolean?} full - Include timestamp. */ -NetworkAddress.prototype.fromRaw = function fromRaw(data, full) { - var br = BufferReader(data); - +NetworkAddress.prototype.fromReader = function fromReader(br, full) { // only version >= 31402 this.ts = full ? br.readU32() : 0; this.services = br.readU53(); @@ -267,6 +265,28 @@ NetworkAddress.prototype.fromRaw = function fromRaw(data, full) { return this; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @param {Boolean?} full - Include timestamp. + */ + +NetworkAddress.prototype.fromRaw = function fromRaw(data, full) { + return this.fromReader(new BufferReader(data), full); +}; + +/** + * Insantiate a network address from buffer reader. + * @param {BufferReader} br + * @param {Boolean?} full - Include timestamp. + * @returns {NetworkAddress} + */ + +NetworkAddress.fromReader = function fromReader(br, full) { + return new NetworkAddress().fromReader(br, full); +}; + /** * Insantiate a network address from serialized data. * @param {Buffer} data @@ -279,14 +299,13 @@ NetworkAddress.fromRaw = function fromRaw(data, full) { }; /** - * Serialize network address. - * @param {Boolean} full - Include timestamp. + * Write network address to a buffer writer. + * @param {BufferWriter} bw + * @param {Boolean?} full - Include timestamp. * @returns {Buffer} */ -NetworkAddress.prototype.toRaw = function toRaw(full, writer) { - var bw = BufferWriter(writer); - +NetworkAddress.prototype.toWriter = function toWriter(bw, full) { if (full) bw.writeU32(this.ts); @@ -294,12 +313,19 @@ NetworkAddress.prototype.toRaw = function toRaw(full, writer) { bw.writeBytes(IP.toBuffer(this.host)); bw.writeU16BE(this.port); - if (!writer) - bw = bw.render(); - return bw; }; +/** + * Serialize network address. + * @param {Boolean?} full - Include timestamp. + * @returns {Buffer} + */ + +NetworkAddress.prototype.toRaw = function toRaw(full) { + return this.toWriter(new BufferWriter(), full).render(); +}; + /* * Expose */ diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index 11adeb3b..5e769559 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -64,21 +64,36 @@ Outpoint.prototype.isNull = function isNull() { return this.index === 0xffffffff && this.hash === constants.NULL_HASH; }; +/** + * Write outpoint to a buffer writer. + * @param {BufferWriter} bw + */ + +Outpoint.prototype.toWriter = function toWriter(bw) { + bw.writeHash(this.hash); + bw.writeU32(this.index); + return bw; +}; + /** * Serialize outpoint. * @returns {Buffer} */ -Outpoint.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Outpoint.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; - bw.writeHash(this.hash); - bw.writeU32(this.index); +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ - if (!writer) - bw = bw.render(); - - return bw; +Outpoint.prototype.fromReader = function fromReader(br) { + this.hash = br.readHash('hex'); + this.index = br.readU32(); + return this; }; /** @@ -88,10 +103,17 @@ Outpoint.prototype.toRaw = function toRaw(writer) { */ Outpoint.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); - this.hash = br.readHash('hex'); - this.index = br.readU32(); - return this; + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate outpoint from a buffer reader. + * @param {BufferReader} br + * @returns {Outpoint} + */ + +Outpoint.fromReader = function fromReader(br) { + return new Outpoint().fromReader(br); }; /** diff --git a/lib/primitives/output.js b/lib/primitives/output.js index 4b71d939..0a7679b7 100644 --- a/lib/primitives/output.js +++ b/lib/primitives/output.js @@ -181,7 +181,7 @@ Output.prototype.getDustThreshold = function getDustThreshold(rate) { */ Output.prototype.getSize = function getSize() { - return this.toRaw(BufferWriter()).written; + return this.toWriter(new BufferWriter()).written; }; /** @@ -217,22 +217,37 @@ Output.fromJSON = function fromJSON(json) { return new Output().fromJSON(json); }; +/** + * Write the output to a buffer writer. + * @param {BufferWriter} bw + */ + +Output.prototype.toWriter = function toWriter(bw) { + bw.write64(this.value); + bw.writeVarBytes(this.script.toRaw()); + return bw; +}; + /** * Serialize the output. * @param {String?} enc - Encoding, can be `'hex'` or null. * @returns {Buffer|String} */ -Output.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Output.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; - bw.write64(this.value); - bw.writeVarBytes(this.script.toRaw()); +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ - if (!writer) - bw = bw.render(); - - return bw; +Output.prototype.fromReader = function fromReader(br) { + this.value = br.read64(); + this.script.fromRaw(br.readVarBytes()); + return this; }; /** @@ -242,12 +257,17 @@ Output.prototype.toRaw = function toRaw(writer) { */ Output.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + return this.fromReader(new BufferReader(data)); +}; - this.value = br.read64(); - this.script.fromRaw(br.readVarBytes()); +/** + * Instantiate an output from a buffer reader. + * @param {BufferReader} br + * @returns {Output} + */ - return this; +Output.fromReader = function fromReader(br) { + return new Output().fromReader(br); }; /** @@ -260,7 +280,6 @@ Output.prototype.fromRaw = function fromRaw(data) { Output.fromRaw = function fromRaw(data, enc) { if (typeof data === 'string') data = new Buffer(data, enc); - return new Output().fromRaw(data); }; diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 1b07984a..03a4196c 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -9,13 +9,13 @@ var assert = require('assert'); var util = require('../utils/util'); +var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var btcutils = require('../btc/utils'); var Amount = require('../btc/amount'); var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var Script = require('../script/script'); -var Stack = require('../script/stack'); var BufferWriter = require('../utils/writer'); var VerifyResult = require('../btc/errors').VerifyResult; var Input = require('./input'); @@ -92,7 +92,6 @@ function TX(options) { this._raw = null; this._size = -1; this._witnessSize = -1; - this._lastWitnessSize = 0; this._outputValue = -1; this._inputValue = -1; @@ -253,17 +252,11 @@ TX.prototype.hash = function _hash(enc) { TX.prototype.witnessHash = function witnessHash(enc) { var hash = this._whash; - if (this.isCoinbase()) { - return enc === 'hex' - ? constants.NULL_HASH - : util.copy(constants.ZERO_HASH); - } - if (!this.hasWitness()) return this.hash(enc); if (!hash) { - hash = crypto.hash256(this.toWitness()); + hash = crypto.hash256(this.toRaw()); if (!this.mutable) this._whash = hash; } @@ -279,13 +272,8 @@ TX.prototype.witnessHash = function witnessHash(enc) { * @returns {Buffer} Serialized transaction. */ -TX.prototype.toRaw = function toRaw(writer) { - var raw = this.getRaw(); - if (writer) { - writer.writeBytes(raw); - return writer; - } - return raw; +TX.prototype.toRaw = function toRaw() { + return this.getRaw().data; }; /** @@ -295,27 +283,34 @@ TX.prototype.toRaw = function toRaw(writer) { * @returns {Buffer} Serialized transaction. */ -TX.prototype.toNormal = function toNormal(writer) { - var raw = this.getRaw(); - if (!TX.isWitness(raw)) { - if (writer) { - writer.writeBytes(raw); - return writer; - } - return raw; - } - return this.frameNormal(writer); +TX.prototype.toNormal = function toNormal() { + if (this.hasWitness()) + return this.frameNormal().data; + return this.toRaw(); }; /** - * Serialize the transaction with the - * witness vector. Will use normal - * serialization if witness vector is empty. - * @returns {Buffer} Serialized transaction. + * Write the transaction to a buffer writer. + * @param {BufferWriter} bw */ -TX.prototype.toWitness = function toWitness(writer) { - return this.toRaw(writer); +TX.prototype.toWriter = function toWriter(bw) { + this.writeRaw(bw); + return bw; +}; + +/** + * Write the transaction to a buffer writer. + * Uses non-witness serialization. + * @param {BufferWriter} bw + */ + +TX.prototype.toNormalWriter = function toNormalWriter(bw) { + if (this.hasWitness()) { + this.frameNormalWriter(bw); + return bw; + } + return this.toWriter(bw); }; /** @@ -323,17 +318,26 @@ TX.prototype.toWitness = function toWitness(writer) { * that this is cached. This will use * the witness serialization if a * witness is present. - * @returns {Buffer} Serialized transaction. + * @private + * @returns {RawTX} */ TX.prototype.getRaw = function getRaw() { var raw; + if (this.mutable) { + assert(!this._raw); + if (this.hasWitness()) + return this.frameWitness(); + return this.frameNormal(); + } + if (this._raw) { assert(this._size > 0); assert(this._witnessSize >= 0); - this._lastWitnessSize = this._witnessSize; - return this._raw; + raw = new RawTX(this._size, this._witnessSize); + raw.data = this._raw; + return raw; } if (this.hasWitness()) @@ -341,39 +345,43 @@ TX.prototype.getRaw = function getRaw() { else raw = this.frameNormal(); - if (!this.mutable) { - this._raw = raw; - this._size = raw.length; - this._witnessSize = this._lastWitnessSize; - } + this._raw = raw.data; + this._size = raw.total; + this._witnessSize = raw.witness; return raw; }; /** - * Calculate real size and size of the witness bytes. - * @returns {Object} Contains `size` and `witnessSize`. + * Write raw transaction to buffer writer. + * Cache if possible. + * @returns {RawTX} + */ + +TX.prototype.writeRaw = function writeRaw(bw) { + var raw; + + if (this.mutable) { + if (this.hasWitness()) + return this.frameWitnessWriter(bw); + return this.frameNormalWriter(bw); + } + + raw = this.getRaw(); + bw.writeBytes(raw.data); + + return raw; +}; + +/** + * Calculate total size and size of the witness bytes. + * @returns {Object} Contains `total` and `witness`. */ TX.prototype.getSizes = function getSizes() { - var sizes = new TXSizes(); - var writer; - - if (this.mutable) { - assert(!this._raw); - writer = new BufferWriter(); - this.toRaw(writer); - sizes.total = writer.written; - sizes.witness = this._lastWitnessSize; - return sizes; - } - - this.getRaw(); - - sizes.total = this._size; - sizes.witness = this._witnessSize; - - return sizes; + if (this.mutable) + return this.writeRaw(new BufferWriter()); + return this.getRaw(); }; /** @@ -394,9 +402,9 @@ TX.prototype.getVirtualSize = function getVirtualSize() { */ TX.prototype.getWeight = function getWeight() { - var sizes = this.getSizes(); - var base = sizes.total - sizes.witness; - return base * (constants.WITNESS_SCALE_FACTOR - 1) + sizes.total; + var raw = this.getSizes(); + var base = raw.total - raw.witness; + return base * (constants.WITNESS_SCALE_FACTOR - 1) + raw.total; }; /** @@ -417,8 +425,8 @@ TX.prototype.getSize = function getSize() { */ TX.prototype.getBaseSize = function getBaseSize() { - var sizes = this.getSizes(); - return sizes.total - sizes.witness; + var raw = this.getSizes(); + return raw.total - raw.witness; }; /** @@ -427,13 +435,14 @@ TX.prototype.getBaseSize = function getBaseSize() { */ TX.prototype.hasWitness = function hasWitness() { - var i; + var i, input; if (this._witnessSize !== -1) return this._witnessSize !== 0; for (i = 0; i < this.inputs.length; i++) { - if (this.inputs[i].witness.items.length > 0) + input = this.inputs[i]; + if (input.witness.items.length > 0) return true; } @@ -452,9 +461,6 @@ TX.prototype.hasWitness = function hasWitness() { */ TX.prototype.signatureHash = function signatureHash(index, prev, type, version) { - if (typeof index !== 'number') - index = this.inputs.indexOf(index); - if (typeof type === 'string') type = constants.hashType[type.toUpperCase()]; @@ -506,8 +512,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { input = this.inputs[index]; // Outpoint. - bw.writeHash(input.prevout.hash); - bw.writeU32(input.prevout.index); + input.prevout.toWriter(bw); // Replace script with previous // output script if current index. @@ -519,8 +524,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { input = this.inputs[i]; // Outpoint. - bw.writeHash(input.prevout.hash); - bw.writeU32(input.prevout.index); + input.prevout.toWriter(bw); // Replace script with previous // output script if current index. @@ -606,7 +610,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) { for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - input.prevout.toRaw(bw); + input.prevout.toWriter(bw); } prevouts = crypto.hash256(bw.render()); @@ -649,7 +653,7 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) { for (i = 0; i < this.outputs.length; i++) { output = this.outputs[i]; - output.toRaw(bw); + output.toWriter(bw); } outputs = crypto.hash256(bw.render()); @@ -715,12 +719,7 @@ TX.prototype.verify = function verify(flags) { */ TX.prototype.verifyInput = function verifyInput(index, flags) { - var input; - - if (typeof index === 'object') - index = this.inputs.indexOf(index); - - input = this.inputs[index]; + var input = this.inputs[index]; assert(input, 'Input does not exist.'); @@ -753,15 +752,15 @@ TX.prototype.verifyInput = function verifyInput(index, flags) { * @returns {Boolean} Whether the inputs are valid. */ -TX.prototype.verifyAsync = function verifyAsync(flags) { +TX.prototype.verifyAsync = co(function* verifyAsync(flags) { if (this.inputs.length === 0) - return Promise.resolve(false); + return false; if (this.isCoinbase()) - return Promise.resolve(true); + return true; - return workerPool.verify(this, flags); -}; + return yield workerPool.verify(this, flags); +}); /** * Verify a transaction input asynchronously. @@ -771,18 +770,11 @@ TX.prototype.verifyAsync = function verifyAsync(flags) { * @returns {Boolean} Whether the input is valid. */ -TX.prototype.verifyInputAsync = function verifyInputAsync(index, flags) { - var input; - - if (typeof index === 'object') - index = this.inputs.indexOf(index); - - input = this.inputs[index]; - +TX.prototype.verifyInputAsync = co(function* verifyInputAsync(index, flags) { + var input = this.inputs[index]; assert(input, 'Input does not exist.'); - - return workerPool.verifyInput(this, index, flags); -}; + return yield workerPool.verifyInput(this, index, flags); +}); /** * Test whether the transaction is a coinbase @@ -1169,13 +1161,17 @@ TX.prototype.isFinal = function isFinal(height, ts) { TX.prototype.getLegacySigops = function getLegacySigops() { var total = 0; - var i; + var i, input, output; - for (i = 0; i < this.inputs.length; i++) - total += this.inputs[i].script.getSigops(false); + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + total += input.script.getSigops(false); + } - for (i = 0; i < this.outputs.length; i++) - total += this.outputs[i].script.getSigops(false); + for (i = 0; i < this.outputs.length; i++) { + output = this.outputs[i]; + total += output.script.getSigops(false); + } return total; }; @@ -1187,19 +1183,20 @@ TX.prototype.getLegacySigops = function getLegacySigops() { TX.prototype.getScripthashSigops = function getScripthashSigops() { var total = 0; - var i, input; + var i, input, coin; if (this.isCoinbase()) return 0; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; + coin = input.coin; - if (!input.coin) + if (!coin) continue; - if (input.coin.script.isScripthash()) - total += input.coin.script.getScripthashSigops(input.script); + if (coin.script.isScripthash()) + total += coin.script.getScripthashSigops(input.script); } return total; @@ -1439,34 +1436,26 @@ TX.prototype.isStandard = function isStandard(ret) { TX.prototype.hasStandardInputs = function hasStandardInputs() { var maxSigops = constants.script.MAX_SCRIPTHASH_SIGOPS; - var VERIFY_NONE = constants.flags.VERIFY_NONE; - var i, input, stack, redeem; + var i, input, coin, redeem; if (this.isCoinbase()) return true; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; + coin = input.coin; - if (!input.coin) + if (!coin) return false; - if (input.coin.script.isUnknown()) + if (coin.script.isUnknown()) return false; - if (input.coin.script.isScripthash()) { - stack = new Stack(); + if (coin.script.isScripthash()) { + redeem = input.script.getRedeem(); - try { - input.script.execute(stack, VERIFY_NONE, this, i, 0); - } catch (e) { + if (!redeem) return false; - } - - if (stack.length === 0) - return false; - - redeem = Script.fromRaw(stack.top(-1)); if (redeem.getSigops(true) > maxSigops) return false; @@ -1737,13 +1726,14 @@ TX.prototype.maxSize = function maxSize() { */ TX.prototype.getModifiedSize = function getModifiedSize(size) { - var i, offset; + var i, input, offset; if (size == null) size = this.maxSize(); for (i = 0; i < this.inputs.length; i++) { - offset = 41 + Math.min(110, this.inputs[i].script.getSize()); + input = this.inputs[i]; + offset = 41 + Math.min(110, input.script.getSize()); if (size > offset) size -= offset; } @@ -1940,9 +1930,6 @@ TX.prototype.isWatched = function isWatched(filter) { var found = false; var i, input, output, prevout; - if (!filter) - return false; - // 1. Test the tx hash if (filter.test(this.hash())) found = true; @@ -2195,32 +2182,51 @@ TX.fromRaw = function fromRaw(data, enc) { return new TX().fromRaw(data); }; +/** + * Instantiate a transaction from a buffer reader. + * @param {BufferReader} br + * @returns {TX} + */ + +TX.fromReader = function fromReader(br) { + return new TX().fromReader(br); +}; + /** * Inject properties from serialized data. * @private - * @param {Buffer|BufferReader} data + * @param {Buffer} data */ TX.prototype.fromRaw = function fromRaw(data) { - var br, i, count; + return this.fromReader(new BufferReader(data)); +}; - if (TX.isWitness(data)) - return this.fromWitness(data); +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ + +TX.prototype.fromReader = function fromReader(br) { + var i, count; + + if (TX.isWitness(br)) + return this.fromWitnessReader(br); - br = BufferReader(data); br.start(); - this.version = br.readU32(); // Technically signed + this.version = br.readU32(); count = br.readVarint(); for (i = 0; i < count; i++) - this.inputs.push(Input.fromRaw(br)); + this.inputs.push(Input.fromReader(br)); count = br.readVarint(); for (i = 0; i < count; i++) - this.outputs.push(Output.fromRaw(br)); + this.outputs.push(Output.fromReader(br)); this.locktime = br.readU32(); @@ -2237,13 +2243,12 @@ TX.prototype.fromRaw = function fromRaw(data) { /** * Inject properties from serialized - * data (witness serialization). + * buffer reader (witness serialization). * @private - * @param {Buffer|BufferReader} data + * @param {BufferReader} br */ -TX.prototype.fromWitness = function fromWitness(data) { - var br = BufferReader(data); +TX.prototype.fromWitnessReader = function fromWitnessReader(br) { var flag = 0; var witnessSize = 0; var hasWitness = false; @@ -2251,7 +2256,7 @@ TX.prototype.fromWitness = function fromWitness(data) { br.start(); - this.version = br.readU32(); // Technically signed + this.version = br.readU32(); assert(br.readU8() === 0, 'Non-zero marker.'); @@ -2264,12 +2269,12 @@ TX.prototype.fromWitness = function fromWitness(data) { count = br.readVarint(); for (i = 0; i < count; i++) - this.inputs.push(Input.fromRaw(br)); + this.inputs.push(Input.fromReader(br)); count = br.readVarint(); for (i = 0; i < count; i++) - this.outputs.push(Output.fromRaw(br)); + this.outputs.push(Output.fromReader(br)); if (flag & 1) { flag ^= 1; @@ -2278,7 +2283,7 @@ TX.prototype.fromWitness = function fromWitness(data) { for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - input.witness.fromRaw(br); + input.witness.fromReader(br); if (input.witness.items.length > 0) hasWitness = true; } @@ -2311,12 +2316,39 @@ TX.prototype.fromWitness = function fromWitness(data) { /** * Serialize transaction without witness. * @private - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. + * @returns {RawTX} */ -TX.prototype.frameNormal = function frameNormal(writer) { - var bw = BufferWriter(writer); +TX.prototype.frameNormal = function frameNormal() { + var bw = new BufferWriter(); + var raw = this.frameNormalWriter(bw); + raw.data = bw.render(); + return raw; +}; + +/** + * Serialize transaction with witness. Calculates the witness + * size as it is framing (exposed on return value as `witness`). + * @private + * @returns {RawTX} + */ + +TX.prototype.frameWitness = function frameWitness() { + var bw = new BufferWriter(); + var raw = this.frameWitnessWriter(bw); + raw.data = bw.render(); + return raw; +}; + +/** + * Serialize transaction without witness. + * @private + * @param {BufferWriter} writer + * @returns {RawTX} + */ + +TX.prototype.frameNormalWriter = function frameNormalWriter(bw) { + var offset = bw.written; var i; if (this.inputs.length === 0 && this.outputs.length !== 0) @@ -2327,33 +2359,28 @@ TX.prototype.frameNormal = function frameNormal(writer) { bw.writeVarint(this.inputs.length); for (i = 0; i < this.inputs.length; i++) - this.inputs[i].toRaw(bw); + this.inputs[i].toWriter(bw); bw.writeVarint(this.outputs.length); for (i = 0; i < this.outputs.length; i++) - this.outputs[i].toRaw(bw); + this.outputs[i].toWriter(bw); bw.writeU32(this.locktime); - if (!writer) - bw = bw.render(); - - this._lastWitnessSize = 0; - - return bw; + return new RawTX(bw.written - offset, 0); }; /** * Serialize transaction with witness. Calculates the witness - * size as it is framing (exposed on return value as `_witnessSize`). + * size as it is framing (exposed on return value as `witness`). * @private - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. + * @param {BufferWriter} bw + * @returns {RawTX} */ -TX.prototype.frameWitness = function frameWitness(writer) { - var bw = BufferWriter(writer); +TX.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { + var offset = bw.written; var witnessSize = 0; var i, start; @@ -2367,17 +2394,17 @@ TX.prototype.frameWitness = function frameWitness(writer) { bw.writeVarint(this.inputs.length); for (i = 0; i < this.inputs.length; i++) - this.inputs[i].toRaw(bw); + this.inputs[i].toWriter(bw); bw.writeVarint(this.outputs.length); for (i = 0; i < this.outputs.length; i++) - this.outputs[i].toRaw(bw); + this.outputs[i].toWriter(bw); start = bw.written; for (i = 0; i < this.inputs.length; i++) - this.inputs[i].witness.toRaw(bw); + this.inputs[i].witness.toWriter(bw); witnessSize += bw.written - start; @@ -2386,12 +2413,7 @@ TX.prototype.frameWitness = function frameWitness(writer) { if (witnessSize === this.inputs.length) throw new Error('Cannot serialize empty-witness tx.'); - this._lastWitnessSize = witnessSize + 2; - - if (!writer) - bw = bw.render(); - - return bw; + return new RawTX(bw.written - offset, witnessSize + 2); }; /** @@ -2400,19 +2422,12 @@ TX.prototype.frameWitness = function frameWitness(writer) { * @returns {Boolean} */ -TX.isWitness = function isWitness(data) { - if (Buffer.isBuffer(data)) { - if (data.length < 6) - return false; - - return data[4] === 0 && data[5] !== 0; - } - - if (data.left() < 6) +TX.isWitness = function isWitness(br) { + if (br.left() < 6) return false; - return data.data[data.offset + 4] === 0 - && data.data[data.offset + 5] !== 0; + return br.data[br.offset + 4] === 0 + && br.data[br.offset + 5] !== 0; }; /** @@ -2426,13 +2441,13 @@ TX.isWitness = function isWitness(data) { * @returns {Buffer} */ -TX.prototype.toExtended = function toExtended(saveCoins, writer) { - var bw = BufferWriter(writer); +TX.prototype.toExtended = function toExtended(saveCoins) { + var bw = new BufferWriter(); var height = this.height; var index = this.index; var i, input, field, bit, oct; - this.toRaw(bw); + this.toWriter(bw); bw.writeU32(this.ps); @@ -2469,14 +2484,11 @@ TX.prototype.toExtended = function toExtended(saveCoins, writer) { continue; } - input.coin.toRaw(bw); + input.coin.toWriter(bw); } } - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2487,10 +2499,10 @@ TX.prototype.toExtended = function toExtended(saveCoins, writer) { */ TX.prototype.fromExtended = function fromExtended(data, saveCoins) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, input, coin, field, bit, oct, spent; - this.fromRaw(br); + this.fromReader(br); this.ps = br.readU32(); @@ -2520,7 +2532,7 @@ TX.prototype.fromExtended = function fromExtended(data, saveCoins) { if (spent) continue; - coin = Coin.fromRaw(br); + coin = Coin.fromReader(br); coin.hash = input.prevout.hash; coin.index = input.prevout.index; @@ -2570,9 +2582,10 @@ TX.isTX = function isTX(obj) { * Helpers */ -function TXSizes() { - this.total = 0; - this.witness = 0; +function RawTX(total, witness) { + this.data = null; + this.total = total; + this.witness = witness; } /* diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 16a2473f..144ba0c2 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -7,12 +7,13 @@ 'use strict'; +var assert = require('assert'); var BN = require('bn.js'); var constants = require('../protocol/constants'); var util = require('../utils/util'); var encoding = require('./encoding'); +var BufferReader = require('../utils/reader'); var BufferWriter = require('../utils/writer'); -var assert = require('assert'); var opcodes = constants.opcodes; /** @@ -30,45 +31,160 @@ function Opcode(value, data) { if (!(this instanceof Opcode)) return new Opcode(value, data); - this.value = value; + this.value = value || 0; this.data = data || null; } +/** + * Encode the opcode to a buffer writer. + * @param {BufferWriter} bw + */ + +Opcode.prototype.toWriter = function toWriter(bw) { + if (this.value === -1) + throw new Error('Cannot reserialize a parse error.'); + + if (!this.data) { + bw.writeU8(this.value); + return bw; + } + + if (this.value <= 0x4b) { + assert(this.value === this.data.length); + bw.writeU8(this.value); + bw.writeBytes(this.data); + return bw; + } + + switch (this.value) { + case opcodes.OP_PUSHDATA1: + bw.writeU8(this.value); + bw.writeU8(this.data.length); + bw.writeBytes(this.data); + break; + case opcodes.OP_PUSHDATA2: + bw.writeU8(this.value); + bw.writeU16(this.data.length); + bw.writeBytes(this.data); + break; + case opcodes.OP_PUSHDATA4: + bw.writeU8(this.value); + bw.writeU32(this.data.length); + bw.writeBytes(this.data); + break; + default: + throw new Error('Unknown pushdata opcode.'); + } + + return bw; +}; + /** * Encode the opcode. * @returns {Buffer} */ Opcode.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + return this.toWriter(new BufferWriter()).render(); +}; - if (this.value === -1) - throw new Error('Cannot reserialize a parse error.'); +/** + * Inject properties from buffer reader. + * @param {BufferReader} br + * @private + */ - if (this.data) { - if (this.value <= 0x4b) { - bw.writeU8(this.data.length); - bw.writeBytes(this.data); - } else if (this.value === opcodes.OP_PUSHDATA1) { - bw.writeU8(opcodes.OP_PUSHDATA1); - bw.writeU8(this.data.length); - bw.writeBytes(this.data); - } else if (this.value === opcodes.OP_PUSHDATA2) { - bw.writeU8(opcodes.OP_PUSHDATA2); - bw.writeU16(this.data.length); - bw.writeBytes(this.data); - } else if (this.value === opcodes.OP_PUSHDATA4) { - bw.writeU8(opcodes.OP_PUSHDATA4); - bw.writeU32(this.data.length); - bw.writeBytes(this.data); - } else { - throw new Error('Unknown pushdata opcode.'); +Opcode.prototype.fromReader = function fromReader(br) { + var op = br.readU8(); + var size; + + if (op >= 0x01 && op <= 0x4b) { + if (br.left() < op) { + this.value = -1; + return this; } - } else { - bw.writeU8(this.value); + this.value = op; + this.data = br.readBytes(op); + return this; } - return bw.render(); + switch (op) { + case opcodes.OP_PUSHDATA1: + if (br.left() < 1) { + this.value = -1; + break; + } + size = br.readU8(); + if (br.left() < size) { + this.value = -1; + break; + } + this.value = op; + this.data = br.readBytes(size); + break; + case opcodes.OP_PUSHDATA2: + if (br.left() < 2) { + this.value = -1; + break; + } + size = br.readU16(); + if (br.left() < size) { + this.value = -1; + break; + } + this.value = op; + this.data = br.readBytes(size); + break; + case opcodes.OP_PUSHDATA4: + if (br.left() < 4) { + this.value = -1; + break; + } + size = br.readU32(); + if (br.left() < size) { + this.value = -1; + break; + } + this.value = op; + this.data = br.readBytes(size); + break; + default: + this.value = op; + break; + } + + return this; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @returns {Opcode} + */ + +Opcode.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Instantiate opcode from buffer reader. + * @param {BufferReader} br + * @returns {Opcode} + */ + +Opcode.fromReader = function fromReader(br) { + return new Opcode().fromReader(br); +}; + +/** + * Instantiate opcode from serialized data. + * @param {Buffer} data + * @returns {Opcode} + */ + +Opcode.fromRaw = function fromRaw(data) { + return new Opcode().fromRaw(data); }; /** diff --git a/lib/script/script.js b/lib/script/script.js index 1d37ac39..4f469183 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -129,7 +129,7 @@ Script.prototype.toArray = function toArray() { Script.prototype.fromArray = function fromArray(code) { assert(Array.isArray(code)); this.code = Script.parseArray(code); - this.raw = Script.encode(this.code); + this.compile(); return this; }; @@ -186,7 +186,27 @@ Script.prototype.toASM = function toASM(decode) { */ Script.prototype.compile = function compile() { - this.raw = Script.encode(this.code); + var bw = new BufferWriter(); + var i, op; + + for (i = 0; i < this.code.length; i++) { + op = this.code[i]; + op.toWriter(bw); + } + + this.raw = bw.render(); + + return this; +}; + +/** + * Write the script to a buffer writer. + * @param {BufferWriter} bw + */ + +Script.prototype.toWriter = function toWriter(bw) { + bw.writeVarBytes(this.raw); + return bw; }; /** @@ -195,11 +215,7 @@ Script.prototype.compile = function compile() { * @returns {Buffer|String} Serialized script. */ -Script.prototype.toRaw = function toRaw(writer) { - if (writer) { - writer.writeVarBytes(this.raw); - return writer; - } +Script.prototype.toRaw = function toRaw() { return this.raw; }; @@ -1401,7 +1417,7 @@ Script.prototype.removeData = function removeData(data) { for (i = index.length - 1; i >= 0; i--) this.code.splice(index[i], 1); - this.raw = Script.encode(this.code); + this.compile(); return index.length; }; @@ -1478,14 +1494,11 @@ Script.isMinimal = function isMinimal(data, opcode, flags) { */ Script.isCode = function isCode(raw) { - var i, op, code; + var script = Script.fromRaw(raw); + var i, op; - assert(Buffer.isBuffer(raw)); - - code = Script.decode(raw); - - for (i = 0; i < code.length; i++) { - op = code[i]; + for (i = 0; i < script.code.length; i++) { + op = script.code[i]; if (op.data) continue; @@ -2430,7 +2443,8 @@ Script.prototype.getCoinbaseFlags = function getCoinbaseFlags() { else nonce = -1; - flags = Script.encode(this.code.slice(index)); + flags = Script.fromArray(this.code.slice(index)); + flags = flags.toRaw(); text = flags.toString('utf8'); text = text.replace(/[\u0000-\u0019\u007f-\u00ff]/g, ''); @@ -3439,6 +3453,16 @@ Script.sign = function sign(msg, key, type) { return bw.render(); }; +/** + * Inject properties from buffer reader. + * @private + * @param {BufferReader} br + */ + +Script.prototype.fromReader = function fromReader(br) { + return this.fromRaw(br.readVarBytes()); +}; + /** * Inject properties from serialized data. * @private @@ -3446,15 +3470,27 @@ Script.sign = function sign(msg, key, type) { */ Script.prototype.fromRaw = function fromRaw(data) { - if (data instanceof BufferReader) - data = data.readVarBytes(); + var br = new BufferReader(data, true); this.raw = data; - this.code = Script.decode(data); + + while (br.left()) + this.code.push(Opcode.fromReader(br)); return this; }; +/** + * Create a script from buffer reader. + * @param {BufferReader} br + * @param {String?} enc - Either `"hex"` or `null`. + * @returns {Script} + */ + +Script.fromReader = function fromReader(br) { + return new Script().fromReader(br); +}; + /** * Create a script from a serialized buffer. * @param {Buffer|String} data - Serialized script. @@ -3468,127 +3504,6 @@ Script.fromRaw = function fromRaw(data, enc) { return new Script().fromRaw(data); }; -/** - * Decode a serialized script into script code. - * Note that the serialized script must _not_ - * include the varint size before it. Parse - * errors will output an opcode with a value - * of -1. - * - * @param {Buffer} raw - Serialized script. - * @returns {Opcode[]} Script code. - */ - -Script.decode = function decode(raw) { - var br = new BufferReader(raw, true); - var code = []; - var op, size, data; - - assert(Buffer.isBuffer(raw)); - - while (br.left()) { - op = br.readU8(); - if (op >= 0x01 && op <= 0x4b) { - if (br.left() < op) { - code.push(new Opcode(-1)); - break; - } - data = br.readBytes(op); - code.push(new Opcode(op, data)); - } else if (op === opcodes.OP_PUSHDATA1) { - if (br.left() < 1) { - code.push(new Opcode(-1)); - break; - } - size = br.readU8(); - if (br.left() < size) { - code.push(new Opcode(-1)); - break; - } - data = br.readBytes(size); - code.push(new Opcode(op, data)); - } else if (op === opcodes.OP_PUSHDATA2) { - if (br.left() < 2) { - code.push(new Opcode(-1)); - break; - } - size = br.readU16(); - if (br.left() < size) { - code.push(new Opcode(-1)); - break; - } - data = br.readBytes(size); - code.push(new Opcode(op, data)); - } else if (op === opcodes.OP_PUSHDATA4) { - if (br.left() < 4) { - code.push(new Opcode(-1)); - break; - } - size = br.readU32(); - if (br.left() < size) { - code.push(new Opcode(-1)); - break; - } - data = br.readBytes(size); - code.push(new Opcode(op, data)); - } else { - code.push(new Opcode(op)); - } - } - - return code; -}; - -/** - * Encode and serialize script code. This will _not_ - * include the varint size at the start. - * @param {Array} code - Script code. - * @returns {Buffer} Serialized script. - */ - -Script.encode = function encode(code, writer) { - var bw = new BufferWriter(writer); - var i, op; - - assert(Array.isArray(code)); - - for (i = 0; i < code.length; i++) { - op = code[i]; - - if (op.value === -1) - throw new Error('Cannot reserialize a parse error.'); - - if (op.data) { - if (op.value <= 0x4b) { - bw.writeU8(op.data.length); - bw.writeBytes(op.data); - } else if (op.value === opcodes.OP_PUSHDATA1) { - bw.writeU8(opcodes.OP_PUSHDATA1); - bw.writeU8(op.data.length); - bw.writeBytes(op.data); - } else if (op.value === opcodes.OP_PUSHDATA2) { - bw.writeU8(opcodes.OP_PUSHDATA2); - bw.writeU16(op.data.length); - bw.writeBytes(op.data); - } else if (op.value === opcodes.OP_PUSHDATA4) { - bw.writeU8(opcodes.OP_PUSHDATA4); - bw.writeU32(op.data.length); - bw.writeBytes(op.data); - } else { - throw new Error('Unknown pushdata opcode.'); - } - continue; - } - - bw.writeU8(op.value); - } - - if (!writer) - bw = bw.render(); - - return bw; -}; - /** * Convert an array of Buffers and * Numbers into an array of Opcodes. diff --git a/lib/script/witness.js b/lib/script/witness.js index 22dfa2d4..8a66b73c 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -299,13 +299,11 @@ Witness.prototype.indexOf = function indexOf(data) { }; /** - * Encode the witness to a Buffer. - * @param {String} enc - Encoding, either `'hex'` or `null`. - * @returns {Buffer|String} Serialized script. + * Write witness to a buffer writer. + * @param {BufferWriter} bw */ -Witness.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Witness.prototype.toWriter = function toWriter(bw) { var i; bw.writeVarint(this.items.length); @@ -313,12 +311,19 @@ Witness.prototype.toRaw = function toRaw(writer) { for (i = 0; i < this.items.length; i++) bw.writeVarBytes(this.items[i]); - if (!writer) - bw = bw.render(); - return bw; }; +/** + * Encode the witness to a Buffer. + * @param {String} enc - Encoding, either `'hex'` or `null`. + * @returns {Buffer|String} Serialized script. + */ + +Witness.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + /** * Convert witness to a hex string. * @returns {String} @@ -513,13 +518,12 @@ Witness.encodeItem = function encodeItem(data) { }; /** - * Inject properties from serialized data. + * Inject properties from buffer reader. * @private - * @param {Buffer} data + * @param {BufferReader} br */ -Witness.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); +Witness.prototype.fromReader = function fromReader(br) { var chunkCount = br.readVarint(); var i; @@ -530,7 +534,26 @@ Witness.prototype.fromRaw = function fromRaw(data) { }; /** - * Create a Witness from a serialized buffer. + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Witness.prototype.fromRaw = function fromRaw(data) { + return this.fromReader(new BufferReader(data)); +}; + +/** + * Create a witness from a buffer reader. + * @param {BufferReader} br + */ + +Witness.fromReader = function fromReader(br) { + return new Witness().fromReader(br); +}; + +/** + * Create a witness from a serialized buffer. * @param {Buffer|String} data - Serialized witness. * @param {String?} enc - Either `"hex"` or `null`. * @returns {Witness} diff --git a/lib/utils/asn1.js b/lib/utils/asn1.js index cbf7f1b6..7cd16ef2 100644 --- a/lib/utils/asn1.js +++ b/lib/utils/asn1.js @@ -29,113 +29,96 @@ 'use strict'; -var assert = require('assert'); var BufferReader = require('./reader'); var ASN1 = exports; -ASN1.parseTag = function parseTag(br) { - var tag = br.readU8(); - var primitive = (tag & 0x20) === 0; +/* + * Primitives + */ + +ASN1.readTag = function readTag(br) { + var type = br.readU8(); + var primitive = (type & 0x20) === 0; var oct; - if ((tag & 0x1f) === 0x1f) { - oct = tag; - tag = 0; + if ((type & 0x1f) === 0x1f) { + oct = type; + type = 0; while ((oct & 0x80) === 0x80) { oct = br.readU8(); - tag <<= 7; - tag |= oct & 0x7f; + type <<= 7; + type |= oct & 0x7f; } } else { - tag &= 0x1f; + type &= 0x1f; } return { + type: type, primitive: primitive, - tag: tag, - len: ASN1.parseLen(br, primitive) + size: ASN1.readSize(br, primitive) }; }; -ASN1.parseLen = function parseLen(br, primitive) { - var len = br.readU8(); - var num, i, j; +ASN1.readSize = function readSize(br, primitive) { + var size = br.readU8(); + var bytes, i, j; // Indefinite form - if (!primitive && len === 0x80) - return null; + if (!primitive && size === 0x80) + throw new Error('Indefinite size.'); // Definite form - if ((len & 0x80) === 0) { + if ((size & 0x80) === 0) { // Short form - return len; + return size; } // Long form - num = len & 0x7f; - assert(num < 4, 'length octect is too long'); + bytes = size & 0x7f; - len = 0; - for (i = 0; i < num; i++) { - len <<= 8; + if (bytes > 3) + throw new Error('Length octet is too long.'); + + size = 0; + for (i = 0; i < bytes; i++) { + size <<= 8; j = br.readU8(); - len |= j; + size |= j; } - return len; + return size; }; -ASN1.parseCert = function parseCert(data) { - var d = BufferReader(data); - var br; - - d.start(); - - br = BufferReader(ASN1.parseSeq(d)); - - return { - tbs: ASN1.parseTBS(br), - sigAlg: ASN1.parseAlgIdent(br), - sig: ASN1.parseBitstr(br), - raw: d.endData(true) - }; +ASN1.readSeq = function readSeq(br) { + var tag = ASN1.implicit(br, 0x10); + return br.readBytes(tag.size); }; -ASN1.parseTBS = function parseTBS(data) { - var d = BufferReader(data); - var br; - - d.start(); - - br = BufferReader(ASN1.parseSeq(d)); - - return { - version: ASN1.parseExplicitInt(br, 0, true), - serial: ASN1.parseInt(br), - sig: ASN1.parseAlgIdent(br), - issuer: ASN1.parseName(br), - validity: ASN1.parseValidity(br), - subject: ASN1.parseName(br), - pubkey: ASN1.parsePubkey(br), - raw: d.endData(true) - }; +ASN1.implicit = function implicit(br, type) { + var tag = ASN1.readTag(br); + if (tag.type !== type) + throw new Error('Unexpected tag: ' + tag.type + '.'); + return tag; }; -ASN1.parseSeq = function parseSeq(data) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); - assert.equal(tag.tag, 0x10); // seq - return br.readBytes(tag.len, true); +ASN1.explicit = function explicit(br, type) { + var offset = br.offset; + var tag = ASN1.readTag(br); + if (tag.type !== type) { + br.offset = offset; + return false; + } + return true; }; -ASN1.parseInt = function parseInt(data, readNum) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); - var num; +ASN1.seq = function seq(br) { + return new BufferReader(ASN1.readSeq(br), true); +}; - assert.equal(tag.tag, 0x02); // int - - num = br.readBytes(tag.len, true); +ASN1.readInt = function readInt(br, readNum) { + var tag = ASN1.implicit(br, 0x02); + var num = br.readBytes(tag.size); if (readNum) return num.readUIntBE(0, num.length); @@ -143,30 +126,28 @@ ASN1.parseInt = function parseInt(data, readNum) { return num; }; -ASN1.parseExplicitInt = function parseExplicitInt(data, i, readNum) { - var br = BufferReader(data); - var off = br.offset; - var tag = ASN1.parseTag(br); - if (tag.tag !== i) { - br.seek(-(br.offset - off)); +ASN1.readExplicitInt = function readExplicitInt(br, type, readNum) { + if (!ASN1.explicit(br, type)) return -1; - } - return ASN1.parseInt(br, readNum); + return ASN1.readInt(br, readNum); }; -ASN1.parseBitstr = function parseBitstr(data) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); - assert.equal(tag.tag, 0x03); // bitstr - return ASN1.alignBitstr(br.readBytes(tag.len, true)); +ASN1.readBitstr = function readBitstr(br) { + var tag = ASN1.implicit(br, 0x03); + var str = br.readBytes(tag.size); + return ASN1.alignBitstr(str); }; -ASN1.parseString = function parseString(data) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); - switch (tag.tag) { +ASN1.readString = function readString(br) { + var tag = ASN1.readTag(br); + var str; + + switch (tag.type) { case 0x03: // bitstr - return ASN1.alignBitstr(br.readBytes(tag.len, true)); + str = br.readBytes(tag.size); + return ASN1.alignBitstr(str); + // Note: + // Fuck all these. case 0x04: // octstr case 0x12: // numstr case 0x13: // prinstr @@ -180,9 +161,9 @@ ASN1.parseString = function parseString(data) { case 0x1c: // unistr case 0x1d: // charstr case 0x1e: // bmpstr - return br.readString('utf8', tag.len); + return br.readString('utf8', tag.size); default: - assert(false, 'Bad string.'); + throw new Error('Unexpected tag: ' + tag.type + '.'); } }; @@ -207,52 +188,83 @@ ASN1.alignBitstr = function(data) { return out; }; -ASN1.parsePubkey = function parsePubkey(data) { - var br = BufferReader(data); - br = BufferReader(ASN1.parseSeq(br)); +/* + * Certificates + */ + +ASN1.readCert = function readCert(br) { + var buf = br; + + buf.start(); + + br = ASN1.seq(buf); + return { - alg: ASN1.parseAlgIdent(br), - pubkey: ASN1.parseBitstr(br) + tbs: ASN1.readTBS(br), + sigAlg: ASN1.readAlgIdent(br), + sig: ASN1.readBitstr(br), + raw: buf.endData(true) }; }; -ASN1.parseName = function parseName(data) { - var br = BufferReader(data); - var values = []; - var tag; +ASN1.readTBS = function readTBS(br) { + var buf = br; - br = BufferReader(ASN1.parseSeq(br)); + buf.start(); + + br = ASN1.seq(buf); + + return { + version: ASN1.readExplicitInt(br, 0x00, true), + serial: ASN1.readInt(br), + sig: ASN1.readAlgIdent(br), + issuer: ASN1.readName(br), + validity: ASN1.readValidity(br), + subject: ASN1.readName(br), + pubkey: ASN1.readPubkey(br), + raw: buf.endData(true) + }; +}; + +ASN1.readPubkey = function readPubkey(br) { + br = ASN1.seq(br); + return { + alg: ASN1.readAlgIdent(br), + pubkey: ASN1.readBitstr(br) + }; +}; + +ASN1.readName = function readName(br) { + var values = []; + + br = ASN1.seq(br); while (br.left()) { - tag = ASN1.parseTag(br); - assert.equal(tag.tag, 0x11); // set - tag = ASN1.parseTag(br); - assert.equal(tag.tag, 0x10); // seq + ASN1.implicit(br, 0x11); // set + ASN1.implicit(br, 0x10); // seq values.push({ - type: ASN1.parseOID(br), - value: ASN1.parseString(br) + type: ASN1.readOID(br), + value: ASN1.readString(br) }); } return values; }; -ASN1.parseValidity = function parseValidity(data) { - var br = BufferReader(data); - br = BufferReader(ASN1.parseSeq(br)); +ASN1.readValidity = function readValidity(br) { + br = ASN1.seq(br); return { - notBefore: ASN1.parseTime(br), - notAfter: ASN1.parseTime(br) + notBefore: ASN1.readTime(br), + notAfter: ASN1.readTime(br) }; }; -ASN1.parseTime = function parseTime(data) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); - var str = br.readString('ascii', tag.len); +ASN1.readTime = function readTime(br) { + var tag = ASN1.readTag(br); + var str = br.readString('ascii', tag.size); var year, mon, day, hour, min, sec; - switch (tag.tag) { + switch (tag.type) { case 0x17: // utctime year = str.slice(0, 2) | 0; mon = str.slice(2, 4) | 0; @@ -274,25 +286,24 @@ ASN1.parseTime = function parseTime(data) { sec = str.slice(12, 14) | 0; break; default: - assert(false); - break; + throw new Error('Unexpected tag: ' + tag.type + '.'); } return Date.UTC(year, mon - 1, day, hour, min, sec, 0) / 1000; }; -ASN1.parseOID = function parseOID(data) { - var br = BufferReader(data); - var tag = ASN1.parseTag(br); +ASN1.readOID = function readOID(br) { + var tag = ASN1.implicit(br, 0x06); + var data = br.readBytes(tag.size); + return ASN1.formatOID(data); +}; + +ASN1.formatOID = function formatOID(data) { + var br = new BufferReader(data); var ids = []; var ident = 0; var subident = 0; - var objid, result, first, second; - - assert.equal(tag.tag, 0x06); // objid - - objid = br.readBytes(tag.len, true); - br = BufferReader(objid); + var result, first, second; while (br.left()) { subident = br.readU8(); @@ -314,17 +325,17 @@ ASN1.parseOID = function parseOID(data) { return result.join('.'); }; -ASN1.parseAlgIdent = function parseAlgIdent(data) { - var br = BufferReader(data); +ASN1.readAlgIdent = function readAlgIdent(br) { var params = null; - var alg; + var alg, tag; - br = BufferReader(ASN1.parseSeq(br)); + br = ASN1.seq(br); - alg = ASN1.parseOID(br); + alg = ASN1.readOID(br); if (br.left() > 0) { - params = br.readBytes(ASN1.parseTag(br).len, true); + tag = ASN1.readTag(br); + params = br.readBytes(tag.size); if (params.length === 0) params = null; } @@ -335,27 +346,49 @@ ASN1.parseAlgIdent = function parseAlgIdent(data) { }; }; -ASN1.parseRSAPublic = function parseRSAPublic(data) { - var br = BufferReader(data); - br = BufferReader(ASN1.parseSeq(br)); +/* + * RSA + */ + +ASN1.readRSAPublic = function readRSAPublic(br) { + br = ASN1.seq(br); return { - modulus: ASN1.parseInt(br), - publicExponent: ASN1.parseInt(br) + modulus: ASN1.readInt(br), + publicExponent: ASN1.readInt(br) }; }; -ASN1.parseRSAPrivate = function parseRSAPrivate(data) { - var br = BufferReader(data); - br = BufferReader(ASN1.parseSeq(br)); +ASN1.readRSAPrivate = function readRSAPrivate(br) { + br = ASN1.seq(br); return { - version: ASN1.parseInt(br, true), - modulus: ASN1.parseInt(br), - publicExponent: ASN1.parseInt(br), - privateExponent: ASN1.parseInt(br), - prime1: ASN1.parseInt(br), - prime2: ASN1.parseInt(br), - exponent1: ASN1.parseInt(br), - exponent2: ASN1.parseInt(br), - coefficient: ASN1.parseInt(br) + version: ASN1.readInt(br, true), + modulus: ASN1.readInt(br), + publicExponent: ASN1.readInt(br), + privateExponent: ASN1.readInt(br), + prime1: ASN1.readInt(br), + prime2: ASN1.readInt(br), + exponent1: ASN1.readInt(br), + exponent2: ASN1.readInt(br), + coefficient: ASN1.readInt(br) }; }; + +/* + * Public + */ + +ASN1.parseRSAPublic = function parseRSAPublic(data) { + return ASN1.readRSAPublic(new BufferReader(data, true)); +}; + +ASN1.parseRSAPrivate = function parseRSAPrivate(data) { + return ASN1.readRSAPrivate(new BufferReader(data, true)); +}; + +ASN1.parseCert = function parseCert(data) { + return ASN1.readCert(new BufferReader(data, true)); +}; + +ASN1.parseTBS = function parseTBS(data) { + return ASN1.readTBS(new BufferReader(data, true)); +}; diff --git a/lib/utils/bloom.js b/lib/utils/bloom.js index 8006d575..4603f1fd 100644 --- a/lib/utils/bloom.js +++ b/lib/utils/bloom.js @@ -178,18 +178,15 @@ Bloom.fromRate = function fromRate(items, rate, update) { * @returns {Buffer} */ -Bloom.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); +Bloom.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeVarBytes(this.filter); bw.writeU32(this.n); bw.writeU32(this.tweak); bw.writeU8(this.update); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -199,7 +196,7 @@ Bloom.prototype.toRaw = function toRaw(writer) { */ Bloom.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.filter = br.readVarBytes(); this.n = br.readU32(); diff --git a/lib/utils/protobuf.js b/lib/utils/protobuf.js index e13423ad..9cdfe34f 100644 --- a/lib/utils/protobuf.js +++ b/lib/utils/protobuf.js @@ -11,6 +11,10 @@ var assert = require('assert'); var BufferReader = require('../utils/reader'); var BufferWriter = require('../utils/writer'); +/* + * Constants + */ + var wireType = { VARINT: 0, FIXED64: 1, @@ -20,11 +24,15 @@ var wireType = { FIXED32: 5 }; +/** + * ProtoReader + * @constructor + */ + function ProtoReader(data, zeroCopy) { - if (data instanceof ProtoReader) - return data; if (!(this instanceof ProtoReader)) return new ProtoReader(data, zeroCopy); + BufferReader.call(this, data, zeroCopy); } @@ -92,72 +100,105 @@ ProtoReader.prototype.nextTag = function nextTag() { ProtoReader.prototype.readField = function readField(tag, opt) { var offset = this.offset; var header = this.readVarint(); - var value, data, group, field; + var field = new Field(header); + var inner; - if (tag != null && (header >>> 3) !== tag) { + if (tag != null && field.tag !== tag) { assert(opt, 'Non-optional field not present.'); this.offset = offset; - return; + return null; } - switch (header & 7) { + switch (field.type) { case wireType.VARINT: - value = this.readVarint(); + field.value = this.readVarint(); break; case wireType.FIXED64: - value = this.readU64(); + field.value = this.readU64(); break; case wireType.DELIMITED: - data = this.readVarBytes(); + field.data = this.readVarBytes(); break; case wireType.START_GROUP: - group = []; + field.group = []; for (;;) { - field = this.readField(); - if (field.type === wireType.END_GROUP) + inner = this.readField(); + if (inner.type === wireType.END_GROUP) break; - group.push(field); + field.group.push(inner); } break; case wireType.END_GROUP: assert(false, 'Unexpected end group.'); break; case wireType.FIXED32: - value = this.readU32(); + field.value = this.readU32(); break; default: assert(false, 'Bad wire type.'); break; } - return { - size: this.offset - offset, - header: header, - tag: header >>> 3, - type: header & 7, - value: value, - data: data, - group: group - }; + field.size = this.offset - offset; + + return field; }; -function ProtoWriter(options) { - if (options instanceof ProtoWriter) - return options; +/** + * ProtoWriter + * @constructor + */ +function ProtoWriter() { if (!(this instanceof ProtoWriter)) - return new ProtoWriter(options); + return new ProtoWriter(); - BufferWriter.call(this, options); + BufferWriter.call(this); } util.inherits(ProtoWriter, BufferWriter); ProtoWriter.prototype.writeVarint = function writeVarint(num) { var size = exports.sizeVarint(num); - var buf = new Buffer(size); - exports.writeVarint(buf, num, 0); - this.writeBytes(buf); + var value; + + // Avoid an extra allocation until + // we make bufferwriter more hackable. + // More insanity here... + switch (size) { + case 6: + value = exports.slipVarint(num); + this.writeU32BE(value / 0x10000 | 0); + this.writeU16BE(value & 0xffff); + break; + case 5: + value = exports.slipVarint(num); + this.writeU32BE(value / 0x100 | 0); + this.writeU8(value & 0xff); + break; + case 4: + value = exports.slipVarint(num); + this.writeU32BE(value); + break; + case 3: + value = exports.slipVarint(num); + this.writeU16BE(value >> 8); + this.writeU8(value & 0xff); + break; + case 2: + value = exports.slipVarint(num); + this.writeU16BE(value); + break; + case 1: + value = exports.slipVarint(num); + this.writeU8(value); + break; + default: + value = new Buffer(size); + exports.writeVarint(value, num, 0); + this.writeBytes(value); + break; + } }; ProtoWriter.prototype.writeFieldVarint = function writeFieldVarint(tag, value) { @@ -189,6 +230,10 @@ ProtoWriter.prototype.writeFieldString = function writeFieldString(tag, data, en this.writeFieldBytes(tag, data); }; +/* + * Encoding + */ + exports.readVarint = function readVarint(data, off) { var num = 0; var ch = 0x80; @@ -199,7 +244,9 @@ exports.readVarint = function readVarint(data, off) { num = 0; break; } + ch = data[off++]; + // Optimization for javascript insanity. switch (size) { case 0: @@ -215,12 +262,13 @@ exports.readVarint = function readVarint(data, off) { num += (ch & 0x7f) * Math.pow(2, 7 * size); break; } + size++; + + assert(size < 7, 'Number exceeds 2^53-1.'); } - assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); - - return { size: size, value: num }; + return new Varint(size, num); }; exports.writeVarint = function writeVarint(data, num, off) { @@ -242,6 +290,28 @@ exports.writeVarint = function writeVarint(data, num, off) { return off; }; +exports.slipVarint = function slipVarint(num) { + var data = 0; + var size = 0; + var ch; + + assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); + + do { + assert(size < 7); + ch = num & 0x7f; + num -= num % 0x80; + num /= 0x80; + if (num !== 0) + ch |= 0x80; + data *= 256; + data += ch; + size++; + } while (num > 0); + + return data; +}; + exports.sizeVarint = function sizeVarint(num) { var size = 0; @@ -256,5 +326,27 @@ exports.sizeVarint = function sizeVarint(num) { return size; }; +/* + * Helpers + */ + +function Field(header) { + this.tag = header >>> 3; + this.type = header & 7; + this.size = 0; + this.value = 0; + this.data = null; + this.group = null; +} + +function Varint(size, value) { + this.size = size; + this.value = value; +} + +/* + * Expose + */ + exports.ProtoReader = ProtoReader; exports.ProtoWriter = ProtoWriter; diff --git a/lib/utils/reader.js b/lib/utils/reader.js index b13b75c7..c6339c61 100644 --- a/lib/utils/reader.js +++ b/lib/utils/reader.js @@ -7,9 +7,9 @@ 'use strict'; +var assert = require('assert'); var encoding = require('./encoding'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); /** * An object that allows reading of buffers in a sane manner. @@ -22,15 +22,14 @@ var assert = require('assert'); */ function BufferReader(data, zeroCopy) { - if (data instanceof BufferReader) - return data; - if (!(this instanceof BufferReader)) return new BufferReader(data, zeroCopy); + assert(Buffer.isBuffer(data), 'Must pass a Buffer.'); + this.data = data; this.offset = 0; - this.zeroCopy = zeroCopy; + this.zeroCopy = zeroCopy || false; this.stack = []; } diff --git a/lib/utils/writer.js b/lib/utils/writer.js index 58d48e80..0a709142 100644 --- a/lib/utils/writer.js +++ b/lib/utils/writer.js @@ -56,12 +56,9 @@ var FILL = 24; * @param {(BufferWriter|Object)?} options */ -function BufferWriter(options) { - if (options instanceof BufferWriter) - return options; - +function BufferWriter() { if (!(this instanceof BufferWriter)) - return new BufferWriter(options); + return new BufferWriter(); this.ops = []; this.written = 0; diff --git a/lib/wallet/account.js b/lib/wallet/account.js index e11c04ff..8bc49e4a 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -910,8 +910,8 @@ Account.prototype.toJSON = function toJSON(minimal) { * @returns {Buffer} */ -Account.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +Account.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i, key; bw.writeVarString(this.name, 'ascii'); @@ -933,10 +933,7 @@ Account.prototype.toRaw = function toRaw(writer) { bw.writeBytes(key.toRaw()); } - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js index cb26ce76..c4434572 100644 --- a/lib/wallet/masterkey.js +++ b/lib/wallet/masterkey.js @@ -445,8 +445,8 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, aes) { * @returns {Buffer} */ -MasterKey.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +MasterKey.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); if (this.encrypted) { bw.writeU8(1); @@ -458,19 +458,13 @@ MasterKey.prototype.toRaw = function toRaw(writer) { bw.writeU32(this.r); bw.writeU32(this.p); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); } bw.writeU8(0); bw.writeVarBytes(this.key.toExtended()); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/wallet/path.js b/lib/wallet/path.js index a31b670d..61adb75a 100644 --- a/lib/wallet/path.js +++ b/lib/wallet/path.js @@ -175,8 +175,8 @@ Path.fromRaw = function fromRaw(data) { * @returns {Buffer} */ -Path.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +Path.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU32(this.account); bw.writeU8(this.keyType); @@ -206,10 +206,7 @@ Path.prototype.toRaw = function toRaw(writer) { bw.write8(this.version); bw.writeU8(this.type); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/wallet/records.js b/lib/wallet/records.js index e6b3f16a..6172411a 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -77,18 +77,15 @@ ChainState.fromRaw = function fromRaw(data) { * @returns {Buffer} */ -ChainState.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +ChainState.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU32(this.startHeight); bw.writeHash(this.startHash); bw.writeU32(this.height); bw.writeU8(this.marked ? 1 : 0); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -202,17 +199,12 @@ BlockMeta.fromRaw = function fromRaw(data) { * @returns {Buffer} */ -BlockMeta.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); - +BlockMeta.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeHash(this.hash); bw.writeU32(this.height); bw.writeU32(this.ts); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -258,7 +250,7 @@ BlockMapRecord.prototype.fromRaw = function fromRaw(data) { for (i = 0; i < count; i++) { hash = br.readHash('hex'); - tx = TXMapRecord.fromRaw(hash, br); + tx = TXMapRecord.fromReader(hash, br); this.txs.push(tx); this.index[tx.hash] = tx; } @@ -283,8 +275,8 @@ BlockMapRecord.fromRaw = function fromRaw(height, data) { * @returns {Buffer} */ -BlockMapRecord.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +BlockMapRecord.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i, tx; bw.writeU32(this.txs.length); @@ -292,13 +284,10 @@ BlockMapRecord.prototype.toRaw = function toRaw(writer) { for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; bw.writeHash(tx.hash); - tx.toRaw(bw); + tx.toWriter(bw); } - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -369,13 +358,25 @@ TXMapRecord.prototype.remove = function remove(wid) { return util.binaryRemove(this.wids, wid, cmp); }; -TXMapRecord.prototype.toRaw = function toRaw(writer) { - return serializeWallets(this.wids, writer); +TXMapRecord.prototype.toWriter = function toWriter(bw) { + return serializeWallets(bw, this.wids); +}; + +TXMapRecord.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +TXMapRecord.prototype.fromReader = function fromReader(br) { + this.wids = parseWallets(br); + return this; }; TXMapRecord.prototype.fromRaw = function fromRaw(data) { - this.wids = parseWallets(data); - return this; + return this.fromReader(new BufferReader(data)); +}; + +TXMapRecord.fromReader = function fromReader(hash, br) { + return new TXMapRecord(hash).fromReader(br); }; TXMapRecord.fromRaw = function fromRaw(hash, data) { @@ -401,13 +402,25 @@ OutpointMapRecord.prototype.remove = function remove(wid) { return util.binaryRemove(this.wids, wid, cmp); }; -OutpointMapRecord.prototype.toRaw = function toRaw(writer) { - return serializeWallets(this.wids, writer); +OutpointMapRecord.prototype.toWriter = function toWriter(bw) { + return serializeWallets(bw, this.wids); +}; + +OutpointMapRecord.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +OutpointMapRecord.prototype.fromReader = function fromReader(br) { + this.wids = parseWallets(br); + return this; }; OutpointMapRecord.prototype.fromRaw = function fromRaw(data) { - this.wids = parseWallets(data); - return this; + return this.fromReader(new BufferReader(data)); +}; + +OutpointMapRecord.fromReader = function fromReader(hash, index, br) { + return new OutpointMapRecord(hash, index).fromReader(br); }; OutpointMapRecord.fromRaw = function fromRaw(hash, index, data) { @@ -432,13 +445,25 @@ PathMapRecord.prototype.remove = function remove(wid) { return util.binaryRemove(this.wids, wid, cmp); }; -PathMapRecord.prototype.toRaw = function toRaw(writer) { - return serializeWallets(this.wids, writer); +PathMapRecord.prototype.toWriter = function toWriter(bw) { + return serializeWallets(bw, this.wids); +}; + +PathMapRecord.prototype.toRaw = function toRaw() { + return this.toWriter(new BufferWriter()).render(); +}; + +PathMapRecord.prototype.fromReader = function fromReader(br) { + this.wids = parseWallets(br); + return this; }; PathMapRecord.prototype.fromRaw = function fromRaw(data) { - this.wids = parseWallets(data); - return this; + return this.fromReader(new BufferReader(data)); +}; + +PathMapRecord.fromReader = function fromReader(hash, br) { + return new PathMapRecord(hash).fromReader(br); }; PathMapRecord.fromRaw = function fromRaw(hash, data) { @@ -457,8 +482,7 @@ function cmpid(a, b) { return a.id - b.id; } -function parseWallets(data) { - var br = new BufferReader(data); +function parseWallets(br) { var count = br.readU32(); var wids = []; var i; @@ -469,8 +493,7 @@ function parseWallets(data) { return wids; } -function serializeWallets(wids, writer) { - var bw = new BufferWriter(writer); +function serializeWallets(bw, wids) { var i, wid; bw.writeU32(wids.length); @@ -480,9 +503,6 @@ function serializeWallets(wids, writer) { bw.writeU32(wid); } - if (!writer) - bw = bw.render(); - return bw; } diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index da3d08c4..16c97252 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -2895,18 +2895,15 @@ TXDBState.prototype.toBalance = function toBalance() { * @returns {Buffer} */ -TXDBState.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +TXDBState.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU64(this.tx); bw.writeU64(this.coin); bw.writeU64(this.unconfirmed); bw.writeU64(this.confirmed); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -2985,8 +2982,8 @@ function Credit(coin, spent) { */ Credit.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); - this.coin.fromRaw(br); + var br = new BufferReader(data); + this.coin.fromReader(br); this.spent = br.readU8() === 1; return this; }; @@ -3006,16 +3003,11 @@ Credit.fromRaw = function fromRaw(data) { * @returns {Buffer} */ -Credit.prototype.toRaw = function toRaw(writer) { - var bw = BufferWriter(writer); - - this.coin.toRaw(bw); +Credit.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); + this.coin.toWriter(bw); bw.writeU8(this.spent ? 1 : 0); - - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** @@ -3377,8 +3369,8 @@ BlockRecord.fromRaw = function fromRaw(data) { * @returns {Buffer} */ -BlockRecord.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +BlockRecord.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); var i; bw.writeHash(this.hash); @@ -3390,10 +3382,7 @@ BlockRecord.prototype.toRaw = function toRaw(writer) { for (i = 0; i < this.hashes.length; i++) bw.writeHash(this.hashes[i]); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index e472bec6..e6be9552 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -2440,8 +2440,8 @@ Wallet.prototype.toJSON = function toJSON(unsafe) { * @returns {Buffer} */ -Wallet.prototype.toRaw = function toRaw(writer) { - var bw = new BufferWriter(writer); +Wallet.prototype.toRaw = function toRaw() { + var bw = new BufferWriter(); bw.writeU32(this.network.magic); bw.writeU32(this.wid); @@ -2453,10 +2453,7 @@ Wallet.prototype.toRaw = function toRaw(writer) { bw.writeU32(this.tokenDepth); bw.writeVarBytes(this.master.toRaw()); - if (!writer) - bw = bw.render(); - - return bw; + return bw.render(); }; /** diff --git a/lib/workers/framer.js b/lib/workers/framer.js index 2aef1903..ad7f41f6 100644 --- a/lib/workers/framer.js +++ b/lib/workers/framer.js @@ -33,7 +33,7 @@ Framer.prototype.packet = function _packet(packet) { bw.writeU8(packet.cmd); bw.seek(4); - packet.toRaw(bw); + packet.toWriter(bw); bw.writeU8(0x0a); diff --git a/lib/workers/packets.js b/lib/workers/packets.js index f2a20074..b69ed3a0 100644 --- a/lib/workers/packets.js +++ b/lib/workers/packets.js @@ -53,7 +53,7 @@ Packet.id = 0; Packet.prototype.cmd = -1; -Packet.prototype.toRaw = function toRaw() { +Packet.prototype.toWriter = function toWriter() { throw new Error('Abstract method.'); }; @@ -71,7 +71,7 @@ util.inherits(EventPacket, Packet); EventPacket.prototype.cmd = packetTypes.EVENT; -EventPacket.prototype.toRaw = function toRaw(bw) { +EventPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarString(JSON.stringify(this.items), 'utf8'); }; @@ -96,7 +96,7 @@ util.inherits(LogPacket, Packet); LogPacket.prototype.cmd = packetTypes.LOG; -LogPacket.prototype.toRaw = function toRaw(bw) { +LogPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarString(this.text, 'utf8'); }; @@ -121,7 +121,7 @@ util.inherits(ErrorPacket, Packet); ErrorPacket.prototype.cmd = packetTypes.ERROR; -ErrorPacket.prototype.toRaw = function toRaw(bw) { +ErrorPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarString(this.error.message + '', 'utf8'); bw.writeVarString(this.error.stack + '', 'utf8'); bw.writeVarString((this.error.type || ''), 'utf8'); @@ -150,7 +150,7 @@ util.inherits(ErrorResultPacket, Packet); ErrorResultPacket.prototype.cmd = packetTypes.ERRORRESULT; -ErrorResultPacket.prototype.toRaw = function toRaw(bw) { +ErrorResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarString(this.error.message + '', 'utf8'); bw.writeVarString(this.error.stack + '', 'utf8'); bw.writeVarString((this.error.type || ''), 'utf8'); @@ -180,16 +180,22 @@ util.inherits(VerifyPacket, Packet); VerifyPacket.prototype.cmd = packetTypes.VERIFY; -VerifyPacket.prototype.toRaw = function(bw) { +VerifyPacket.prototype.toWriter = function(bw) { frameTX(this.tx, bw); - bw.writeU32(this.flags); + bw.write32(this.flags != null ? this.flags : -1); }; VerifyPacket.fromRaw = function fromRaw(TX, data) { var br = new BufferReader(data, true); var packet = new VerifyPacket(); + packet.tx = parseTX(TX, br); - packet.flags = br.readU32(); + + packet.flags = br.read32(); + + if (packet.flags === -1) + packet.flags = null; + return packet; }; @@ -207,7 +213,7 @@ util.inherits(VerifyResultPacket, Packet); VerifyResultPacket.prototype.cmd = packetTypes.VERIFYRESULT; -VerifyResultPacket.prototype.toRaw = function toRaw(bw) { +VerifyResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeU8(this.value ? 1 : 0); }; @@ -234,16 +240,16 @@ util.inherits(SignPacket, Packet); SignPacket.prototype.cmd = packetTypes.SIGN; -SignPacket.prototype.toRaw = function toRaw(bw) { +SignPacket.prototype.toWriter = function toWriter(bw) { var i, ring; frameTX(this.tx, bw); - bw.writeU32(this.rings.length); + bw.writeVarint(this.rings.length); for (i = 0; i < this.rings.length; i++) { ring = this.rings[i]; - bw.writeBytes(ring.toRaw()); + ring.toWriter(bw); } bw.write8(this.type != null ? this.type : -1); @@ -256,10 +262,10 @@ SignPacket.fromRaw = function fromRaw(MTX, KeyRing, data) { packet.tx = parseTX(MTX, br); - count = br.readU32(); + count = br.readVarint(); for (i = 0; i < count; i++) { - ring = KeyRing.fromRaw(br); + ring = KeyRing.fromReader(br); packet.rings.push(ring); } @@ -300,7 +306,7 @@ SignResultPacket.fromTX = function fromTX(tx, total) { return packet; }; -SignResultPacket.prototype.toRaw = function toRaw(bw) { +SignResultPacket.prototype.toWriter = function toWriter(bw) { var i; assert(this.script.length === this.witness.length); @@ -309,8 +315,8 @@ SignResultPacket.prototype.toRaw = function toRaw(bw) { bw.writeVarint(this.script.length); for (i = 0; i < this.script.length; i++) { - this.script[i].toRaw(bw); - this.witness[i].toRaw(bw); + this.script[i].toWriter(bw); + this.witness[i].toWriter(bw); } }; @@ -337,8 +343,8 @@ SignResultPacket.fromRaw = function fromRaw(data) { count = br.readVarint(); for (i = 0; i < count; i++) { - packet.script.push(Script.fromRaw(br)); - packet.witness.push(Witness.fromRaw(br)); + packet.script.push(Script.fromReader(br)); + packet.witness.push(Witness.fromReader(br)); } return packet; @@ -360,9 +366,9 @@ util.inherits(VerifyInputPacket, Packet); VerifyInputPacket.prototype.cmd = packetTypes.VERIFYINPUT; -VerifyInputPacket.prototype.toRaw = function toRaw(bw) { +VerifyInputPacket.prototype.toWriter = function toWriter(bw) { frameTX(this.tx, bw); - bw.writeU32(this.index); + bw.writeVarint(this.index); bw.write32(this.flags != null ? this.flags : -1); }; @@ -371,7 +377,7 @@ VerifyInputPacket.fromRaw = function fromRaw(TX, data) { var packet = new VerifyInputPacket(); packet.tx = parseTX(TX, br); - packet.index = br.readU32(); + packet.index = br.readVarint(); packet.flags = br.read32(); if (packet.flags === -1) @@ -394,7 +400,7 @@ util.inherits(VerifyInputResultPacket, Packet); VerifyInputResultPacket.prototype.cmd = packetTypes.VERIFYINPUTRESULT; -VerifyInputResultPacket.prototype.toRaw = function toRaw(bw) { +VerifyInputResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeU8(this.value ? 1 : 0); }; @@ -410,11 +416,11 @@ VerifyInputResultPacket.fromRaw = function fromRaw(data) { * @constructor */ -function SignInputPacket(tx, index, rings, type) { +function SignInputPacket(tx, index, ring, type) { Packet.call(this); this.tx = tx || null; this.index = index; - this.rings = rings || []; + this.ring = ring || null; this.type = type != null ? type : null; } @@ -422,18 +428,19 @@ util.inherits(SignInputPacket, Packet); SignInputPacket.prototype.cmd = packetTypes.SIGNINPUT; -SignInputPacket.prototype.toRaw = function toRaw(bw) { - var i, ring; +SignInputPacket.prototype.toWriter = function toWriter(bw) { + var input = this.tx.inputs[this.index]; - frameTX(this.tx, bw); - bw.writeU32(this.index); + assert(input); + assert(input.coin); - bw.writeU32(this.rings.length); + this.tx.toWriter(bw); + bw.writeVarint(this.index); - for (i = 0; i < this.rings.length; i++) { - ring = this.rings[i]; - bw.writeBytes(ring.toRaw()); - } + bw.writeVarint(input.coin.value); + input.coin.script.toWriter(bw); + + this.ring.toWriter(bw); bw.write8(this.type != null ? this.type : -1); }; @@ -441,27 +448,33 @@ SignInputPacket.prototype.toRaw = function toRaw(bw) { SignInputPacket.fromRaw = function fromRaw(MTX, KeyRing, data) { var br = new BufferReader(data, true); var packet = new SignInputPacket(); - var i, count, ring; + var coin = new Coin(); + var input; packet.tx = parseTX(MTX, br); - packet.index = br.readU32(); + packet.index = br.readVarint(); - count = br.readU32(); + coin.value = br.readVarint(); + coin.script.fromReader(br); - for (i = 0; i < count; i++) { - ring = KeyRing.fromRaw(br); - packet.rings.push(ring); - } + packet.ring = KeyRing.fromReader(br); packet.type = br.read8(); if (packet.type === -1) packet.type = null; + input = packet.tx.inputs[packet.index]; + assert(input); + + coin.hash = input.prevout.hash; + coin.index = input.prevout.index; + + input.coin = coin; + return packet; }; - /** * SignInputResultPacket * @constructor @@ -490,10 +503,10 @@ SignInputResultPacket.fromTX = function fromTX(tx, i, value) { return packet; }; -SignInputResultPacket.prototype.toRaw = function toRaw(bw) { +SignInputResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeU8(this.value ? 1 : 0); - this.script.toRaw(bw); - this.witness.toRaw(bw); + this.script.toWriter(bw); + this.witness.toWriter(bw); }; SignInputResultPacket.prototype.inject = function inject(tx, i) { @@ -507,8 +520,8 @@ SignInputResultPacket.fromRaw = function fromRaw(data) { var br = new BufferReader(data, true); var packet = new SignInputResultPacket(); packet.value = br.readU8() === 1; - packet.script = Script.fromRaw(br); - packet.witness = Witness.fromRaw(br); + packet.script = Script.fromReader(br); + packet.witness = Witness.fromReader(br); return packet; }; @@ -529,7 +542,7 @@ util.inherits(ECVerifyPacket, Packet); ECVerifyPacket.prototype.cmd = packetTypes.ECVERIFY; -ECVerifyPacket.prototype.toRaw = function(bw) { +ECVerifyPacket.prototype.toWriter = function(bw) { bw.writeVarBytes(this.msg); bw.writeVarBytes(this.sig); bw.writeVarBytes(this.key); @@ -558,7 +571,7 @@ util.inherits(ECVerifyResultPacket, Packet); ECVerifyResultPacket.prototype.cmd = packetTypes.ECVERIFYRESULT; -ECVerifyResultPacket.prototype.toRaw = function toRaw(bw) { +ECVerifyResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeU8(this.value ? 1 : 0); }; @@ -584,7 +597,7 @@ util.inherits(ECSignPacket, Packet); ECSignPacket.prototype.cmd = packetTypes.ECSIGN; -ECSignPacket.prototype.toRaw = function(bw) { +ECSignPacket.prototype.toWriter = function(bw) { bw.writeVarBytes(this.msg); bw.writeVarBytes(this.key); }; @@ -611,7 +624,7 @@ util.inherits(ECSignResultPacket, Packet); ECSignResultPacket.prototype.cmd = packetTypes.ECSIGNRESULT; -ECSignResultPacket.prototype.toRaw = function toRaw(bw) { +ECSignResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarBytes(this.sig); }; @@ -639,7 +652,7 @@ util.inherits(MinePacket, Packet); MinePacket.prototype.cmd = packetTypes.MINE; -MinePacket.prototype.toRaw = function(bw) { +MinePacket.prototype.toWriter = function(bw) { bw.writeBytes(this.data); bw.writeBytes(this.target); bw.writeU32(this.min); @@ -670,7 +683,7 @@ util.inherits(MineResultPacket, Packet); MineResultPacket.prototype.cmd = packetTypes.MINERESULT; -MineResultPacket.prototype.toRaw = function toRaw(bw) { +MineResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeU32(this.nonce); }; @@ -702,7 +715,7 @@ util.inherits(ScryptPacket, Packet); ScryptPacket.prototype.cmd = packetTypes.SCRYPT; -ScryptPacket.prototype.toRaw = function(bw) { +ScryptPacket.prototype.toWriter = function(bw) { bw.writeVarBytes(this.passwd); bw.writeVarBytes(this.salt); bw.writeU32(this.N); @@ -737,7 +750,7 @@ util.inherits(ScryptResultPacket, Packet); ScryptResultPacket.prototype.cmd = packetTypes.SCRYPTRESULT; -ScryptResultPacket.prototype.toRaw = function toRaw(bw) { +ScryptResultPacket.prototype.toWriter = function toWriter(bw) { bw.writeVarBytes(this.key); }; @@ -755,7 +768,7 @@ ScryptResultPacket.fromRaw = function fromRaw(data) { function frameTX(tx, bw) { var i, input; - tx.toRaw(bw); + tx.toWriter(bw); for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -767,12 +780,12 @@ function frameTX(tx, bw) { bw.writeU8(1); bw.writeVarint(input.coin.value); - input.coin.script.toRaw(bw); + input.coin.script.toWriter(bw); } } function parseTX(TX, br) { - var tx = TX.fromRaw(br); + var tx = TX.fromReader(br); var i, input, prevout, coin; for (i = 0; i < tx.inputs.length; i++) { @@ -784,7 +797,7 @@ function parseTX(TX, br) { coin = new Coin(); coin.value = br.readVarint(); - coin.script.fromRaw(br); + coin.script.fromReader(br); coin.hash = prevout.hash; coin.index = prevout.index; diff --git a/lib/workers/workerpool.js b/lib/workers/workerpool.js index 2ccf512d..6faaccec 100644 --- a/lib/workers/workerpool.js +++ b/lib/workers/workerpool.js @@ -317,17 +317,9 @@ WorkerPool.prototype.verifyInput = co(function* verifyInput(tx, index, flags) { */ WorkerPool.prototype.signInput = co(function* signInput(tx, index, ring, type) { - var rings = ring; - var packet, result; - - if (!Array.isArray(rings)) - rings = [rings]; - - packet = new packets.SignInputPacket(tx, index, rings, type); - result = yield this.execute(packet, -1); - + var packet = new packets.SignInputPacket(tx, index, ring, type); + var result = yield this.execute(packet, -1); result.inject(tx); - return result.value; }); diff --git a/migrate/chaindb1to2.js b/migrate/chaindb1to2.js index 356bb5e5..db218763 100644 --- a/migrate/chaindb1to2.js +++ b/migrate/chaindb1to2.js @@ -188,7 +188,7 @@ var reserializeUndo = co(function* reserializeUndo() { while (br.left()) { undo.push(null); - injectCoin(undo.top(), Coin.fromRaw(br)); + injectCoin(undo.top(), Coin.fromReader(br)); } batch.write(item.key, undo.toRaw()); diff --git a/migrate/walletdb5to6.js b/migrate/walletdb5to6.js index e5d1f54e..bc5ad879 100644 --- a/migrate/walletdb5to6.js +++ b/migrate/walletdb5to6.js @@ -139,8 +139,8 @@ function parseWallets(data) { return wids; } -function serializeWallets(wids, writer) { - var p = new BufferWriter(writer); +function serializeWallets(wids) { + var p = new BufferWriter(); var i, wid; p.writeU32(wids.length); @@ -150,10 +150,7 @@ function serializeWallets(wids, writer) { p.writeU32(wid); } - if (!writer) - p = p.render(); - - return p; + return p.render(); } function accountToRaw(account) { diff --git a/test/protocol-test.js b/test/protocol-test.js index 8a828bbf..1db1fe2a 100644 --- a/test/protocol-test.js +++ b/test/protocol-test.js @@ -207,10 +207,9 @@ describe('Protocol', function() { }); it('should parse, reserialize, and verify alert packets', function() { - var p = new bcoin.reader(alertData); - p.start(); - while (p.left()) { - var alert = packets.AlertPacket.fromRaw(p); + var br = new bcoin.reader(alertData); + while (br.left()) { + var alert = packets.AlertPacket.fromReader(br); assert(alert.verify(network.alertKey)); alert._payload = null; alert._hash = null; @@ -218,6 +217,5 @@ describe('Protocol', function() { alert = packets.AlertPacket.fromRaw(data); assert(alert.verify(network.alertKey)); } - p.end(); }); }); diff --git a/test/script-test.js b/test/script-test.js index 8711ef3d..b1fec413 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -19,22 +19,22 @@ describe('Script', function() { + '101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f' + 'ac'; - var decoded = bcoin.script.decode(new Buffer(src, 'hex')); - assert.equal(decoded.length, 3); - assert.equal(decoded[0].data.toString('hex'), + var decoded = bcoin.script(new Buffer(src, 'hex')); + assert.equal(decoded.code.length, 3); + assert.equal(decoded.code[0].data.toString('hex'), '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'); - assert.equal(decoded[1].data.toString('hex'), + assert.equal(decoded.code[1].data.toString('hex'), '101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f'); - assert.equal(decoded[2].value, opcodes.OP_CHECKSIG); + assert.equal(decoded.code[2].value, opcodes.OP_CHECKSIG); - var dst = bcoin.script.encode(decoded); + var dst = decoded.toRaw(); assert.equal(dst.toString('hex'), src); }); it('should encode/decode numbers', function() { var script = [0, 0x51, 0x52, 0x60]; var encoded = bcoin.script.fromArray(script).raw; - var decoded = bcoin.script.decode(encoded).map(function(op) { return op.value; }); + var decoded = bcoin.script(encoded).toArray(); assert.deepEqual(decoded, script); });