diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 8cbcd202..e7db629e 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -11,8 +11,8 @@ var AsyncObject = require('../utils/async'); var constants = require('../protocol/constants'); var util = require('../utils/util'); var assert = require('assert'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); var Amount = require('../btc/amount'); var encoding = require('../utils/encoding'); var co = require('../utils/co'); @@ -601,7 +601,7 @@ ChainDB.prototype.saveDeployments = function saveDeployments() { */ ChainDB.prototype.writeDeployments = function writeDeployments(batch) { - var bw = new BufferWriter(); + var bw = new StaticWriter(1 + 9 * this.network.deploys.length); var i, deployment; bw.writeU8(this.network.deploys.length); @@ -2010,7 +2010,7 @@ ChainOptions.prototype.verify = function verify(options) { }; ChainOptions.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(12); var flags = 0; if (this.spv) @@ -2113,7 +2113,7 @@ ChainState.prototype.commit = function commit(hash) { }; ChainState.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(56); bw.writeHash(this.tip); bw.writeU64(this.tx); bw.writeU64(this.coin); diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index f374bbff..ec68a9be 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -14,8 +14,8 @@ var util = require('../utils/util'); var btcutils = require('../btc/utils'); var crypto = require('../crypto/crypto'); var assert = require('assert'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); var Headers = require('../primitives/headers'); var InvItem = require('../primitives/invitem'); var co = require('../utils/co'); @@ -430,7 +430,7 @@ ChainEntry.fromBlock = function fromBlock(chain, block, prev) { */ ChainEntry.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(116); bw.writeU32(this.version); bw.writeHash(this.prevBlock); diff --git a/lib/env.js b/lib/env.js index feba87e7..6257c7f7 100644 --- a/lib/env.js +++ b/lib/env.js @@ -215,6 +215,7 @@ function Environment() { this.require('co', './utils/co'); this.require('encoding', './utils/encoding'); this.require('reader', './utils/reader'); + this.require('staticwriter', './utils/staticwriter'); this.require('util', './utils/util'); this.require('writer', './utils/writer'); diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index 6f9629a3..af9f331b 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -10,8 +10,9 @@ var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var assert = require('assert'); var constants = require('../protocol/constants'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); +var encoding = require('../utils/encoding'); var wordlist = require('./wordlist'); var nfkd = require('../utils/nfkd'); @@ -413,6 +414,30 @@ Mnemonic.fromJSON = function fromJSON(json) { return new Mnemonic().fromJSON(json); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +Mnemonic.prototype.getSize = function getSize() { + var size = 0; + var len; + + size += 2; + size += 1; + size += this.getEntropy().length; + + len = Buffer.byteLength(this.getPhrase(), 'utf8'); + size += encoding.sizeVarint(len); + size += len; + + len = Buffer.byteLength(this.passphrase, 'utf8'); + size += encoding.sizeVarint(len); + size += len; + + return size; +}; + /** * Write the mnemonic to a buffer writer. * @params {BufferWriter} bw @@ -438,7 +463,8 @@ Mnemonic.prototype.toWriter = function toWriter(bw) { */ Mnemonic.prototype.toRaw = function toRaw(writer) { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; /** diff --git a/lib/hd/private.js b/lib/hd/private.js index f66c783d..1dc7d27f 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -13,8 +13,9 @@ var assert = require('assert'); var constants = require('../protocol/constants'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); var Mnemonic = require('./mnemonic'); var HDPublicKey = require('./public'); @@ -246,7 +247,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { return child; } - bw = new BufferWriter(); + bw = new StaticWriter(37); if (hardened) { bw.writeU8(0); @@ -682,6 +683,15 @@ HDPrivateKey.prototype.toBase58 = function toBase58(network) { return base58.encode(this.toRaw(network)); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +HDPrivateKey.prototype.getSize = function getSize() { + return 82; +}; + /** * Write the key to a buffer writer. * @param {BufferWriter} bw @@ -713,7 +723,23 @@ HDPrivateKey.prototype.toWriter = function toWriter(bw, network) { */ HDPrivateKey.prototype.toRaw = function toRaw(network) { - return this.toWriter(new BufferWriter(), network).render(); + return this.toWriter(new StaticWriter(82), network).render(); +}; + +/** + * Calculate extended serialization size. + * @returns {Number} + */ + +HDPrivateKey.prototype.getExtendedSize = function getSize() { + var size = this.getSize(); + + size += 1; + + if (this.mnemonic) + size += this.mnemonic.getSize(); + + return size; }; /** @@ -744,7 +770,8 @@ HDPrivateKey.prototype.toExtendedWriter = function toExtendedWriter(bw, network) */ HDPrivateKey.prototype.toExtended = function toExtended(network) { - return this.toExtendedWriter(new BufferWriter(), network).render(); + var size = this.getExtendedSize(); + return this.toExtendedWriter(new StaticWriter(size), network).render(); }; /** diff --git a/lib/hd/public.js b/lib/hd/public.js index e276f4aa..22590e2f 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -13,7 +13,7 @@ var assert = require('assert'); var constants = require('../protocol/constants'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); var base58 = require('../utils/base58'); var common = require('./common'); @@ -203,7 +203,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) { return child; } - bw = new BufferWriter(); + bw = new StaticWriter(37); bw.writeBytes(this.publicKey); bw.writeU32BE(index); data = bw.render(); @@ -565,6 +565,15 @@ HDPublicKey.prototype.toWriter = function toWriter(bw, network) { return bw; }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +HDPublicKey.prototype.getSize = function getSize() { + return 82; +}; + /** * Serialize the key. * @param {Network|NetworkType} network @@ -572,7 +581,7 @@ HDPublicKey.prototype.toWriter = function toWriter(bw, network) { */ HDPublicKey.prototype.toRaw = function toRaw(network) { - return this.toWriter(new BufferWriter(), network).render(); + return this.toWriter(new StaticWriter(82), network).render(); }; /** diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index 26df3704..323c5d2a 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -12,7 +12,8 @@ var util = require('../utils/util'); var assert = require('assert'); var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var Logger = require('../node/logger'); var Network = require('../protocol/network'); var global = util.global; @@ -315,32 +316,46 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc } }; +/** + * Get serialization size. + * @returns {Number} + */ + +ConfirmStats.prototype.getSize = function getSize() { + var size = 0; + var i; + + size += 8; + + size += sizeArray(this.buckets); + size += sizeArray(this.avg); + size += sizeArray(this.txAvg); + + size += encoding.sizeVarint(this.maxConfirms); + for (i = 0; i < this.maxConfirms; i++) + size += sizeArray(this.confAvg[i]); + + return size; +}; + /** * Serialize confirm stats. * @returns {Buffer} */ ConfirmStats.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; - function writeArray(buckets) { - var i; - - bw.writeVarint(buckets.length); - - for (i = 0; i < buckets.length; i++) - bw.writeDouble(buckets[i]); - } - bw.writeDouble(this.decay); - writeArray(this.buckets); - writeArray(this.avg); - writeArray(this.txAvg); + writeArray(bw, this.buckets); + writeArray(bw, this.avg); + writeArray(bw, this.txAvg); bw.writeVarint(this.maxConfirms); for (i = 0; i < this.maxConfirms; i++) - writeArray(this.confAvg[i]); + writeArray(bw, this.confAvg[i]); return bw.render(); }; @@ -356,25 +371,15 @@ ConfirmStats.fromRaw = function fromRaw(data, type, logger) { var br = new BufferReader(data); var i, decay, buckets, avg, txAvg, maxConfirms, confAvg, stats; - function readArray() { - var buckets = new Float64Array(br.readVarint()); - var i; - - for (i = 0; i < buckets.length; i++) - buckets[i] = br.readDouble(); - - return buckets; - } - decay = br.readDouble(); - buckets = readArray(); - avg = readArray(); - txAvg = readArray(); + buckets = readArray(br); + avg = readArray(br); + txAvg = readArray(br); maxConfirms = br.readVarint(); confAvg = new Array(maxConfirms); for (i = 0; i < maxConfirms; i++) - confAvg[i] = readArray(); + confAvg[i] = readArray(br); if (decay <= 0 || decay >= 1) throw new Error('Decay must be between 0 and 1 (non-inclusive).'); @@ -797,13 +802,36 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s return Math.floor(priority); }; +/** + * Get serialization size. + * @returns {Number} + */ + +PolicyEstimator.prototype.getSize = function getSize() { + var size = 0; + var len; + + size += 8; + + len = this.feeStats.getSize(); + size += encoding.sizeVarint(len); + size += len; + + len = this.priStats.getSize(); + size += encoding.sizeVarint(len); + size += len; + + return size; +}; + /** * Serialize the estimator. * @returns {Buffer} */ PolicyEstimator.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.writeU32(this.network.magic); bw.writeU32(this.bestHeight); @@ -877,6 +905,30 @@ function compare(a, b) { return a[0] - b; } +function sizeArray(buckets) { + var size = encoding.sizeVarint(buckets.length); + return size + buckets.length * 8; +} + +function writeArray(bw, buckets) { + var i; + + bw.writeVarint(buckets.length); + + for (i = 0; i < buckets.length; i++) + bw.writeDouble(buckets[i]); +} + +function readArray(br) { + var buckets = new Float64Array(br.readVarint()); + var i; + + for (i = 0; i < buckets.length; i++) + buckets[i] = br.readDouble(); + + return buckets; +} + /* * Expose */ diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 978348dd..1a8c05f8 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -16,7 +16,7 @@ var packets = require('./packets'); var assert = require('assert'); var constants = require('../protocol/constants'); var ec = require('../crypto/ec'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); /** @@ -282,7 +282,7 @@ BIP150.prototype.getAddress = function getAddress() { }; BIP150.address = function address(key) { - var bw = new BufferWriter(); + var bw = new StaticWriter(27); bw.writeU8(0x0f); bw.writeU16BE(0xff01); bw.writeBytes(crypto.hash160(key)); diff --git a/lib/net/bip151.js b/lib/net/bip151.js index 37743fd6..ea6a1f65 100644 --- a/lib/net/bip151.js +++ b/lib/net/bip151.js @@ -21,8 +21,9 @@ var constants = require('../protocol/constants'); var chachapoly = require('../crypto/chachapoly'); var packets = require('./packets'); var ec = require('../crypto/ec'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); +var encoding = require('../utils/encoding'); var EncinitPacket = packets.EncinitPacket; var EncackPacket = packets.EncackPacket; @@ -89,7 +90,7 @@ function BIP151Stream(cipher) { */ BIP151Stream.prototype.init = function init(publicKey) { - var bw = new BufferWriter(); + var bw = new StaticWriter(33); this.publicKey = publicKey; this.secret = ec.ecdh(this.publicKey, this.privateKey); @@ -517,6 +518,24 @@ BIP151.prototype.maybeRekey = function maybeRekey(data) { this.output.rekey(); }; +/** + * Calculate packet size. + * @param {String} cmd + * @param {Buffer} body + * @returns {Number} + */ + +BIP151.prototype.packetSize = function packetSize(cmd, body) { + var size = 0; + size += 4; + size += encoding.sizeVarint(cmd.length); + size += cmd.length; + size += 4; + size += body.length; + size += 16; + return size; +}; + /** * Frame plaintext payload for the output stream. * @param {String} cmd @@ -525,21 +544,23 @@ BIP151.prototype.maybeRekey = function maybeRekey(data) { */ BIP151.prototype.packet = function packet(cmd, body) { - var bw = new BufferWriter(); - var payload, packet; + var size = this.packetSize(cmd, body); + var bw = new StaticWriter(size); + var packet, payload; + bw.seek(4); bw.writeVarString(cmd, 'ascii'); bw.writeU32(body.length); bw.writeBytes(body); + bw.seek(16); - payload = bw.render(); - - packet = new Buffer(4 + payload.length + 16); + packet = bw.render(); + payload = packet.slice(4, -16); this.maybeRekey(packet); this.output.encryptSize(payload.length).copy(packet, 0); - this.output.encrypt(payload).copy(packet, 4); + this.output.encrypt(payload); this.output.finish().copy(packet, 4 + payload.length); this.output.sequence(); diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 5bed4ed5..d0d0ced0 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -9,6 +9,8 @@ var util = require('../utils/util'); var BufferReader = require('../utils/reader'); var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var assert = require('assert'); @@ -148,7 +150,30 @@ CompactBlock.prototype.toNormalWriter = function toNormalWriter(bw) { }; CompactBlock.prototype.frame = function frame(witness) { - return this.frameWriter(new BufferWriter(), witness).render(); + var size = this.getSize(); + return this.frameWriter(new StaticWriter(size), witness).render(); +}; + +CompactBlock.prototype.getSize = function getSize(witness) { + var size = 0; + var i, ptx; + + size += 80; + size += 8; + size += encoding.sizeVarint(this.ids.length); + size += this.ids.length * 6; + size += encoding.sizeVarint(this.ptx.length); + + for (i = 0; i < this.ptx.length; i++) { + ptx = this.ptx[i]; + size += encoding.sizeVarint(ptx.index); + if (witness) + size += ptx.tx.getSize(); + else + size += ptx.tx.getBaseSize(); + } + + return size; }; CompactBlock.prototype.frameWriter = function frameWriter(bw, witness) { @@ -611,6 +636,24 @@ TXResponse.prototype.toNormalWriter = function toNormalWriter(bw) { return this.frameWriter(bw, false); }; +TXResponse.prototype.getSize = function getSize(witness) { + var size = 0; + var i, tx; + + size += 32; + size += encoding.sizeVarint(this.txs.length); + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + if (witness) + size += tx.getSize(); + else + size += tx.getBaseSize(); + } + + return size; +}; + TXResponse.prototype.frameWriter = function frameWriter(bw, witness) { var i, tx; @@ -630,7 +673,8 @@ TXResponse.prototype.frameWriter = function frameWriter(bw, witness) { }; TXResponse.prototype.frame = function frame(witness) { - return this.frameWriter(new BufferWriter(), witness).render(); + var size = this.getSize(); + return this.frameWriter(new StaticWriter(size), witness).render(); }; /* diff --git a/lib/net/packets.js b/lib/net/packets.js index ded300cb..5a494bef 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -23,8 +23,9 @@ var MerkleBlock = require('../primitives/merkleblock'); var Outpoint = require('../primitives/outpoint'); var Output = require('../primitives/output'); var TX = require('../primitives/tx'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var DUMMY = new Buffer(0); /** @@ -181,13 +182,37 @@ VersionPacket.fromOptions = function fromOptions(options) { return new VersionPacket().fromOptions(options); }; +/** + * Get serialization size. + * @returns {Number} + */ + +VersionPacket.prototype.getSize = function getSize() { + var size = 0; + var len; + + size += 20; + size += this.recv.getSize(false); + size += this.from.getSize(false); + size += 8; + + len = Buffer.byteLength(this.agent, 'ascii'); + size += encoding.sizeVarint(len); + size += len; + + size += 5; + + return size; +}; + /** * Serialize version packet. * @returns {Buffer} */ VersionPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.write32(this.version); bw.writeU64(this.services); @@ -264,7 +289,7 @@ VersionPacket.prototype.hasCompact = function hasCompact() { */ VersionPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.version = br.read32(); this.services = br.readU53(); @@ -324,6 +349,15 @@ function VerackPacket() { VerackPacket.prototype.cmd = 'verack'; VerackPacket.prototype.type = exports.types.VERACK; +/** + * Get serialization size. + * @returns {Number} + */ + +VerackPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize verack packet. * @returns {Buffer} @@ -378,13 +412,23 @@ util.inherits(PingPacket, Packet); PingPacket.prototype.cmd = 'ping'; PingPacket.prototype.type = exports.types.PING; +/** + * Get serialization size. + * @returns {Number} + */ + +PingPacket.prototype.getSize = function getSize() { + return this.nonce ? 8 : 0; +}; + /** * Serialize ping packet. * @returns {Buffer} */ PingPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); if (this.nonce) bw.writeBytes(this.nonce); @@ -399,7 +443,7 @@ PingPacket.prototype.toRaw = function toRaw() { */ PingPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); if (br.left() >= 8) this.nonce = br.readBytes(8); return this; @@ -440,13 +484,22 @@ util.inherits(PongPacket, Packet); PongPacket.prototype.cmd = 'pong'; PongPacket.prototype.type = exports.types.PONG; +/** + * Get serialization size. + * @returns {Number} + */ + +PongPacket.prototype.getSize = function getSize() { + return 8; +}; + /** * Serialize pong packet. * @returns {Buffer} */ PongPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(8); bw.writeBytes(this.nonce); return bw.render(); }; @@ -458,7 +511,7 @@ PongPacket.prototype.toRaw = function toRaw() { */ PongPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.nonce = br.readBytes(8); return this; }; @@ -638,6 +691,25 @@ AlertPacket.prototype.verify = function verify(key) { return ec.verify(this.hash(), this.signature, key); }; +/** + * Write the alert packet to a buffer writer. + * @param {BufferWriter} bw + */ + +AlertPacket.prototype.getSize = function getSize() { + var size = 0; + var len; + + len = this.getPayloadSize(); + size += encoding.sizeVarint(len); + size += len; + + size += encoding.sizeVarint(this.signature.length); + size += this.signature.length; + + return size; +}; + /** * Write the alert packet to a buffer writer. * @param {BufferWriter} bw @@ -656,7 +728,40 @@ AlertPacket.prototype.toWriter = function toWriter(bw) { */ AlertPacket.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); +}; + +/** + * Get serialization size. + * @returns {Number} + */ + +AlertPacket.prototype.getPayloadSize = function getPayloadSize() { + var size = 0; + var i; + + size += 28; + size += encoding.sizeVarint(this.cancels.length); + size += this.cancels.length * 4; + size += 8; + + size += encoding.sizeVarint(this.subVers.length); + for (i = 0; i < this.subVers.length; i++) { + size += encoding.sizeVarint(this.subVers[i].length); + size += this.subVers[i].length; + } + + size += 4; + + size += encoding.sizeVarint(this.comment.length); + size += this.comment.length; + size += encoding.sizeVarint(this.statusBar.length); + size += this.statusBar.length; + size += encoding.sizeVarint(this.reserved.length); + size += this.reserved.length; + + return size; }; /** @@ -666,7 +771,8 @@ AlertPacket.prototype.toRaw = function toRaw() { */ AlertPacket.prototype.framePayload = function framePayload() { - var bw = new BufferWriter(); + var size = this.getPayloadSize(); + var bw = new StaticWriter(size); var i; bw.write32(this.version); @@ -784,6 +890,15 @@ util.inherits(GetAddrPacket, Packet); GetAddrPacket.prototype.cmd = 'getaddr'; GetAddrPacket.prototype.type = exports.types.GETADDR; +/** + * Get serialization size. + * @returns {Number} + */ + +GetAddrPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize getaddr packet. * @returns {Buffer} @@ -838,13 +953,26 @@ util.inherits(AddrPacket, Packet); AddrPacket.prototype.cmd = 'addr'; AddrPacket.prototype.type = exports.types.ADDR; +/** + * Get serialization size. + * @returns {Number} + */ + +AddrPacket.prototype.getSize = function getSize() { + var size = 0; + size += encoding.sizeVarint(this.items.length); + size += 30 * this.items.length; + return size; +}; + /** * Serialize addr packet. * @returns {Buffer} */ AddrPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeVarint(this.items.length); @@ -862,7 +990,7 @@ AddrPacket.prototype.toRaw = function toRaw() { */ AddrPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, count; count = br.readVarint(); @@ -908,13 +1036,26 @@ util.inherits(InvPacket, Packet); InvPacket.prototype.cmd = 'inv'; InvPacket.prototype.type = exports.types.INV; +/** + * Get serialization size. + * @returns {Number} + */ + +InvPacket.prototype.getSize = function getSize() { + var size = 0; + size += encoding.sizeVarint(this.items.length); + size += 36 * this.items.length; + return size; +}; + /** * Serialize inv packet. * @returns {Buffer} */ InvPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeVarint(this.items.length); @@ -1048,13 +1189,28 @@ util.inherits(GetBlocksPacket, Packet); GetBlocksPacket.prototype.cmd = 'getblocks'; GetBlocksPacket.prototype.type = exports.types.GETBLOCKS; +/** + * Get serialization size. + * @returns {Number} + */ + +GetBlocksPacket.prototype.getSize = function getSize() { + var size = 0; + size += 4; + size += encoding.sizeVarint(this.locator.length); + size += 32 * this.locator.length; + size += 32; + return size; +}; + /** * Serialize getblocks packet. * @returns {Buffer} */ GetBlocksPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeU32(this.version); @@ -1075,7 +1231,7 @@ GetBlocksPacket.prototype.toRaw = function toRaw() { */ GetBlocksPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, count; this.version = br.readU32(); @@ -1162,13 +1318,33 @@ util.inherits(HeadersPacket, Packet); HeadersPacket.prototype.cmd = 'headers'; HeadersPacket.prototype.type = exports.types.HEADERS; +/** + * Get serialization size. + * @returns {Number} + */ + +HeadersPacket.prototype.getSize = function getSize() { + var size = 0; + var i, item; + + size += encoding.sizeVarint(this.items.length); + + for (i = 0; i < this.items.length; i++) { + item = this.items[i]; + size += item.getSize(); + } + + return size; +}; + /** * Serialize headers packet. * @returns {Buffer} */ HeadersPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeVarint(this.items.length); @@ -1186,7 +1362,7 @@ HeadersPacket.prototype.toRaw = function toRaw() { */ HeadersPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, count; count = br.readVarint(); @@ -1228,6 +1404,15 @@ util.inherits(SendHeadersPacket, Packet); SendHeadersPacket.prototype.cmd = 'sendheaders'; SendHeadersPacket.prototype.type = exports.types.SENDHEADERS; +/** + * Get serialization size. + * @returns {Number} + */ + +SendHeadersPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize sendheaders packet. * @returns {Buffer} @@ -1452,13 +1637,34 @@ RejectPacket.fromOptions = function fromOptions(options) { return new RejectPacket().fromOptions(options); }; +/** + * Get serialization size. + * @returns {Number} + */ + +RejectPacket.prototype.getSize = function getSize() { + var size = 0; + + size += encoding.sizeVarint(this.message.length); + size += this.message.length; + size += 1; + size += encoding.sizeVarint(this.reason.length); + size += this.reason.length; + + if (this.data) + size += 32; + + return size; +}; + /** * Serialize reject packet. * @returns {Buffer} */ RejectPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); assert(this.message.length <= 12); assert(this.reason.length <= 111); @@ -1480,7 +1686,7 @@ RejectPacket.prototype.toRaw = function toRaw() { */ RejectPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.message = br.readVarString('ascii', 12); this.code = br.readU8(); @@ -1592,6 +1798,15 @@ util.inherits(MempoolPacket, Packet); MempoolPacket.prototype.cmd = 'mempool'; MempoolPacket.prototype.type = exports.types.MEMPOOL; +/** + * Get serialization size. + * @returns {Number} + */ + +MempoolPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize mempool packet. * @returns {Buffer} @@ -1715,13 +1930,23 @@ util.inherits(FilterAddPacket, Packet); FilterAddPacket.prototype.cmd = 'filteradd'; FilterAddPacket.prototype.type = exports.types.FILTERADD; +/** + * Get serialization size. + * @returns {Number} + */ + +FilterAddPacket.prototype.getSize = function getSize() { + return encoding.sizeVarint(this.data.length) + this.data.length; +}; + /** * Serialize filteradd packet. * @returns {Buffer} */ FilterAddPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.writeVarBytes(this.data); return bw.render(); }; @@ -1733,7 +1958,7 @@ FilterAddPacket.prototype.toRaw = function toRaw() { */ FilterAddPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.data = br.readVarBytes(); return this; }; @@ -1769,6 +1994,15 @@ util.inherits(FilterClearPacket, Packet); FilterClearPacket.prototype.cmd = 'filterclear'; FilterClearPacket.prototype.type = exports.types.FILTERCLEAR; +/** + * Get serialization size. + * @returns {Number} + */ + +FilterClearPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize filterclear packet. * @returns {Buffer} @@ -1881,13 +2115,27 @@ util.inherits(GetUTXOsPacket, Packet); GetUTXOsPacket.prototype.cmd = 'getutxos'; GetUTXOsPacket.prototype.type = exports.types.GETUTXOS; +/** + * Get serialization size. + * @returns {Number} + */ + +GetUTXOsPacket.prototype.getSize = function getSize() { + var size = 0; + size += 1; + size += encoding.sizeVarint(this.prevout.length); + size += this.prevout.length * 36; + return size; +}; + /** * Serialize getutxos packet. * @returns {Buffer} */ GetUTXOsPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeU8(this.mempool ? 1 : 0); @@ -1906,7 +2154,7 @@ GetUTXOsPacket.prototype.toRaw = function toRaw() { */ GetUTXOsPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, count; this.mempool = br.readU8() === 1; @@ -2004,13 +2252,38 @@ UTXOsPacket.fromOptions = function fromOptions(options) { return new UTXOsPacket().fromOptions(options); }; +/** + * Get serialization size. + * @returns {Number} + */ + +UTXOsPacket.prototype.getSize = function getSize() { + var size = 0; + var i, len; + + size += 4; + size += 32; + + len = (this.hits.length + 7) / 8 | 0; + size += encoding.sizeVarint(len); + size += len; + + size += encoding.sizeVarint(this.coins.length); + + for (i = 0; i < this.coins.length; i++) + size += 12 * this.coins[i].script.getVarSize(); + + return size; +}; + /** * Serialize utxos packet. * @returns {Buffer} */ UTXOsPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var map = new Buffer((this.hits.length + 7) / 8 | 0); var i, bit, oct, coin, height; @@ -2048,7 +2321,7 @@ UTXOsPacket.prototype.toRaw = function toRaw() { */ UTXOsPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); var i, bit, oct, coin, output; var version, height, map, count; @@ -2116,6 +2389,15 @@ util.inherits(HaveWitnessPacket, Packet); HaveWitnessPacket.prototype.cmd = 'havewitness'; HaveWitnessPacket.prototype.type = exports.types.HAVEWITNESS; +/** + * Get serialization size. + * @returns {Number} + */ + +HaveWitnessPacket.prototype.getSize = function getSize() { + return 0; +}; + /** * Serialize havewitness packet. * @returns {Buffer} @@ -2170,13 +2452,22 @@ util.inherits(FeeFilterPacket, Packet); FeeFilterPacket.prototype.cmd = 'feefilter'; FeeFilterPacket.prototype.type = exports.types.FEEFILTER; +/** + * Get serialization size. + * @returns {Number} + */ + +FeeFilterPacket.prototype.getSize = function getSize() { + return 8; +}; + /** * Serialize feefilter packet. * @returns {Buffer} */ FeeFilterPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(8); bw.write64(this.rate); return bw.render(); }; @@ -2188,7 +2479,7 @@ FeeFilterPacket.prototype.toRaw = function toRaw() { */ FeeFilterPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.rate = br.read64(); return this; }; @@ -2231,13 +2522,22 @@ util.inherits(SendCmpctPacket, Packet); SendCmpctPacket.prototype.cmd = 'sendcmpct'; SendCmpctPacket.prototype.type = exports.types.SENDCMPCT; +/** + * Get serialization size. + * @returns {Number} + */ + +SendCmpctPacket.prototype.getSize = function getSize() { + return 9; +}; + /** * Serialize sendcmpct packet. * @returns {Buffer} */ SendCmpctPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(9); bw.writeU8(this.mode); bw.writeU64(this.version); return bw.render(); @@ -2250,7 +2550,7 @@ SendCmpctPacket.prototype.toRaw = function toRaw() { */ SendCmpctPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.mode = br.readU8(); this.version = br.readU53(); return this; @@ -2469,13 +2769,22 @@ util.inherits(EncinitPacket, Packet); EncinitPacket.prototype.cmd = 'encinit'; EncinitPacket.prototype.type = exports.types.ENCINIT; +/** + * Get serialization size. + * @returns {Number} + */ + +EncinitPacket.prototype.getSize = function getSize() { + return 34; +}; + /** * Serialize encinit packet. * @returns {Buffer} */ EncinitPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(34); bw.writeBytes(this.publicKey); bw.writeU8(this.cipher); return bw.render(); @@ -2488,7 +2797,7 @@ EncinitPacket.prototype.toRaw = function toRaw() { */ EncinitPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.publicKey = br.readBytes(33); this.cipher = br.readU8(); return this; @@ -2529,13 +2838,22 @@ util.inherits(EncackPacket, Packet); EncackPacket.prototype.cmd = 'encack'; EncackPacket.prototype.type = exports.types.ENCACK; +/** + * Get serialization size. + * @returns {Number} + */ + +EncackPacket.prototype.getSize = function getSize() { + return 33; +}; + /** * Serialize encack packet. * @returns {Buffer} */ EncackPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(33); bw.writeBytes(this.publicKey); return bw.render(); }; @@ -2547,7 +2865,7 @@ EncackPacket.prototype.toRaw = function toRaw() { */ EncackPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.publicKey = br.readBytes(33); return this; }; @@ -2587,13 +2905,22 @@ util.inherits(AuthChallengePacket, Packet); AuthChallengePacket.prototype.cmd = 'authchallenge'; AuthChallengePacket.prototype.type = exports.types.AUTHCHALLENGE; +/** + * Get serialization size. + * @returns {Number} + */ + +EncackPacket.prototype.getSize = function getSize() { + return 32; +}; + /** * Serialize authchallenge packet. * @returns {Buffer} */ AuthChallengePacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(32); bw.writeBytes(this.hash); return bw.render(); }; @@ -2605,7 +2932,7 @@ AuthChallengePacket.prototype.toRaw = function toRaw() { */ AuthChallengePacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.hash = br.readHash(); return this; }; @@ -2645,13 +2972,22 @@ util.inherits(AuthReplyPacket, Packet); AuthReplyPacket.prototype.cmd = 'authreply'; AuthReplyPacket.prototype.type = exports.types.AUTHREPLY; +/** + * Get serialization size. + * @returns {Number} + */ + +AuthReplyPacket.prototype.getSize = function getSize() { + return 64; +}; + /** * Serialize authreply packet. * @returns {Buffer} */ AuthReplyPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(64); bw.writeBytes(this.signature); return bw.render(); }; @@ -2663,7 +2999,7 @@ AuthReplyPacket.prototype.toRaw = function toRaw() { */ AuthReplyPacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.signature = br.readBytes(64); return this; }; @@ -2703,13 +3039,22 @@ util.inherits(AuthProposePacket, Packet); AuthProposePacket.prototype.cmd = 'authpropose'; AuthProposePacket.prototype.type = exports.types.AUTHPROPOSE; +/** + * Get serialization size. + * @returns {Number} + */ + +AuthProposePacket.prototype.getSize = function getSize() { + return 32; +}; + /** * Serialize authpropose packet. * @returns {Buffer} */ AuthProposePacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(32); bw.writeBytes(this.hash); return bw.render(); }; @@ -2721,7 +3066,7 @@ AuthProposePacket.prototype.toRaw = function toRaw() { */ AuthProposePacket.prototype.fromRaw = function fromRaw(data) { - var br = BufferReader(data); + var br = new BufferReader(data); this.hash = br.readHash(); return this; }; @@ -2763,15 +3108,22 @@ util.inherits(UnknownPacket, Packet); UnknownPacket.prototype.type = exports.types.UNKNOWN; +/** + * Get serialization size. + * @returns {Number} + */ + +UnknownPacket.prototype.getSize = function getSize() { + return this.data.length; +}; + /** * Serialize unknown packet. * @returns {Buffer} */ UnknownPacket.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); - bw.writeBytes(this.data); - return bw.render(); + return this.data; }; /** diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index 78e7c514..243e9804 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -160,14 +160,37 @@ AbstractBlock.prototype.hash = function hash(enc) { */ AbstractBlock.prototype.abbr = function abbr() { - var bw = new StaticWriter(80); + return this.writeAbbr(new StaticWriter(80)).render(); +}; + +/** + * Serialize the block headers. + * @param {BufferWriter} bw + */ + +AbstractBlock.prototype.writeAbbr = function writeAbbr(bw) { bw.writeU32(this.version); bw.writeHash(this.prevBlock); bw.writeHash(this.merkleRoot); bw.writeU32(this.ts); bw.writeU32(this.bits); bw.writeU32(this.nonce); - return bw.render(); + return bw; +}; + +/** + * Parse the block headers. + * @param {BufferReader} br + */ + +AbstractBlock.prototype.parseAbbr = function parseAbbr(br) { + this.version = br.readU32(); + this.prevBlock = br.readHash('hex'); + this.merkleRoot = br.readHash('hex'); + this.ts = br.readU32(); + this.bits = br.readU32(); + this.nonce = br.readU32(); + return this; }; /** diff --git a/lib/primitives/address.js b/lib/primitives/address.js index c5354ff5..23d4d7bf 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -13,8 +13,8 @@ var constants = require('../protocol/constants'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var assert = require('assert'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); var scriptTypes = constants.scriptTypes; @@ -123,6 +123,25 @@ Address.prototype.getType = function getType() { return constants.scriptTypesByVal[this.type].toLowerCase(); }; +/** + * Calculate size of serialized address. + * @returns {Number} + */ + +Address.prototype.getSize = function getSize() { + var size = 0; + + size += 1; + + if (this.version !== -1) + size += 2; + + size += this.hash.length; + size += 4; + + return size; +}; + /** * Compile the address object to its raw serialization. * @param {{NetworkType|Network)?} network @@ -131,7 +150,8 @@ Address.prototype.getType = function getType() { */ Address.prototype.toRaw = function toRaw(network) { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var prefix; if (!network) diff --git a/lib/primitives/block.js b/lib/primitives/block.js index f2d71942..61afa4ff 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -9,13 +9,14 @@ var assert = require('assert'); var util = require('../utils/util'); +var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); var btcutils = require('../btc/utils'); var constants = require('../protocol/constants'); var AbstractBlock = require('./abstractblock'); var VerifyResult = require('../btc/errors').VerifyResult; -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); var TX = require('./tx'); var MerkleBlock = require('./merkleblock'); var Headers = require('./headers'); @@ -42,7 +43,7 @@ function Block(options) { this._raw = null; this._size = -1; - this._witnessSize = -1; + this._witness = -1; if (options) this.fromOptions(options); @@ -81,7 +82,7 @@ Block.fromOptions = function fromOptions(options) { */ Block.prototype.toRaw = function toRaw() { - return this.getRaw().data; + return this.frame().data; }; /** @@ -101,7 +102,14 @@ Block.prototype.toNormal = function toNormal() { */ Block.prototype.toWriter = function toWriter(bw) { - this.writeRaw(bw); + var raw; + + if (this.mutable) + return this.writeWitness(bw); + + raw = this.frame(); + bw.writeBytes(raw.data); + return bw; }; @@ -112,7 +120,7 @@ Block.prototype.toWriter = function toWriter(bw) { Block.prototype.toNormalWriter = function toNormalWriter(bw) { if (this.hasWitness()) { - this.frameNormalWriter(bw); + this.writeNormal(bw); return bw; } return this.toWriter(bw); @@ -125,7 +133,7 @@ Block.prototype.toNormalWriter = function toNormalWriter(bw) { * @returns {RawBlock} */ -Block.prototype.getRaw = function getRaw() { +Block.prototype.frame = function frame() { var raw; if (this.mutable) { @@ -135,8 +143,8 @@ Block.prototype.getRaw = function getRaw() { if (this._raw) { assert(this._size > 0); - assert(this._witnessSize >= 0); - raw = new RawBlock(this._size, this._witnessSize); + assert(this._witness >= 0); + raw = new RawBlock(this._size, this._witness); raw.data = this._raw; return raw; } @@ -145,26 +153,7 @@ Block.prototype.getRaw = function getRaw() { 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); + this._witness = raw.witness; return raw; }; @@ -176,8 +165,8 @@ Block.prototype.writeRaw = function writeRaw(bw) { Block.prototype.getSizes = function getSizes() { if (this.mutable) - return this.writeRaw(new BufferWriter()); - return this.getRaw(); + return this.getWitnessSizes(); + return this.frame(); }; /** @@ -229,8 +218,8 @@ Block.prototype.getBaseSize = function getBaseSize() { Block.prototype.hasWitness = function hasWitness() { var i, tx; - if (this._witnessSize !== -1) - return this._witnessSize !== 0; + if (this._witness !== -1) + return this._witness !== 0; for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; @@ -720,29 +709,25 @@ Block.fromJSON = function fromJSON(json) { */ Block.prototype.fromReader = function fromReader(br) { - var witnessSize = 0; + var witness = 0; var i, tx; br.start(); - 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.parseAbbr(br); + this.totalTX = br.readVarint(); for (i = 0; i < this.totalTX; i++) { tx = TX.fromReader(br); - witnessSize += tx._witnessSize; + witness += tx._witness; this.addTX(tx); } if (!this.mutable) { this._raw = br.endData(); this._size = this._raw.length; - this._witnessSize = witnessSize; + this._witness = witness; } return this; @@ -802,15 +787,11 @@ Block.prototype.toMerkle = function toMerkle(filter) { * @returns {Buffer} */ -Block.prototype.frameNormalWriter = function frameNormalWriter(bw) { +Block.prototype.writeNormal = function writeNormal(bw) { var i, tx; - bw.writeU32(this.version); - bw.writeHash(this.prevBlock); - bw.writeHash(this.merkleRoot); - bw.writeU32(this.ts); - bw.writeU32(this.bits); - bw.writeU32(this.nonce); + this.writeAbbr(bw); + bw.writeVarint(this.txs.length); for (i = 0; i < this.txs.length; i++) { @@ -818,7 +799,7 @@ Block.prototype.frameNormalWriter = function frameNormalWriter(bw) { tx.toNormalWriter(bw); } - return new RawBlock(bw.written, 0); + return bw; }; /** @@ -829,25 +810,19 @@ Block.prototype.frameNormalWriter = function frameNormalWriter(bw) { * @returns {Buffer} */ -Block.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { - var witnessSize = 0; - var i, tx, raw; +Block.prototype.writeWitness = function writeWitness(bw) { + var i, tx; + + this.writeAbbr(bw); - 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; + tx.toWriter(bw); } - return new RawBlock(bw.written, witnessSize); + return bw; }; /** @@ -859,10 +834,11 @@ Block.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { */ Block.prototype.frameNormal = function frameNormal() { - var bw = new BufferWriter(); - var raw = this.frameNormalWriter(bw); - raw.data = bw.render(); - return raw; + var size = this.getNormalSizes(); + var bw = new StaticWriter(size.total); + this.writeNormal(bw); + size.data = bw.render(); + return size; }; /** @@ -873,10 +849,11 @@ Block.prototype.frameNormal = function frameNormal() { */ Block.prototype.frameWitness = function frameWitness() { - var bw = new BufferWriter(); - var raw = this.frameWitnessWriter(bw); - raw.data = bw.render(); - return raw; + var size = this.getWitnessSizes(); + var bw = new StaticWriter(size.total); + this.writeWitness(bw); + size.data = bw.render(); + return size; }; /** @@ -888,6 +865,49 @@ Block.prototype.toHeaders = function toHeaders() { return Headers.fromBlock(this); }; +/** + * Get real block size without witness. + * @returns {RawBlock} + */ + +Block.prototype.getNormalSizes = function getNormalSizes() { + var size = 0; + var i, tx; + + size += 80; + size += encoding.sizeVarint(this.txs.length); + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + size += tx.getBaseSize(); + } + + return new RawBlock(size, 0); +}; + +/** + * Get real block size with witness. + * @returns {RawBlock} + */ + +Block.prototype.getWitnessSizes = function getWitnessSizes() { + var size = 0; + var witness = 0; + var i, sizes, tx; + + size += 80; + size += encoding.sizeVarint(this.txs.length); + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + sizes = tx.getSizes(); + size += sizes.total; + witness += sizes.witness; + } + + return new RawBlock(size, witness); +}; + /** * Test whether an object is a Block. * @param {Object} obj diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index 41a7031d..300630d6 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -15,8 +15,8 @@ var Amount = require('../btc/amount'); var Output = require('./output'); var Script = require('../script/script'); var Network = require('../protocol/network'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); /** * Represents an unspent output. @@ -241,6 +241,15 @@ Coin.fromJSON = function fromJSON(json) { return new Coin().fromJSON(json); }; +/** + * Calculate size of coin. + * @returns {Number} + */ + +Coin.prototype.getSize = function getSize() { + return 17 + this.script.getVarSize(); +}; + /** * Write the coin to a buffer writer. * @param {BufferWriter} bw @@ -267,7 +276,8 @@ Coin.prototype.toWriter = function toWriter(bw) { */ Coin.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; /** diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index bad7ae05..79a1036a 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -9,7 +9,8 @@ var util = require('../utils/util'); var AbstractBlock = require('./abstractblock'); -var BufferWriter = require('../utils/writer'); +var encoding = require('../utils/encoding'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); /** @@ -47,7 +48,7 @@ Headers.prototype._verify = function _verify(ret) { */ Headers.prototype.getSize = function getSize() { - return this.toWriter(new BufferWriter()).written; + return 80 + encoding.sizeVarint(this.totalTX); }; /** @@ -56,12 +57,7 @@ Headers.prototype.getSize = function getSize() { */ Headers.prototype.toWriter = function toWriter(bw) { - bw.writeU32(this.version); - bw.writeHash(this.prevBlock); - bw.writeHash(this.merkleRoot); - bw.writeU32(this.ts); - bw.writeU32(this.bits); - bw.writeU32(this.nonce); + this.writeAbbr(bw); bw.writeVarint(this.totalTX); return bw; }; @@ -72,7 +68,8 @@ Headers.prototype.toWriter = function toWriter(bw) { */ Headers.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; /** @@ -82,12 +79,7 @@ Headers.prototype.toRaw = function toRaw() { */ 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.parseAbbr(br); this.totalTX = br.readVarint(); return this; }; @@ -132,13 +124,7 @@ Headers.fromRaw = function fromRaw(data, enc) { */ 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; + return this.parseAbbr(br); }; /** diff --git a/lib/primitives/invitem.js b/lib/primitives/invitem.js index 4c5defc5..098c9de0 100644 --- a/lib/primitives/invitem.js +++ b/lib/primitives/invitem.js @@ -8,8 +8,8 @@ 'use strict'; var constants = require('../protocol/constants'); -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); /** * Inv Item @@ -27,6 +27,15 @@ function InvItem(type, hash) { this.hash = hash; } +/** + * Write inv item to buffer writer. + * @param {BufferWriter} bw + */ + +InvItem.prototype.getSize = function getSize() { + return 36; +}; + /** * Write inv item to buffer writer. * @param {BufferWriter} bw @@ -44,7 +53,7 @@ InvItem.prototype.toWriter = function toWriter(bw) { */ InvItem.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + return this.toWriter(new StaticWriter(36)).render(); }; /** diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index 6ca26d1d..45e9868d 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -9,12 +9,13 @@ var constants = require('../protocol/constants'); var util = require('../utils/util'); +var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); var assert = require('assert'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); var Script = require('../script/script'); var Address = require('./address'); @@ -262,6 +263,25 @@ KeyRing.fromScript = function fromScript(key, script, compressed, network) { return new KeyRing().fromScript(key, script, compressed, network); }; +/** + * Calculate WIF serialization size. + * @returns {Number} + */ + +KeyRing.prototype.getSecretSize = function getSecretSize() { + var size = 0; + + size += 1; + size += this.privateKey.length; + + if (this.publicKey.length === 33) + size += 1; + + size += 4; + + return size; +}; + /** * Convert key to a CBitcoinSecret. * @param {(Network|NetworkType)?} network @@ -269,7 +289,8 @@ KeyRing.fromScript = function fromScript(key, script, compressed, network) { */ KeyRing.prototype.toSecret = function toSecret() { - var bw = new BufferWriter(); + var size = this.getSecretSize(); + var bw = new StaticWriter(size); assert(this.privateKey, 'Cannot serialize without private key.'); @@ -800,6 +821,26 @@ KeyRing.fromJSON = function fromJSON(json) { return new KeyRing().fromJSON(json); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +KeyRing.prototype.getSize = function getSize() { + var size = 0; + size += 1; + if (this.privateKey) { + size += encoding.sizeVarint(this.privateKey.length); + size += this.privateKey.length; + size += 1; + } else { + size += encoding.sizeVarint(this.publicKey.length); + size += this.publicKey.length; + } + size += this.script ? this.script.getVarSize() : 1; + return size; +}; + /** * Write the keyring to a buffer writer. * @param {BufferWriter} bw @@ -837,7 +878,8 @@ KeyRing.prototype.toWriter = function toWriter(bw) { */ KeyRing.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; /** diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index ead6c0f0..ca0d0f89 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -98,12 +98,8 @@ MemBlock.prototype.fromRaw = function fromRaw(data) { var height = -1; var inCount, input; - 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.parseAbbr(br); + this.totalTX = br.readVarint(); if (this.version > 1 && this.totalTX > 0) { diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 6aed1d37..7f6f95e7 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -14,8 +14,9 @@ var constants = require('../protocol/constants'); var DUMMY = new Buffer([0]); var AbstractBlock = require('./abstractblock'); var VerifyResult = require('../btc/errors').VerifyResult; -var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var Headers = require('./headers'); var TX = require('./tx'); @@ -88,15 +89,6 @@ MerkleBlock.fromOptions = function fromOptions(data) { return new MerkleBlock().fromOptions(data); }; -/** - * Get merkleblock size. - * @returns {Number} Size. - */ - -MerkleBlock.prototype.getSize = function getSize() { - return this.toWriter(new BufferWriter()).written; -}; - /** * Add a transaction to the block's tx vector. * @param {TX} tx @@ -345,6 +337,22 @@ MerkleBlock.prototype.format = function format(view, height) { }; }; +/** + * Get merkleblock size. + * @returns {Number} Size. + */ + +MerkleBlock.prototype.getSize = function getSize() { + var size = 0; + size += 80; + size += 4; + size += encoding.sizeVarint(this.hashes.length); + size += this.hashes.length * 32; + size += encoding.sizeVarint(this.flags.length); + size += this.flags.length; + return size; +}; + /** * Write the merkleblock to a buffer writer. * @param {BufferWriter} bw @@ -353,12 +361,8 @@ MerkleBlock.prototype.format = function format(view, height) { MerkleBlock.prototype.toWriter = function toWriter(bw) { var i; - bw.writeU32(this.version); - bw.writeHash(this.prevBlock); - bw.writeHash(this.merkleRoot); - bw.writeU32(this.ts); - bw.writeU32(this.bits); - bw.writeU32(this.nonce); + this.writeAbbr(bw); + bw.writeU32(this.totalTX); bw.writeVarint(this.hashes.length); @@ -378,7 +382,8 @@ MerkleBlock.prototype.toWriter = function toWriter(bw) { */ MerkleBlock.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; /** @@ -390,12 +395,8 @@ MerkleBlock.prototype.toRaw = function toRaw() { MerkleBlock.prototype.fromReader = function fromReader(br) { var i, count; - this.version = br.readU32(); - this.prevBlock = br.readHash('hex'); - this.merkleRoot = br.readHash('hex'); - this.ts = br.readU32(); - this.bits = br.readU32(); - this.nonce = br.readU32(); + this.parseAbbr(br); + this.totalTX = br.readU32(); count = br.readVarint(); diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index 87885f80..c1e17452 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -11,7 +11,7 @@ var Network = require('../protocol/network'); var util = require('../utils/util'); var IP = require('../utils/ip'); var assert = require('assert'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); /** @@ -316,6 +316,15 @@ NetworkAddress.prototype.toWriter = function toWriter(bw, full) { return bw; }; +/** + * Calculate serialization size of address. + * @returns {Number} + */ + +NetworkAddress.prototype.getSize = function getSize(full) { + return 26 + (full ? 4 : 0); +}; + /** * Serialize network address. * @param {Boolean?} full - Include timestamp. @@ -323,7 +332,8 @@ NetworkAddress.prototype.toWriter = function toWriter(bw, full) { */ NetworkAddress.prototype.toRaw = function toRaw(full) { - return this.toWriter(new BufferWriter(), full).render(); + var size = this.getSize(full); + return this.toWriter(new StaticWriter(size), full).render(); }; /* diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 371b95dd..02257ea4 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -17,7 +17,7 @@ var Amount = require('../btc/amount'); var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var Script = require('../script/script'); -var BufferWriter = require('../utils/writer'); +var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var VerifyResult = require('../btc/errors').VerifyResult; var Input = require('./input'); @@ -25,8 +25,6 @@ var Output = require('./output'); var Outpoint = require('./outpoint'); var InvItem = require('./invitem'); var workerPool = require('../workers/workerpool').pool; -var BufferWriter = require('../utils/writer'); -var BufferReader = require('../utils/reader'); /* * Constants @@ -70,7 +68,7 @@ function TX(options) { this._raw = null; this._size = -1; - this._witnessSize = -1; + this._witness = -1; this._outputValue = -1; this._inputValue = -1; @@ -204,7 +202,7 @@ TX.prototype.witnessHash = function witnessHash(enc) { */ TX.prototype.toRaw = function toRaw() { - return this.getRaw().data; + return this.frame().data; }; /** @@ -226,7 +224,14 @@ TX.prototype.toNormal = function toNormal() { */ TX.prototype.toWriter = function toWriter(bw) { - this.writeRaw(bw); + if (this.mutable) { + if (this.hasWitness()) + return this.writeWitness(bw); + return this.writeNormal(bw); + } + + bw.writeBytes(this.toRaw()); + return bw; }; @@ -238,7 +243,7 @@ TX.prototype.toWriter = function toWriter(bw) { TX.prototype.toNormalWriter = function toNormalWriter(bw) { if (this.hasWitness()) { - this.frameNormalWriter(bw); + this.writeNormal(bw); return bw; } return this.toWriter(bw); @@ -253,7 +258,7 @@ TX.prototype.toNormalWriter = function toNormalWriter(bw) { * @returns {RawTX} */ -TX.prototype.getRaw = function getRaw() { +TX.prototype.frame = function frame() { var raw; if (this.mutable) { @@ -265,8 +270,8 @@ TX.prototype.getRaw = function getRaw() { if (this._raw) { assert(this._size > 0); - assert(this._witnessSize >= 0); - raw = new RawTX(this._size, this._witnessSize); + assert(this._witness >= 0); + raw = new RawTX(this._size, this._witness); raw.data = this._raw; return raw; } @@ -278,28 +283,7 @@ TX.prototype.getRaw = function getRaw() { this._raw = raw.data; this._size = raw.total; - this._witnessSize = raw.witness; - - return raw; -}; - -/** - * 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); + this._witness = raw.witness; return raw; }; @@ -310,9 +294,12 @@ TX.prototype.writeRaw = function writeRaw(bw) { */ TX.prototype.getSizes = function getSizes() { - if (this.mutable) - return this.writeRaw(new BufferWriter()); - return this.getRaw(); + if (this.mutable) { + if (this.hasWitness()) + return this.getWitnessSizes(); + return this.getNormalSizes(); + } + return this.frame(); }; /** @@ -382,8 +369,8 @@ TX.prototype.getBaseSize = function getBaseSize() { TX.prototype.hasWitness = function hasWitness() { var i, input; - if (this._witnessSize !== -1) - return this._witnessSize !== 0; + if (this._witness !== -1) + return this._witness !== 0; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; @@ -2263,7 +2250,7 @@ TX.prototype.fromReader = function fromReader(br) { if (!this.mutable) { this._raw = br.endData(); this._size = this._raw.length; - this._witnessSize = 0; + this._witness = 0; } else { br.end(); } @@ -2280,7 +2267,7 @@ TX.prototype.fromReader = function fromReader(br) { TX.prototype.fromWitnessReader = function fromWitnessReader(br) { var flag = 0; - var witnessSize = 0; + var witness = 0; var hasWitness = false; var i, count, input; @@ -2309,7 +2296,7 @@ TX.prototype.fromWitnessReader = function fromWitnessReader(br) { if (flag & 1) { flag ^= 1; - br.start(); + witness = br.offset; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; @@ -2318,7 +2305,7 @@ TX.prototype.fromWitnessReader = function fromWitnessReader(br) { hasWitness = true; } - witnessSize = br.end() + 2; + witness = (br.offset - witness) + 2; } if (flag !== 0) @@ -2335,7 +2322,7 @@ TX.prototype.fromWitnessReader = function fromWitnessReader(br) { if (!this.mutable && hasWitness) { this._raw = br.endData(); this._size = this._raw.length; - this._witnessSize = witnessSize; + this._witness = witness; } else { br.end(); } @@ -2350,10 +2337,11 @@ TX.prototype.fromWitnessReader = function fromWitnessReader(br) { */ TX.prototype.frameNormal = function frameNormal() { - var bw = new BufferWriter(); - var raw = this.frameNormalWriter(bw); - raw.data = bw.render(); - return raw; + var sizes = this.getNormalSizes(); + var bw = new StaticWriter(sizes.total); + this.writeNormal(bw); + sizes.data = bw.render(); + return sizes; }; /** @@ -2364,10 +2352,11 @@ TX.prototype.frameNormal = function frameNormal() { */ TX.prototype.frameWitness = function frameWitness() { - var bw = new BufferWriter(); - var raw = this.frameWitnessWriter(bw); - raw.data = bw.render(); - return raw; + var sizes = this.getWitnessSizes(); + var bw = new StaticWriter(sizes.total); + this.writeWitness(bw); + sizes.data = bw.render(); + return sizes; }; /** @@ -2377,8 +2366,7 @@ TX.prototype.frameWitness = function frameWitness() { * @returns {RawTX} */ -TX.prototype.frameNormalWriter = function frameNormalWriter(bw) { - var offset = bw.written; +TX.prototype.writeNormal = function writeNormal(bw) { var i, input, output; if (this.inputs.length === 0 && this.outputs.length !== 0) @@ -2402,7 +2390,7 @@ TX.prototype.frameNormalWriter = function frameNormalWriter(bw) { bw.writeU32(this.locktime); - return new RawTX(bw.written - offset, 0); + return bw; }; /** @@ -2413,10 +2401,8 @@ TX.prototype.frameNormalWriter = function frameNormalWriter(bw) { * @returns {RawTX} */ -TX.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { - var offset = bw.written; - var witnessSize = 0; - var i, start, input, output; +TX.prototype.writeWitness = function writeWitness(bw) { + var i, witness, input, output; if (this.inputs.length === 0 && this.outputs.length !== 0) throw new Error('Cannot serialize zero-input tx.'); @@ -2439,21 +2425,86 @@ TX.prototype.frameWitnessWriter = function frameWitnessWriter(bw) { output.toWriter(bw); } - start = bw.written; + witness = bw.written; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; input.witness.toWriter(bw); } - witnessSize += bw.written - start; + witness = bw.written - witness; bw.writeU32(this.locktime); - if (witnessSize === this.inputs.length) + if (witness === this.inputs.length) throw new Error('Cannot serialize empty-witness tx.'); - return new RawTX(bw.written - offset, witnessSize + 2); + return bw; +}; + +/** + * Calculate the real size of the transaction + * without the witness vector. + * @returns {RawTX} + */ + +TX.prototype.getNormalSizes = function getNormalSizes() { + var base = 0; + var i, input, output; + + base += 4; + + base += encoding.sizeVarint(this.inputs.length); + + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + base += input.getSize(); + } + + base += encoding.sizeVarint(this.outputs.length); + + for (i = 0; i < this.outputs.length; i++) { + output = this.outputs[i]; + base += output.getSize(); + } + + base += 4; + + return new RawTX(base, 0); +}; + +/** + * Calculate the real size of the transaction + * with the witness included. + * @returns {RawTX} + */ + +TX.prototype.getWitnessSizes = function getWitnessSizes() { + var base = 0; + var witness = 0; + var i, input, output; + + base += 4; + witness += 2; + + base += encoding.sizeVarint(this.inputs.length); + + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + base += input.getSize(); + witness += input.witness.getVarSize(); + } + + base += encoding.sizeVarint(this.outputs.length); + + for (i = 0; i < this.outputs.length; i++) { + output = this.outputs[i]; + base += output.getSize(); + } + + base += 4; + + return new RawTX(base + witness, witness); }; /** diff --git a/lib/primitives/txmeta.js b/lib/primitives/txmeta.js index 8d2da6fa..3ab9c31e 100644 --- a/lib/primitives/txmeta.js +++ b/lib/primitives/txmeta.js @@ -9,7 +9,7 @@ var assert = require('assert'); var util = require('../utils/util'); var TX = require('./tx'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); /** @@ -193,6 +193,28 @@ TXMeta.fromJSON = function fromJSON(json) { return new TXMeta().fromJSON(JSON); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +TXMeta.prototype.getSize = function getSize() { + var size = 0; + + size += this.tx.getSize(); + size += 4; + + if (this.block) { + size += 1; + size += 32; + size += 4 * 3; + } else { + size += 1; + } + + return size; +}; + /** * Serialize a transaction to "extended format". * This is the serialization format bcoin uses internally @@ -203,7 +225,8 @@ TXMeta.fromJSON = function fromJSON(json) { */ TXMeta.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); this.tx.toWriter(bw); diff --git a/lib/script/script.js b/lib/script/script.js index c5025997..0c93ce54 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -1823,7 +1823,7 @@ Script.fromAddress = function fromAddress(address) { */ Script.prototype.fromCommitment = function fromCommitment(hash, flags) { - var bw = new BufferWriter(); + var bw = new StaticWriter(36); bw.writeU32BE(0xaa21a9ed); bw.writeHash(hash); @@ -3551,7 +3551,7 @@ Script.checksig = function checksig(msg, sig, key, flags) { Script.sign = function sign(msg, key, type) { var sig = ec.sign(msg, key); - var bw = new BufferWriter(); + var bw = new StaticWriter(sig.length + 1); // Add the sighash type as a single byte // to the signature. diff --git a/lib/utils/bloom.js b/lib/utils/bloom.js index 4603f1fd..d7b2302e 100644 --- a/lib/utils/bloom.js +++ b/lib/utils/bloom.js @@ -10,8 +10,9 @@ var assert = require('assert'); var constants = require('../protocol/constants'); var murmur3 = require('./murmur3'); -var BufferWriter = require('./writer'); var BufferReader = require('./reader'); +var StaticWriter = require('./staticwriter'); +var encoding = require('./encoding'); var sum32 = murmur3.sum32; var mul32 = murmur3.mul32; @@ -173,13 +174,27 @@ Bloom.fromRate = function fromRate(items, rate, update) { return new Bloom(size, n, -1, update); }; +/** + * Get serialization size. + * @returns {Number} + */ + +Bloom.prototype.getSize = function getSize() { + var size = 0; + size += encoding.sizeVarint(this.filter.length); + size += this.filter.length; + size += 9; + return size; +}; + /** * Serialize bloom filter. * @returns {Buffer} */ Bloom.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.writeVarBytes(this.filter); bw.writeU32(this.n); diff --git a/lib/utils/index.js b/lib/utils/index.js index f75fa492..6e69900a 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -20,5 +20,6 @@ exports.protobuf = require('./protobuf'); exports.ProtoWriter = exports.protobuf.ProtoWriter; exports.ProtoReader = exports.protobuf.ProtoReader; exports.BufferReader = require('./reader'); +exports.StaticWriter = require('./staticwriter'); exports.util = require('./util'); exports.BufferWriter = require('./writer'); diff --git a/lib/utils/lru.js b/lib/utils/lru.js index ea8f647d..589cf05f 100644 --- a/lib/utils/lru.js +++ b/lib/utils/lru.js @@ -495,6 +495,8 @@ NullCache.prototype.unpush = function unpush(key) {}; * Expose */ -LRU.Nil = NullCache; +exports = LRU; +exports.LRU = LRU; +exports.Nil = NullCache; -module.exports = LRU; +module.exports = exports; diff --git a/lib/wallet/account.js b/lib/wallet/account.js index a9d4dbc7..76618489 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -10,7 +10,8 @@ var util = require('../utils/util'); var co = require('../utils/co'); var assert = require('assert'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var Path = require('./path'); var common = require('./common'); var Script = require('../script/script'); @@ -905,13 +906,32 @@ Account.prototype.toJSON = function toJSON(minimal) { }; }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +Account.prototype.getSize = function getSize() { + var size = 0; + size += encoding.sizeVarint(this.name.length); + size += this.name.length; + size += 1 * 5; + size += 4 * 4; + size += 1; + size += 82; + size += 1; + size += this.keys.length * 82; + return size; +}; + /** * Serialize the account. * @returns {Buffer} */ Account.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i, key; bw.writeVarString(this.name, 'ascii'); diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js index c4434572..98203035 100644 --- a/lib/wallet/masterkey.js +++ b/lib/wallet/masterkey.js @@ -12,7 +12,8 @@ var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var assert = require('assert'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var HD = require('../hd/hd'); /* @@ -439,6 +440,35 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, aes) { return key; }); +/** + * Calculate serialization size. + * @returns {Number} + */ + +MasterKey.prototype.getSize = function getSize() { + var size = 0; + var len; + + if (this.encrypted) { + size += 1; + size += encoding.sizeVarint(this.iv.length); + size += this.iv.length; + size += encoding.sizeVarint(this.ciphertext.length); + size += this.ciphertext.length; + size += 1; + size += 4 * 3; + return size; + } + + size += 1; + + len = this.key.getExtendedSize(); + size += encoding.sizeVarint(len); + size += len; + + return size; +}; + /** * Serialize the key in the form of: * `[enc-flag][iv?][ciphertext?][extended-key?]` @@ -446,7 +476,8 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, aes) { */ MasterKey.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); if (this.encrypted) { bw.writeU8(1); diff --git a/lib/wallet/path.js b/lib/wallet/path.js index 61adb75a..ecce0f35 100644 --- a/lib/wallet/path.js +++ b/lib/wallet/path.js @@ -8,7 +8,8 @@ var assert = require('assert'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); var Address = require('../primitives/address'); var Script = require('../script/script'); @@ -170,13 +171,41 @@ Path.fromRaw = function fromRaw(data) { return new Path().fromRaw(data); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +Path.prototype.getSize = function getSize() { + var size = 0; + + size += 4; + size += 1; + + switch (this.keyType) { + case Path.types.HD: + size += 8; + break; + case Path.types.KEY: + size += 1; + size += encoding.sizeVarint(this.data.length); + size += this.data.length; + break; + } + + size += 2; + + return size; +}; + /** * Serialize path. * @returns {Buffer} */ Path.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.writeU32(this.account); bw.writeU8(this.keyType); diff --git a/lib/wallet/records.js b/lib/wallet/records.js index a75395da..973a2300 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -10,7 +10,7 @@ var assert = require('assert'); var util = require('../utils/util'); var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var TX = require('../primitives/tx'); /** @@ -79,7 +79,7 @@ ChainState.fromRaw = function fromRaw(data) { */ ChainState.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(41); bw.writeU32(this.startHeight); bw.writeHash(this.startHash); @@ -201,7 +201,7 @@ BlockMeta.fromRaw = function fromRaw(data) { */ BlockMeta.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(42); bw.writeHash(this.hash); bw.writeU32(this.height); bw.writeU32(this.ts); @@ -270,6 +270,26 @@ BlockMapRecord.fromRaw = function fromRaw(height, data) { return new BlockMapRecord(height).fromRaw(data); }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +BlockMapRecord.prototype.getSize = function getSize() { + var size = 0; + var i, tx; + + size += 4; + + for (i = 0; i < this.txs.length; i++) { + tx = this.txs[i]; + size += 32; + size += tx.getSize(); + } + + return size; +}; + /** * Serialize the wallet block as a block. * Contains matching transaction hashes. @@ -277,7 +297,8 @@ BlockMapRecord.fromRaw = function fromRaw(height, data) { */ BlockMapRecord.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i, tx; bw.writeU32(this.txs.length); @@ -363,8 +384,13 @@ TXMapRecord.prototype.toWriter = function toWriter(bw) { return serializeWallets(bw, this.wids); }; +TXMapRecord.prototype.getSize = function getSize() { + return sizeWallets(this.wids); +}; + TXMapRecord.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; TXMapRecord.prototype.fromReader = function fromReader(br) { @@ -407,8 +433,13 @@ OutpointMapRecord.prototype.toWriter = function toWriter(bw) { return serializeWallets(bw, this.wids); }; +OutpointMapRecord.prototype.getSize = function getSize() { + return sizeWallets(this.wids); +}; + OutpointMapRecord.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; OutpointMapRecord.prototype.fromReader = function fromReader(br) { @@ -450,8 +481,13 @@ PathMapRecord.prototype.toWriter = function toWriter(bw) { return serializeWallets(bw, this.wids); }; +PathMapRecord.prototype.getSize = function getSize() { + return sizeWallets(this.wids); +}; + PathMapRecord.prototype.toRaw = function toRaw() { - return this.toWriter(new BufferWriter()).render(); + var size = this.getSize(); + return this.toWriter(new StaticWriter(size)).render(); }; PathMapRecord.prototype.fromReader = function fromReader(br) { @@ -573,13 +609,36 @@ TXRecord.prototype.getDepth = function getDepth(height) { return height - this.height + 1; }; +/** + * Get serialization size. + * @returns {Number} + */ + +TXRecord.prototype.getSize = function getSize() { + var size = 0; + + size += this.tx.getSize(); + size += 4; + + if (this.block) { + size += 1; + size += 32; + size += 4 * 3; + } else { + size += 1; + } + + return size; +}; + /** * Serialize a transaction to "extended format". * @returns {Buffer} */ TXRecord.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var index = this.index; this.tx.toWriter(bw); @@ -663,6 +722,10 @@ function parseWallets(br) { return wids; } +function sizeWallets(wids) { + return 4 + wids.length * 4; +} + function serializeWallets(bw, wids) { var i, wid; diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index e1f554af..8dd29010 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -13,7 +13,7 @@ var co = require('../utils/co'); var assert = require('assert'); var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var btcutils = require('../btc/utils'); var Amount = require('../btc/amount'); var CoinView = require('../coins/coinview'); @@ -2908,7 +2908,7 @@ TXDBState.prototype.toBalance = function toBalance() { */ TXDBState.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var bw = new StaticWriter(32); bw.writeU64(this.tx); bw.writeU64(this.coin); @@ -3010,13 +3010,23 @@ Credit.fromRaw = function fromRaw(data) { return new Credit().fromRaw(data); }; +/** + * Get serialization size. + * @returns {Number} + */ + +Credit.prototype.getSize = function getSize() { + return this.coin.getSize() + 1; +}; + /** * Serialize credit. * @returns {Buffer} */ Credit.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); this.coin.toWriter(bw); bw.writeU8(this.spent ? 1 : 0); return bw.render(); @@ -3389,13 +3399,23 @@ BlockRecord.fromRaw = function fromRaw(data) { return new BlockRecord().fromRaw(data); }; +/** + * Get serialization size. + * @returns {Number} + */ + +BlockRecord.prototype.getSize = function getSize() { + return 44 + this.hashes.length * 32; +}; + /** * Serialize the wallet block as a tip (hash and height). * @returns {Buffer} */ BlockRecord.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); var i; bw.writeHash(this.hash); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 5e7937a1..aa739694 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -18,7 +18,7 @@ var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var btcutils = require('../btc/utils'); var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); +var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); var TXDB = require('./txdb'); var Path = require('./path'); @@ -619,13 +619,13 @@ Wallet.prototype.getID = function getID() { key = this.master.key.derive(44); - bw = new BufferWriter(); + bw = new StaticWriter(37); bw.writeBytes(key.publicKey); bw.writeU32(this.network.magic); hash = crypto.hash160(bw.render()); - bw = new BufferWriter(); + bw = new StaticWriter(27); bw.writeU8(0x03); bw.writeU8(0xbe); bw.writeU8(0x04); @@ -651,7 +651,7 @@ Wallet.prototype.getToken = function getToken(nonce) { key = this.master.key.derive(44, true); - bw = new BufferWriter(); + bw = new StaticWriter(36); bw.writeBytes(key.privateKey); bw.writeU32(nonce); @@ -2534,13 +2534,34 @@ Wallet.prototype.toJSON = function toJSON(unsafe) { }; }; +/** + * Calculate serialization size. + * @returns {Number} + */ + +Wallet.prototype.getSize = function getSize() { + var size = 0; + var len; + + size += 50; + size += encoding.sizeVarint(this.id.length); + size += this.id.length; + + len = this.master.getSize(); + size += encoding.sizeVarint(len); + size += len; + + return size; +}; + /** * Serialize the wallet. * @returns {Buffer} */ Wallet.prototype.toRaw = function toRaw() { - var bw = new BufferWriter(); + var size = this.getSize(); + var bw = new StaticWriter(size); bw.writeU32(this.network.magic); bw.writeU32(this.wid);