From b6ce94cec3e2ac2fe1656039e104e6313732f237 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 28 Jun 2016 18:11:04 -0700 Subject: [PATCH] optimize sighashing. default values for every constructor. --- lib/bcoin/abstractblock.js | 27 ++-- lib/bcoin/address.js | 25 +++- lib/bcoin/bip152.js | 31 ++++- lib/bcoin/block.js | 19 +-- lib/bcoin/coin.js | 24 ++-- lib/bcoin/coins.js | 27 ++-- lib/bcoin/hd.js | 155 ++++++++++++++++------ lib/bcoin/input.js | 50 ++++---- lib/bcoin/keypair.js | 50 ++++---- lib/bcoin/keyring.js | 45 +++++-- lib/bcoin/memblock.js | 18 +-- lib/bcoin/mempool.js | 120 ++++++++++++----- lib/bcoin/merkleblock.js | 12 +- lib/bcoin/mtx.js | 12 +- lib/bcoin/output.js | 11 +- lib/bcoin/protocol/constants.js | 19 +++ lib/bcoin/protocol/packets.js | 156 ++++++++++++++--------- lib/bcoin/script.js | 2 +- lib/bcoin/tx.js | 219 ++++++++++++++++++++++++++------ lib/bcoin/txdb.js | 2 +- lib/bcoin/utils.js | 1 + lib/bcoin/wallet.js | 49 ++++--- test/script-test.js | 16 +-- test/tx-test.js | 8 +- 24 files changed, 750 insertions(+), 348 deletions(-) diff --git a/lib/bcoin/abstractblock.js b/lib/bcoin/abstractblock.js index b1567eeb..fc7ce5d4 100644 --- a/lib/bcoin/abstractblock.js +++ b/lib/bcoin/abstractblock.js @@ -39,8 +39,8 @@ function AbstractBlock(options) { return new AbstractBlock(options); this.version = 1; - this.prevBlock = null; - this.merkleRoot = null; + this.prevBlock = constants.NULL_HASH; + this.merkleRoot = constants.NULL_HASH; this.ts = 0; this.bits = 0; this.nonce = 0; @@ -80,16 +80,19 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) { this.ts = options.ts; this.bits = options.bits; this.nonce = options.nonce; - this.totalTX = options.totalTX || 0; - this.height = options.height != null ? options.height : -1; - this.txs = null; - this.mutable = !!options.mutable; + if (options.totalTX != null) { + assert(utils.isNumber(options.totalTX)); + this.totalTX = options.totalTX; + } - this._valid = null; - this._hash = null; - this._size = null; - this._witnessSize = null; + if (options.height != null) { + assert(utils.isNumber(options.height)); + this.height = options.height; + } + + if (options.mutable != null) + this.mutable = !!options.mutable; return this; }; @@ -102,7 +105,6 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) { AbstractBlock.prototype.parseJSON = function parseJSON(json) { assert(json, 'Block data is required.'); - assert(utils.isNumber(json.height)); assert(utils.isNumber(json.version)); assert(typeof json.prevBlock === 'string'); assert(typeof json.merkleRoot === 'string'); @@ -110,8 +112,8 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) { assert(utils.isNumber(json.bits)); assert(utils.isNumber(json.nonce)); assert(utils.isNumber(json.totalTX)); + assert(utils.isNumber(json.height)); - this.height = json.height; this.version = json.version; this.prevBlock = utils.revHex(json.prevBlock); this.merkleRoot = utils.revHex(json.merkleRoot); @@ -119,6 +121,7 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) { this.bits = json.bits; this.nonce = json.nonce; this.totalTX = json.totalTX; + this.height = json.height; return this; }; diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index fd51c9bc..b40c5ccf 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -9,6 +9,7 @@ var bcoin = require('./env'); var networks = bcoin.protocol.network; +var constants = bcoin.protocol.constants; var utils = require('./utils'); var assert = utils.assert; var BufferWriter = require('./writer'); @@ -35,10 +36,10 @@ function Address(options) { if (!(this instanceof Address)) return new Address(options); - this.hash = null; - this.type = null; - this.version = null; - this.network = null; + this.hash = constants.ZERO_HASH160; + this.type = 'pubkeyhash'; + this.version = -1; + this.network = bcoin.network.get().type; if (options) this.fromOptions(options); @@ -51,13 +52,23 @@ function Address(options) { */ Address.prototype.fromOptions = function fromOptions(options) { + assert(options.hash); + this.hash = options.hash; - this.type = options.type || 'pubkeyhash'; - this.version = options.version == null ? -1 : options.version; - this.network = bcoin.network.get(options.network).type; + + if (options.type) + this.type = options.type; + + if (options.version != null) + this.version = options.version; + + if (options.network) + this.network = bcoin.network.get(options.network).type; if (typeof this.hash === 'string') this.hash = new Buffer(this.hash, 'hex'); + + return this; }; /** diff --git a/lib/bcoin/bip152.js b/lib/bcoin/bip152.js index 2def8834..26fc0360 100644 --- a/lib/bcoin/bip152.js +++ b/lib/bcoin/bip152.js @@ -49,12 +49,23 @@ CompactBlock.prototype._verify = function _verify(ret) { }; CompactBlock.prototype.fromOptions = function fromOptions(options) { + assert(bn.isBN(options.keyNonce)); + assert(Array.isArray(options.ids)); + assert(Array.isArray(options.ptx)); + this.keyNonce = options.keyNonce; - this.ids = options.ids || []; - this.ptx = options.ptx || []; - this.available = options.available || []; - this.idMap = options.idMap || {}; - this.count = options.count || 0; + this.ids = options.ids; + this.ptx = options.ptx; + + if (options.available) + this.available = options.available; + + if (options.idMap) + this.idMap = options.idMap; + + if (options.count) + this.count = options.count; + this.k0 = options.k0; this.k1 = options.k1; @@ -358,7 +369,10 @@ function BlockTXRequest(options) { BlockTXRequest.prototype.fromOptions = function fromOptions(options) { this.hash = options.hash; - this.indexes = options.indexes || []; + + if (options.indexes) + this.indexes = options.indexes; + return this; }; @@ -452,7 +466,10 @@ function BlockTX(options) { BlockTX.prototype.fromOptions = function fromOptions(options) { this.hash = options.hash; - this.txs = options.txs || []; + + if (options.txs) + this.txs = options.txs; + return this; }; diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 8020de3b..6d83328c 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -27,11 +27,13 @@ function Block(options) { bcoin.abstractblock.call(this, options); this.txs = []; + this._cbHeight = null; this._commitmentHash = null; + this._raw = null; - this._size = null; - this._witnessSize = null; + this._size = -1; + this._witnessSize = -1; this._lastWitnessSize = 0; if (options) @@ -49,15 +51,6 @@ utils.inherits(Block, bcoin.abstractblock); Block.prototype.fromOptions = function fromOptions(options) { var i; - this._cbHeight = null; - this._commitmentHash = null; - - this._raw = options._raw || null; - this._size = options._size || null; - this._witnessSize = options._witnessSize != null - ? options._witnessSize - : null; - if (options.txs) { for (i = 0; i < options.txs.length; i++) this.addTX(options.txs[i]); @@ -150,7 +143,7 @@ Block.prototype.getRaw = function getRaw() { Block.prototype.getSizes = function getSizes() { var writer; - if (this._size != null) { + if (this._size !== -1) { return { size: this._size, witnessSize: this._witnessSize @@ -224,7 +217,7 @@ Block.prototype.getBaseSize = function getBaseSize() { Block.prototype.hasWitness = function hasWitness() { var i; - if (this._witnessSize != null) + if (this._witnessSize !== -1) return this._witnessSize !== 0; for (i = 0; i < this.txs.length; i++) { diff --git a/lib/bcoin/coin.js b/lib/bcoin/coin.js index 92aca874..31449ad0 100644 --- a/lib/bcoin/coin.js +++ b/lib/bcoin/coin.js @@ -9,6 +9,7 @@ var bcoin = require('./env'); var utils = require('./utils'); +var constants = bcoin.protocol.constants; var assert = utils.assert; /** @@ -34,13 +35,13 @@ function Coin(options) { if (!(this instanceof Coin)) return new Coin(options); - this.version = null; - this.height = null; - this.value = null; - this.script = null; - this.coinbase = null; - this.hash = null; - this.index = null; + this.version = 1; + this.height = -1; + this.value = 0; + this.script = new bcoin.script(); + this.coinbase = true; + this.hash = constants.NULL_HASH; + this.index = 0; if (options) this.fromOptions(options); @@ -66,7 +67,10 @@ Coin.prototype.fromOptions = function fromOptions(options) { this.version = options.version; this.height = options.height; this.value = options.value; - this.script = bcoin.script(options.script); + + if (options.script) + this.script.fromOptions(options.script); + this.coinbase = options.coinbase; this.hash = options.hash; this.index = options.index; @@ -195,7 +199,7 @@ Coin.prototype.fromJSON = function fromJSON(json) { this.version = json.version; this.height = json.height; this.value = utils.satoshi(json.value); - this.script = bcoin.script.fromJSON(json.script); + this.script.fromJSON(json.script); this.coinbase = json.coinbase; this.hash = json.hash ? utils.revHex(json.hash) : null; this.index = json.index; @@ -240,7 +244,7 @@ Coin.prototype.fromRaw = function fromRaw(data) { this.version = p.readU32(); this.height = p.readU32(); this.value = p.read64N(); - this.script = bcoin.script.fromRaw(p.readVarBytes()); + this.script.fromRaw(p.readVarBytes()); this.coinbase = p.readU8() === 1; if (this.height === 0x7fffffff) diff --git a/lib/bcoin/coins.js b/lib/bcoin/coins.js index edca553b..94fdfae9 100644 --- a/lib/bcoin/coins.js +++ b/lib/bcoin/coins.js @@ -10,6 +10,7 @@ var bcoin = require('./env'); var utils = bcoin.utils; var assert = utils.assert; +var constants = bcoin.protocol.constants; var BufferReader = require('./reader'); var BufferWriter = require('./writer'); @@ -30,10 +31,10 @@ function Coins(options) { if (!(this instanceof Coins)) return new Coins(options); - this.version = -1; - this.hash = null; + this.version = 1; + this.hash = constants.NULL_HASH; this.height = -1; - this.coinbase = false; + this.coinbase = true; this.outputs = []; if (options) @@ -47,10 +48,17 @@ function Coins(options) { */ Coins.prototype.fromOptions = function fromOptions(options) { - this.version = options.version != null ? options.version : -1; - this.hash = options.hash || null; - this.height = options.height != null ? options.height : -1; - this.coinbase = options.coinbase || false; + if (options.version != null) + this.version = options.version; + + if (options.hash) + this.hash = options.hash; + + if (options.height != null) + this.height = options.height; + + if (options.coinbase != null) + this.coinbase = options.coinbase; if (options.outputs) this.outputs = options.outputs; @@ -74,13 +82,16 @@ Coins.fromOptions = function fromOptions(options) { */ Coins.prototype.add = function add(coin) { - if (this.version === -1) { + if (this.outputs.length === 0) { this.version = coin.version; this.hash = coin.hash; this.height = coin.height; this.coinbase = coin.coinbase; } + while (this.outputs.length <= coin.index) + this.outputs.push(null); + if (coin.script.isUnspendable()) { this.outputs[coin.index] = null; return; diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 2bcc6c15..9ad01a13 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -91,6 +91,16 @@ var BufferWriter = require('./writer'); var BufferReader = require('./reader'); var unorm; +/* + * Constants + */ + +var PUBLIC_KEY = new Buffer(33); +PUBLIC_KEY.fill(0); + +var FINGER_PRINT = new Buffer(4); +FINGER_PRINT.fill(0); + /** * HD Mnemonic * @exports Mnemonic @@ -112,12 +122,6 @@ function Mnemonic(options) { if (!(this instanceof Mnemonic)) return new Mnemonic(options); - if (!options) - options = {}; - - if (typeof options === 'string') - options = { phrase: options }; - this.bits = 128; this.entropy = null; this.phrase = null; @@ -136,17 +140,21 @@ function Mnemonic(options) { */ Mnemonic.prototype.fromOptions = function fromOptions(options) { - if (!options) - options = {}; - if (typeof options === 'string') options = { phrase: options }; - this.bits = options.bits || 128; + if (options.bits != null) + this.bits = options.bits; + this.entropy = options.entropy; this.phrase = options.phrase; - this.passphrase = options.passphrase || ''; - this.language = options.language || 'english'; + + if (options.passphrase) + this.passphrase = options.passphrase; + + if (options.language) + this.language = options.language; + this.seed = options.seed; assert(this.bits >= 128); @@ -280,12 +288,27 @@ Mnemonic.prototype.toJSON = function toJSON() { */ Mnemonic.prototype.fromJSON = function fromJSON(json) { + assert(utils.isNumber(json.bits)); + assert(!json.entropy || typeof json.entropy === 'string'); + assert(!json.phrase || typeof json.phrase === 'string'); + assert(typeof json.passphrase === 'string'); + assert(typeof json.language === 'string'); + assert(!json.seed || typeof json.seed === 'string'); + this.bits = json.bits; - this.entropy = new Buffer(json.entropy, 'hex'); - this.phrase = json.phrase; + + if (json.entry) + this.entropy = new Buffer(json.entropy, 'hex'); + + if (json.phrase) + this.phrase = json.phrase; + this.passphrase = json.passphrase; this.language = json.language; - this.seed = new Buffer(json.seed, 'hex'); + + if (json.seed) + this.seed = new Buffer(json.seed, 'hex'); + return this; }; @@ -308,11 +331,30 @@ Mnemonic.prototype.toRaw = function toRaw(writer) { var p = new BufferWriter(writer); p.writeU16(this.bits); - p.writeBytes(this.entropy); - p.writeVarString(this.phrase, 'utf8'); + + if (this.entropy) { + p.writeU8(1); + p.writeBytes(this.entropy); + } else { + p.writeU8(0); + } + + if (this.phrase) { + p.writeU8(1); + p.writeVarString(this.phrase, 'utf8'); + } else { + p.writeU8(0); + } + p.writeVarString(this.passphrase, 'utf8'); p.writeVarString(this.language, 'utf8'); - p.writeBytes(this.seed); + + if (this.seed) { + p.writeU8(1); + p.writeBytes(this.seed); + } else { + p.writeU8(0); + } if (!writer) p = p.render(); @@ -328,12 +370,21 @@ Mnemonic.prototype.toRaw = function toRaw(writer) { Mnemonic.prototype.fromRaw = function fromRaw(data) { var p = new BufferReader(data); + this.bits = p.readU16(); - this.entropy = p.readBytes(this.bits / 8); - this.phrase = p.readVarString('utf8'); + + if (p.readU8() === 1) + this.entropy = p.readBytes(this.bits / 8); + + if (p.readU8() === 1) + this.phrase = p.readVarString('utf8'); + this.passphrase = p.readVarString('utf8'); this.language = p.readVarString('utf8'); - this.seed = p.readBytes(64); + + if (p.readU8() === 1) + this.seed = p.readBytes(64); + return this; }; @@ -607,14 +658,14 @@ function HDPrivateKey(options) { if (!(this instanceof HDPrivateKey)) return new HDPrivateKey(options); - this.network = null; - this.depth = null; - this.parentFingerPrint = null; - this.childIndex = null; - this.chainCode = null; - this.privateKey = null; + this.network = bcoin.network.get().type; + this.depth = 0; + this.parentFingerPrint = FINGER_PRINT; + this.childIndex = 0; + this.chainCode = constants.ZERO_HASH; + this.privateKey = constants.ZERO_HASH; - this.publicKey = null; + this.publicKey = PUBLIC_KEY; this.fingerPrint = null; this.mnemonic = null; @@ -638,9 +689,16 @@ utils.inherits(HDPrivateKey, HD); HDPrivateKey.prototype.fromOptions = function fromOptions(options) { assert(options, 'No options for HD private key.'); + assert(utils.isNumber(options.depth)); + assert(Buffer.isBuffer(options.parentFingerPrint)); + assert(utils.isNumber(options.childIndex)); + assert(Buffer.isBuffer(options.chainCode)); + assert(Buffer.isBuffer(options.privateKey)); assert(options.depth <= 0xff, 'Depth is too high.'); - this.network = bcoin.network.get(options.network).type; + if (options.network) + this.network = bcoin.network.get(options.network).type; + this.depth = options.depth; this.parentFingerPrint = options.parentFingerPrint; this.childIndex = options.childIndex; @@ -649,8 +707,15 @@ HDPrivateKey.prototype.fromOptions = function fromOptions(options) { this.publicKey = ec.publicKeyCreate(options.privateKey, true); - this.mnemonic = options.mnemonic || null; - this._xprivkey = options.xprivkey || null; + if (options.mnemonic) { + assert(options.mnemonic instanceof Mnemonic); + this.mnemonic = options.mnemonic; + } + + if (options.xprivkey) { + assert(typeof options.xprivkey === 'string'); + this._xprivkey = options.xprivkey; + } return this; }; @@ -1291,14 +1356,13 @@ function HDPublicKey(options) { if (!(this instanceof HDPublicKey)) return new HDPublicKey(options); - this.network = null; - this.depth = null; - this.parentFingerPrint = null; - this.childIndex = null; - this.chainCode = null; - this.publicKey = null; + this.network = bcoin.network.get().type; + this.depth = 0; + this.parentFingerPrint = FINGER_PRINT; + this.childIndex = 0; + this.chainCode = constants.ZERO_HASH; + this.publicKey = PUBLIC_KEY; - this.privateKey = null; this.fingerPrint = null; this._xpubkey = null; @@ -1320,16 +1384,25 @@ utils.inherits(HDPublicKey, HD); HDPublicKey.prototype.fromOptions = function fromOptions(options) { assert(options, 'No options for HDPublicKey'); - assert(options.depth <= 0xff, 'Depth is too high.'); + assert(utils.isNumber(options.depth)); + assert(Buffer.isBuffer(options.parentFingerPrint)); + assert(utils.isNumber(options.childIndex)); + assert(Buffer.isBuffer(options.chainCode)); + assert(Buffer.isBuffer(options.publicKey)); + + if (options.network) + this.network = bcoin.network.get(options.network).type; - this.network = bcoin.network.get(options.network).type; this.depth = options.depth; this.parentFingerPrint = options.parentFingerPrint; this.childIndex = options.childIndex; this.chainCode = options.chainCode; this.publicKey = options.publicKey; - this._xpubkey = options.xpubkey; + if (options.xpubkey) { + assert(typeof options.xpubkey === 'string'); + this._xpubkey = options.xpubkey; + } return this; }; diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index a51a200d..8e188cfd 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -26,8 +26,8 @@ function Outpoint(hash, index) { if (!(this instanceof Outpoint)) return new Outpoint(hash, index); - this.hash = hash || null; - this.index = index != null ? index : null; + this.hash = hash || constants.NULL_HASH; + this.index = index != null ? index : 0xffffffff; } /** @@ -200,10 +200,10 @@ function Input(options, mutable) { return new Input(options, mutable); this.mutable = false; - this.prevout = null; - this.script = null; - this.sequence = null; - this.witness = null; + this.prevout = new Outpoint(); + this.script = new bcoin.script(); + this.sequence = 0xffffffff; + this.witness = new bcoin.witness(); this.coin = null; if (options) @@ -219,14 +219,22 @@ function Input(options, mutable) { Input.prototype.fromOptions = function fromOptions(options, mutable) { assert(options, 'Input data is required.'); - assert(options.sequence == null || utils.isNumber(options.sequence)); this.mutable = !!mutable; - this.prevout = Outpoint.fromOptions(options.prevout); - this.script = bcoin.script(options.script); - this.sequence = options.sequence == null ? 0xffffffff : options.sequence; - this.witness = bcoin.witness(options.witness); - this.coin = null; + + if (options.prevout) + this.prevout.fromOptions(options.prevout); + + if (options.script) + this.script.fromOptions(options.script); + + if (options.sequence != null) { + assert(utils.isNumber(options.sequence)); + this.sequence = options.sequence; + } + + if (options.witness) + this.witness.fromOptions(options.witness); if (options.coin) this.coin = bcoin.coin(options.coin); @@ -496,10 +504,10 @@ Input.prototype.toJSON = function toJSON() { Input.prototype.fromJSON = function fromJSON(json) { assert(json, 'Input data is required.'); assert(utils.isNumber(json.sequence)); - this.prevout = Outpoint.fromJSON(json.prevout); + this.prevout.fromJSON(json.prevout); this.coin = json.coin ? bcoin.coin.fromJSON(json.coin) : null; - this.script = bcoin.script.fromJSON(json.script); - this.witness = bcoin.witness.fromJSON(json.witness); + this.script.fromJSON(json.script); + this.witness.fromJSON(json.witness); this.sequence = json.sequence; return this; }; @@ -541,8 +549,8 @@ Input.prototype.toRaw = function toRaw(writer) { Input.prototype.fromRaw = function fromRaw(data) { var p = bcoin.reader(data); - this.prevout = Outpoint.fromRaw(p); - this.script = bcoin.script.fromRaw(p.readVarBytes()); + this.prevout.fromRaw(p); + this.script.fromRaw(p.readVarBytes()); this.sequence = p.readU32(); return this; @@ -589,7 +597,7 @@ Input.prototype.toExtended = function toExtended(writer) { Input.prototype.fromExtended = function fromExtended(data) { var p = bcoin.reader(data); this.fromRaw(p); - this.witness = bcoin.witness.fromRaw(p); + this.witness.fromRaw(p); return this; }; @@ -616,10 +624,8 @@ Input.fromExtended = function fromExtended(data, enc) { Input.prototype.fromCoin = function fromCoin(coin) { assert(typeof coin.hash === 'string'); assert(typeof coin.index === 'number'); - this.prevout = new Outpoint(coin.hash, coin.index); - this.script = new bcoin.script(); - this.sequence = 0xffffffff; - this.witness = new bcoin.witness(); + this.prevout.hash = coin.hash; + this.prevout.index = coin.index; this.coin = coin; return this; }; diff --git a/lib/bcoin/keypair.js b/lib/bcoin/keypair.js index 47461594..66d0366c 100644 --- a/lib/bcoin/keypair.js +++ b/lib/bcoin/keypair.js @@ -29,10 +29,10 @@ function KeyPair(options) { if (!(this instanceof KeyPair)) return new KeyPair(options); - this.network = null; + this.network = bcoin.network.get().type; this.compressed = true; this.privateKey = null; - this._publicKey = null; + this.publicKey = null; if (options) this.fromOptions(options); @@ -45,20 +45,24 @@ function KeyPair(options) { */ KeyPair.prototype.fromOptions = function fromOptions(options) { - if (!options) - options = {}; - - this.compressed = options.compressed !== false; - this.network = bcoin.network.get(options.network); - - if (!options.privateKey && !options.publicKey) - throw new Error('No options for keypair'); - + assert(options.privateKey || options.publicKey); assert(!options.privateKey || Buffer.isBuffer(options.privateKey)); assert(!options.publicKey || Buffer.isBuffer(options.publicKey)); + if (options.compressed != null) + this.compressed = options.compressed; + + if (options.network) + this.network = bcoin.network.get(options.network); + this.privateKey = options.privateKey; - this._publicKey = options.publicKey; + this.publicKey = options.publicKey; + + if (!this.publicKey) { + this.publicKey = bcoin.ec.publicKeyCreate( + this.privateKey, this.compressed + ); + } return this; }; @@ -83,6 +87,7 @@ KeyPair.generate = function(network) { var key = new KeyPair(); key.network = bcoin.network.get(network); key.privateKey = bcoin.ec.generatePrivateKey(); + key.publicKey = bcoin.ec.publicKeyCreate(key.privateKey, true); return key; }; @@ -107,19 +112,6 @@ KeyPair.prototype.verify = function verify(msg, sig) { return bcoin.ec.verify(msg, sig, this.getPublicKey()); }; -KeyPair.prototype.__defineGetter__('publicKey', function() { - if (!this._publicKey) { - if (!this.privateKey) - return; - - this._publicKey = bcoin.ec.publicKeyCreate( - this.privateKey, this.compressed - ); - } - - return this._publicKey; -}); - /** * Get private key. * @param {String?} enc - Can be `"hex"`, `"base58"`, or `null`. @@ -220,10 +212,16 @@ KeyPair.prototype.fromRaw = function fromRaw(data) { if (p.left() > 4) { assert(p.readU8() === 1, 'Bad compression flag.'); this.compressed = true; + } else { + this.compressed = false; } p.verifyChecksum(); + this.publicKey = bcoin.ec.publicKeyCreate( + this.privateKey, this.compressed + ); + return this; }; @@ -312,7 +310,7 @@ KeyPair.fromJSON = function fromJSON(json) { KeyPair.isKeyPair = function isKeyPair(obj) { return obj - && obj._privateKey !== undefined + && obj.privateKey !== undefined && typeof obj.fromSecret === 'function'; }; diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index 76d1990f..68b72cb8 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -34,8 +34,8 @@ function KeyRing(options) { if (!(this instanceof KeyRing)) return new KeyRing(options); - this.network = null; - this.type = null; + this.network = bcoin.network.get(); + this.type = 'pubkeyhash'; this.m = 1; this.n = 1; this.witness = false; @@ -71,18 +71,37 @@ function KeyRing(options) { KeyRing.prototype.fromOptions = function fromOptions(options) { var i; - this.network = bcoin.network.get(options.network); - this.type = options.type || 'pubkeyhash'; - this.m = options.m || 1; - this.n = options.n || 1; - this.witness = options.witness || false; - this.id = options.id; - this.name = options.name; - this.account = options.account; - this.change = options.change; - this.index = options.index; + if (options.network) + this.network = bcoin.network.get(options.network); + + if (options.type) + this.type = options.type; + + if (options.m) + this.m = options.m; + + if (options.n) + this.n = options.n; + + if (options.witness != null) + this.witness = options.witness; + + if (options.id) + this.id = options.id; + + if (options.name) + this.name = options.name; + + if (options.account != null) + this.account = options.account; + + if (options.change != null) + this.change = options.change; + + if (options.index != null) + this.index = options.index; + this.key = options.key; - this.keys = []; if (this.n > 1) this.type = 'multisig'; diff --git a/lib/bcoin/memblock.js b/lib/bcoin/memblock.js index 80c876bb..ce3532de 100644 --- a/lib/bcoin/memblock.js +++ b/lib/bcoin/memblock.js @@ -29,25 +29,11 @@ var utils = require('./utils'); * @exports MemBlock * @constructor * @param {NakedBlock} options - * @param {Buffer} options.raw - * @param {Number} options.coinbaseHeight - * @property {Number} version - Block version. Note - * that BCoin reads versions as unsigned despite - * them being signed on the protocol level. This - * number will never be negative. - * @property {Hash} prevBlock - Previous block hash. - * @property {Hash} merkleRoot - Merkle root hash. - * @property {Number} ts - Timestamp. - * @property {Number} bits - * @property {Number} nonce - * @property {Number} totalTX - Transaction count. - * @property {Number} height - Block height (-1 if not present). * @property {Boolean} memory - Always true. * @property {Number} coinbaseHeight - The coinbase height which * was extracted by the parser (the coinbase is the only * transaction we parse ahead of time). * @property {Buffer} raw - The raw block data. - * @property {ReversedHash} rhash - Reversed block hash (uint256le). */ function MemBlock(options) { @@ -57,7 +43,7 @@ function MemBlock(options) { bcoin.abstractblock.call(this, options); this.memory = true; - this.coinbaseHeight = null; + this.coinbaseHeight = -1; this.raw = null; if (options) @@ -199,7 +185,7 @@ MemBlock.prototype.toBlock = function toBlock() { */ MemBlock.isMemBlock = function isMemBlock(obj) { - return obj && typeof obj.toBlock === 'function'; + return obj && obj.memory && typeof obj.toBlock === 'function'; }; /* diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 90433a48..c4758ab5 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -1876,6 +1876,30 @@ function MempoolEntry(options) { if (!(this instanceof MempoolEntry)) return new MempoolEntry(options); + this.tx = null; + this.height = -1; + this.size = 0; + this.priority = 0; + this.fee = 0; + this.ts = 0; + + this.chainValue = 0; + this.count = 0; + this.sizes = 0; + this.fees = 0; + this.dependencies = false; + + if (options) + this.fromOptions(options); +} + +/** + * Inject properties from options object. + * @private + * @param {Object} options + */ + +MempoolEntry.prototype.fromOptions = function fromOptions(options) { this.tx = options.tx; this.height = options.height; this.size = options.size; @@ -1888,16 +1912,28 @@ function MempoolEntry(options) { this.sizes = options.sizes; this.fees = options.fees; this.dependencies = options.dependencies; -} + + return this; +}; /** - * Create a mempool entry from a TX. - * @param {TX} tx - * @param {Number} height - Entry height. + * Instantiate mempool entry from options. + * @param {Object} options * @returns {MempoolEntry} */ -MempoolEntry.fromTX = function fromTX(tx, height) { +MempoolEntry.fromOptions = function fromOptions(options) { + return new MempoolEntry().fromOptions(options); +}; + +/** + * Inject properties from transaction. + * @private + * @param {TX} tx + * @param {Number} height + */ + +MempoolEntry.prototype.fromTX = function fromTX(tx, height) { var data = tx.getPriority(height); var dependencies = false; var size = tx.getVirtualSize(); @@ -1911,19 +1947,30 @@ MempoolEntry.fromTX = function fromTX(tx, height) { } } - return new MempoolEntry({ - tx: tx, - height: height, - size: size, - priority: data.priority, - fee: fee, - chainValue: data.value, - ts: utils.now(), - count: 1, - sizes: size, - fees: fee, - dependencies: dependencies - }); + this.tx = tx; + this.height = height; + this.size = size; + this.priority = data.priority; + this.fee = fee; + this.chainValue = data.value; + this.ts = utils.now(); + this.count = 1; + this.sizes = size; + this.fees = fee; + this.dependencies = dependencies; + + return this; +}; + +/** + * Create a mempool entry from a TX. + * @param {TX} tx + * @param {Number} height - Entry height. + * @returns {MempoolEntry} + */ + +MempoolEntry.fromTX = function fromTX(tx, height) { + return new MempoolEntry().fromTX(tx, height); }; /** @@ -1957,6 +2004,28 @@ MempoolEntry.prototype.toRaw = function toRaw(writer) { return p; }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +MempoolEntry.prototype.fromRaw = function fromRaw(data) { + var p = new BufferReader(data); + this.tx = bcoin.tx.fromRaw(p); + this.height = p.readU32(); + this.size = p.readU32(); + this.priority = p.readDouble(); + this.fee = p.readVarint(); + this.chainValue = p.readVarint(); + this.ts = p.readU32(); + this.count = p.readU32(); + this.sizes = p.readU32(); + this.fees = p.readVarint(); + this.dependencies = p.readU8() === 1; + return this; +}; + /** * Create a mempool entry from serialized data. * @param {Buffer|BufferReader} data @@ -1964,20 +2033,7 @@ MempoolEntry.prototype.toRaw = function toRaw(writer) { */ MempoolEntry.fromRaw = function fromRaw(data) { - var p = new BufferReader(data); - return new MempoolEntry({ - tx: bcoin.tx.fromRaw(p), - height: p.readU32(), - size: p.readU32(), - priority: p.readDouble(), - fee: p.readVarint(), - chainValue: p.readVarint(), - ts: p.readU32(), - count: p.readU32(), - sizes: p.readU32(), - fees: p.readVarint(), - dependencies: p.readU8() === 1 - }); + return new MempoolEntry().fromRaw(data); }; /** diff --git a/lib/bcoin/merkleblock.js b/lib/bcoin/merkleblock.js index e0c29ae9..a4e50771 100644 --- a/lib/bcoin/merkleblock.js +++ b/lib/bcoin/merkleblock.js @@ -11,6 +11,7 @@ var bcoin = require('./env'); var utils = require('./utils'); var assert = utils.assert; var constants = bcoin.protocol.constants; +var DUMMY = new Buffer([0]); /** * Represents a merkle (filtered) block. @@ -26,8 +27,8 @@ function MerkleBlock(options) { bcoin.abstractblock.call(this, options); - this.hashes = null; - this.flags = null; + this.hashes = []; + this.flags = DUMMY; // List of matched TXs this.map = {}; @@ -54,8 +55,11 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) { assert(Array.isArray(options.hashes)); assert(Buffer.isBuffer(options.flags)); - this.hashes = options.hashes; - this.flags = options.flags; + if (options.hashes) + this.hashes = options.hashes; + + if (options.flags) + this.flags = options.flags; return this; }; diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index f262c8fb..dacfbdd5 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -79,15 +79,17 @@ function MTX(options) { this._hash = null; this._whash = null; + this._raw = null; - this._size = null; - this._witnessSize = null; - this._outputValue = null; - this._inputValue = null; + this._size = -1; + this._witnessSize = -1; + this._lastWitnessSize = 0; + + this._outputValue = -1; + this._inputValue = -1; this._hashPrevouts = null; this._hashSequence = null; this._hashOutputs = null; - this._lastWitnessSize = 0; if (options.inputs) { for (i = 0; i < options.inputs.length; i++) diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index e06a047c..59240c7f 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -30,8 +30,8 @@ function Output(options, mutable) { return new Output(options, mutable); this.mutable = false; - this.value = null; - this.script = null; + this.value = 0; + this.script = new bcoin.script(); if (options) this.fromOptions(options, mutable); @@ -51,7 +51,8 @@ Output.prototype.fromOptions = function fromOptions(options, mutable) { this.mutable = !!mutable; this.value = options.value || 0; - this.script = bcoin.script(options.script); + if (options.script) + this.script.fromOptions(options.script); return this; }; @@ -232,7 +233,7 @@ Output.prototype.isDust = function isDust(rate) { Output.prototype.fromJSON = function fromJSON(json) { this.value = utils.satoshi(json.value); - this.script = bcoin.script.fromJSON(json.script); + this.script.fromJSON(json.script); return this; }; @@ -274,7 +275,7 @@ Output.prototype.fromRaw = function fromRaw(data) { var p = bcoin.reader(data); this.value = p.read64N(); - this.script = bcoin.script.fromRaw(p.readVarBytes()); + this.script.fromRaw(p.readVarBytes()); return this; }; diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index a1103440..bd68ad34 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -611,6 +611,25 @@ exports.MAX_HASH = new Buffer( exports.NULL_HASH = '0000000000000000000000000000000000000000000000000000000000000000'; +/** + * A hash of all zeroes. + * @const {Buffer} + * @default + */ + +exports.ZERO_HASH160 = new Buffer( + '0000000000000000000000000000000000000000', + 'hex' +); + +/** + * A hash of all zeroes. + * @const {String} + * @default + */ + +exports.NULL_HASH160 = '0000000000000000000000000000000000000000'; + /** * BCoin version. * @const {String} diff --git a/lib/bcoin/protocol/packets.js b/lib/bcoin/protocol/packets.js index 34a264bb..999dc81a 100644 --- a/lib/bcoin/protocol/packets.js +++ b/lib/bcoin/protocol/packets.js @@ -47,10 +47,10 @@ function VersionPacket(options) { this.version = constants.VERSION; this.services = constants.LOCAL_SERVICES; - this.ts = 0; - this.recv = null; - this.from = null; - this.nonce = null; + this.ts = bcoin.now(); + this.recv = new NetworkAddress(); + this.from = new NetworkAddress(); + this.nonce = new bn(0); this.agent = constants.USER_AGENT; this.height = 0; this.relay = true; @@ -66,21 +66,33 @@ function VersionPacket(options) { */ VersionPacket.prototype.fromOptions = function fromOptions(options) { - this.version = options.version != null - ? options.version - : constants.VERSION; - this.services = options.services != null - ? options.services - : constants.LOCAL_SERVICES; - this.ts = options.ts != null - ? options.ts - : bcoin.now(); - this.recv = options.recv || new NetworkAddress(); - this.from = options.from || new NetworkAddress(); - this.nonce = options.nonce || utils.nonce(); - this.agent = options.agent || constants.USER_AGENT; - this.height = options.height || 0; - this.relay = options.relay != null ? options.relay : true; + if (options.version != null) + this.version = options.version; + + if (options.services != null) + this.services = options.services; + + if (options.ts != null) + this.ts = options.ts; + + if (options.recv) + this.recv.fromOptions(options.recv); + + if (options.from) + this.from.fromOptions(options.from); + + if (options.nonce) + this.nonce = options.nonce; + + if (options.agent) + this.agent = options.agent; + + if (options.height != null) + this.height = options.height; + + if (options.relay != null) + this.relay = options.relay; + return this; }; @@ -176,30 +188,21 @@ VersionPacket.prototype.fromRaw = function fromRaw(data) { this.version = p.read32(); this.services = p.readU53(); this.ts = p.read53(); - this.recv = NetworkAddress.fromRaw(p, false); + this.recv.fromRaw(p, false); if (p.left() > 0) { - this.from = NetworkAddress.fromRaw(p, false); + this.from.fromRaw(p, false); this.nonce = p.readU64(); - } else { - this.from = new NetworkAddress(); - this.nonce = new bn(0); } if (p.left() > 0) this.agent = p.readVarString('ascii', 256); - else - this.agent = ''; if (p.left() > 0) this.height = p.read32(); - else - this.height = 0; if (p.left() > 0) this.relay = p.readU8() === 1; - else - this.relay = true; if (this.version === 10300) this.version = 300; @@ -393,19 +396,23 @@ GetBlocksPacket.fromRaw = function fromRaw(data, enc) { */ function AlertPacket(options) { + var time; + if (!(this instanceof AlertPacket)) return new AlertPacket(options); - this.version = 0; - this.relayUntil = 0; - this.expiration = 0; - this.id = 0; + time = bcoin.now() + 7 * 86400; + + this.version = 1; + this.relayUntil = time; + this.expiration = time; + this.id = 1; this.cancel = 0; this.cancels = []; - this.minVer = 0; + this.minVer = 10000; this.maxVer = constants.VERSION; this.subVers = []; - this.priority = 0; + this.priority = 100; this.comment = ''; this.statusBar = ''; this.reserved = ''; @@ -425,21 +432,45 @@ function AlertPacket(options) { */ AlertPacket.prototype.fromOptions = function fromOptions(options) { - var time = bcoin.now() + 7 * 86400; + if (options.version != null) + this.version = options.version; + + if (options.relayUntil != null) + this.relayUntil = options.relayUntil; + + if (options.expiration != null) + this.expiration = options.expiration; + + if (options.id != null) + this.id = options.id; + + if (options.cancel != null) + this.cancel = options.cancel; + + if (options.cancels) + this.cancels = options.cancels; + + if (options.minVer != null) + this.minVer = options.minVer; + + if (options.maxVer != null) + this.maxVer = options.maxVer; + + if (options.subVers) + this.subVers = options.subVers; + + if (options.priority != null) + this.priority = options.priority; + + if (options.comment != null) + this.comment = options.comment; + + if (options.statusBar != null) + this.statusBar = options.statusBar; + + if (options.reserved != null) + this.reserved = options.reserved; - this.version = options.version != null ? options.version : 1; - this.relayUntil = options.relayUntil != null ? options.relayUntil : time; - this.expiration = options.expiration != null ? options.expiration : time; - this.id = options.id != null ? options.id : 0; - this.cancel = options.cancel != null ? options.cancel : 0; - this.cancels = options.cancels || []; - this.minVer = options.minVer != null ? options.minVer : 10000; - this.maxVer = options.maxVer != null ? options.maxVer : constants.VERSION; - this.subVers = options.subVers || []; - this.priority = options.priority != null ? options.priority : 100; - this.comment = options.comment || ''; - this.statusBar = options.statusBar || ''; - this.reserved = options.reserved || ''; this.signature = options.signature; return this; @@ -639,19 +670,24 @@ function RejectPacket(options) { RejectPacket.prototype.fromOptions = function fromOptions(options) { var code = options.code; - if (typeof code === 'string') - code = constants.reject[code.toUpperCase()]; + if (options.message) + this.message = options.message; - if (!code) - code = constants.reject.INVALID; + if (code != null) { + if (typeof code === 'string') + code = constants.reject[code.toUpperCase()]; - if (code >= constants.reject.INTERNAL) - code = constants.reject.INVALID; + if (code >= constants.reject.INTERNAL) + code = constants.reject.INVALID; - this.message = options.message || ''; - this.code = code; - this.reason = options.reason || ''; - this.data = options.data || null; + this.code = code; + } + + if (options.reason) + this.reason = options.reason; + + if (options.data) + this.data = options.data; return this; }; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 6e03af94..f6144347 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -3587,7 +3587,7 @@ Script.isHashType = function isHashType(sig) { type = sig[sig.length - 1] & ~constants.hashType.ANYONECANPAY; - if (!constants.hashTypeByVal[type]) + if (!(type >= constants.hashType.ALL && type <= constants.hashType.SINGLE)) return false; return true; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index d20ec19a..7c87576c 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -69,12 +69,12 @@ function TX(options) { this._whash = null; this._raw = null; - this._size = null; - this._witnessSize = null; + this._size = -1; + this._witnessSize = -1; this._lastWitnessSize = 0; - this._outputValue = null; - this._inputValue = null; + this._outputValue = -1; + this._inputValue = -1; this._hashPrevouts = null; this._hashSequence = null; this._hashOutputs = null; @@ -93,42 +93,63 @@ TX.prototype.fromOptions = function fromOptions(options) { var i; assert(options, 'TX data is required.'); - assert(utils.isNumber(options.version)); assert(utils.isNumber(options.flag)); assert(Array.isArray(options.inputs)); assert(Array.isArray(options.outputs)); assert(utils.isNumber(options.locktime)); - this.version = options.version; - this.flag = options.flag; + if (options.version != null) { + assert(utils.isNumber(options.version)); + this.version = options.version; + } - for (i = 0; i < options.inputs.length; i++) - this.inputs.push(new bcoin.input(options.inputs[i])); + if (options.flag != null) { + assert(utils.isNumber(options.flag)); + this.flag = options.flag; + } - for (i = 0; i < options.outputs.length; i++) - this.outputs.push(new bcoin.output(options.outputs[i])); + if (options.inputs) { + assert(Array.isArray(options.inputs)); + for (i = 0; i < options.inputs.length; i++) + this.inputs.push(new bcoin.input(options.inputs[i])); + } - this.locktime = options.locktime; + if (options.outputs) { + assert(Array.isArray(options.outputs)); + for (i = 0; i < options.outputs.length; i++) + this.outputs.push(new bcoin.output(options.outputs[i])); + } - this.ts = options.ts || 0; - this.block = options.block || null; - this.index = options.index != null - ? options.index - : -1; - this.ps = this.ts === 0 - ? (options.ps != null ? options.ps : utils.now()) - : 0; - this.height = options.height != null - ? options.height - : -1; + if (options.locktime != null) { + assert(utils.isNumber(options.locktime)); + this.locktime = options.locktime; + } - this._raw = options._raw || null; - this._size = options._size != null - ? options._size - : 0; - this._witnessSize = options._witnessSize != null - ? options._witnessSize - : null; + if (options.ts != null) + assert(utils.isNumber(options.locktime)); + this.ts = options.ts; + + if (options.block !== undefined) { + assert(options.block === null || typeof options.block === 'string'); + this.block = options.block; + } + + if (options.index != null) { + assert(utils.isNumber(options.index)); + this.index = options.index; + } + + if (options.ps != null) { + assert(utils.isNumber(options.ps)); + this.ps = this.ts === 0 + ? options.ps + : 0; + } + + if (options.height != null) { + assert(utils.isNumber(options.height)); + this.height = options.height; + } return this; }; @@ -416,7 +437,7 @@ TX.prototype.getBaseSize = function getBaseSize() { TX.prototype.hasWitness = function hasWitness() { var i; - if (this._witnessSize != null) + if (this._witnessSize !== -1) return this._witnessSize !== 0; for (i = 0; i < this.inputs.length; i++) { @@ -459,10 +480,9 @@ TX.prototype.signatureHash = function signatureHash(index, prev, type, version) * @returns {Buffer} */ -TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { +TX.prototype.signatureHashCopy = function signatureHashV0(index, prev, type) { var p = new BufferWriter(); var empty = new Script(); - var witness = new bcoin.witness(); var i, copy, input, output; if (typeof index !== 'number') @@ -484,7 +504,6 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { input.prevout = this.inputs[i].prevout; input.script = this.inputs[i].script; input.sequence = this.inputs[i].sequence; - input.witness = witness; copy.inputs.push(input); } @@ -553,6 +572,134 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { return utils.hash256(p.render()); }; +/** + * Legacy sighashing -- O(n^2). + * Note: Optimized version. No + * transaction copy necessary. + * @private + * @param {Number} index + * @param {Script} prev + * @param {SighashType} type + * @returns {Buffer} + */ + +TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { + var i, input, output; + var p = new BufferWriter(); + var hashType; + + if (typeof index !== 'number') + index = this.inputs.indexOf(index); + + if (typeof type === 'string') + type = constants.hashType[type.toUpperCase()]; + + assert(index >= 0 && index < this.inputs.length); + assert(prev instanceof Script); + + // Get the unmasked hash type. + hashType = type & 0x1f; + + if (hashType === constants.hashType.SINGLE) { + // Bitcoind used to return 1 as an error code: + // it ended up being treated like a hash. + if (index >= this.outputs.length) + return utils.copy(constants.ONE_HASH); + } + + // Remove all code separators. + prev = prev.removeSeparators(); + + p.write32(this.version); + + if (type & constants.hashType.ANYONECANPAY) { + p.writeVarint(1); + + // Serialize only the current + // input if ANYONECANPAY. + input = this.inputs[index]; + + // Outpoint. + p.writeHash(input.prevout.hash); + p.writeU32(input.prevout.index); + + // Replace script with previous + // output script if current index. + p.writeVarBytes(prev.toRaw()); + p.writeU32(input.sequence); + } else { + p.writeVarint(this.inputs.length); + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + + // Outpoint. + p.writeHash(input.prevout.hash); + p.writeU32(input.prevout.index); + + // Replace script with previous + // output script if current index. + if (i === index) { + p.writeVarBytes(prev.toRaw()); + p.writeU32(input.sequence); + continue; + } + + // Script is null. + p.writeVarint(0); + + // Sequences are 0 if NONE or SINGLE. + if (hashType === constants.hashType.NONE + || hashType === constants.hashType.SINGLE) { + p.writeU32(0); + } else { + p.writeU32(input.sequence); + } + } + } + + if (hashType === constants.hashType.NONE) { + // No outputs if NONE. + p.writeVarint(0); + } else if (hashType === constants.hashType.SINGLE) { + // Drop all outputs after the + // current input index if SINGLE. + p.writeVarint(index + 1); + + for (i = 0; i < index + 1; i++) { + output = this.outputs[i]; + + // Regular serialization if + // at current input index. + if (i === index) { + p.write64(output.value); + p.writeVarBytes(output.script.toRaw()); + continue; + } + + // Null all outputs not at + // current input index. + p.write64(-1); + p.writeVarint(0); + } + } else { + // Regular output serialization if ALL. + p.writeVarint(this.outputs.length); + + for (i = 0; i < this.outputs.length; i++) { + output = this.outputs[i]; + p.write64(output.value); + p.writeVarBytes(output.script.toRaw()); + } + } + + p.writeU32(this.locktime); + + // Append the hash type. + p.writeU32(type); + + return utils.hash256(p.render()); +}; + /** * Witness sighashing -- O(n). * @private @@ -777,7 +924,7 @@ TX.prototype.getInputValue = function getInputValue() { var total = 0; var i; - if (this._inputValue != null) + if (this._inputValue !== -1) return this._inputValue; if (!this.hasCoins()) @@ -801,7 +948,7 @@ TX.prototype.getOutputValue = function getOutputValue() { var total = 0; var i; - if (this._outputValue != null) + if (this._outputValue !== -1) return this._outputValue; for (i = 0; i < this.outputs.length; i++) diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index d059d564..0f0e9449 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -1677,7 +1677,7 @@ function WalletMap() { this.inputs = []; this.outputs = []; this.accounts = []; - this.table = null; + this.table = {}; } WalletMap.prototype.fromTX = function fromTX(table, tx) { diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index db28be04..66d60a10 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -1731,6 +1731,7 @@ utils.readVarint2 = function readVarint2(data, off, big) { } if (bnum) { + assert(bnum.bitLength() <= 256); bnum.iushln(7).iaddn(ch & 0x7f); if ((ch & 0x80) === 0) break; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 127d0e20..a0691ffc 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -1565,15 +1565,15 @@ function Account(db, options) { this.id = null; this.name = null; - this.witness = false; + this.witness = this.network.witness; this.accountKey = null; this.accountIndex = 0; this.receiveDepth = 0; this.changeDepth = 0; - this.type = null; - this.keys = []; + this.type = 'pubkeyhash'; this.m = 1; this.n = 1; + this.keys = []; this.initialized = false; if (options) @@ -1598,18 +1598,32 @@ Account.prototype.fromOptions = function fromOptions(options) { this.id = options.id; this.name = options.name; - this.witness = options.witness != null - ? options.witness - : this.network.witness; + + if (options.witness != null) + this.witness = options.witness; + this.accountKey = options.accountKey; - this.accountIndex = options.accountIndex; - this.receiveDepth = options.receiveDepth || 0; - this.changeDepth = options.changeDepth || 0; - this.type = options.type || 'pubkeyhash'; - this.keys = []; - this.m = options.m || 1; - this.n = options.n || 1; - this.initialized = options.initialized || false; + + if (options.accountIndex != null) + this.accountIndex = options.accountIndex; + + if (options.receiveDepth != null) + this.receiveDepth = options.receiveDepth; + + if (options.changeDepth != null) + this.changeDepth = options.changeDepth; + + if (options.type) + this.type = options.type; + + if (options.m) + this.m = options.m; + + if (options.n) + this.n = options.n; + + if (options.initialized != null) + this.initialized = options.initialized; if (this.n > 1) this.type = 'multisig'; @@ -1673,11 +1687,11 @@ Account.prototype.init = function init(callback) { return this.save(callback); } - this.initialized = true; - assert(this.receiveDepth === 0); assert(this.changeDepth === 0); + this.initialized = true; + this.setDepth(1, 1, callback); }; @@ -2236,7 +2250,8 @@ Account.prototype.toJSON = function toJSON() { Account.prototype.fromJSON = function fromJSON(json) { var i; - this.network = bcoin.network.get(json.network); + assert.equal(json.network, this.network.type); + this.id = json.id; this.name = json.name; this.initialized = json.initialized; diff --git a/test/script-test.js b/test/script-test.js index a69cf557..8be549c7 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -344,23 +344,23 @@ describe('Script', function() { }); if (nocache) { tx._raw = null; - tx._size = null; - tx._witnessSize = null; + tx._size = -1; + tx._witnessSize = -1; tx._lastWitnessSize = 0; tx._hash = null; - tx._inputValue = null; - tx._outputValue = null; + tx._inputValue = -1; + tx._outputValue = -1; tx._hashPrevouts = null; tx._hashSequence = null; tx._hashOutputs = null; coin._raw = null; - coin._size = null; - coin._witnessSize = null; + coin._size = -1; + coin._witnessSize = -1; coin._lastWitnessSize = 0; coin._hash = null; - coin._inputValue = null; - coin._outputValue = null; + coin._inputValue = -1; + coin._outputValue = -1; coin._hashPrevouts = null; coin._hashSequence = null; coin._hashOutputs = null; diff --git a/test/tx-test.js b/test/tx-test.js index 063b3d98..a8635943 100644 --- a/test/tx-test.js +++ b/test/tx-test.js @@ -48,12 +48,12 @@ function clearCache(tx, nocache) { } tx._raw = null; - tx._size = null; - tx._witnessSize = null; + tx._size = -1; + tx._witnessSize = -1; tx._lastWitnessSize = 0; tx._hash = null; - tx._inputValue = null; - tx._outputValue = null; + tx._inputValue = -1; + tx._outputValue = -1; tx._hashPrevouts = null; tx._hashSequence = null; tx._hashOutputs = null;