diff --git a/.eslintrc.json b/.eslintrc.json index 565748a3..80a4f000 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,16 +1,17 @@ { - "extends": "eslint:recommended", "env": { "browser": true, "es6": true, - "node": true, - "mocha": true + "mocha": true, + "node": true }, + "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 8 }, "rules": { - "strict": 2, + "consistent-return": "off", + "func-name-matching": "off", "indent": ["error", 2, { "SwitchCase": 1, "CallExpression": { @@ -18,28 +19,37 @@ }, "ArrayExpression": "off" }], + "handle-callback-err": "off", "linebreak-style": ["error", "unix"], - "quotes": ["error", "single"], - "semi": ["error", "always"], - "no-console": 0, + "no-buffer-constructor": "error", + "no-console": "off", + "no-cond-assign": "off", + "no-extra-semi": "off", + "no-fallthrough": "off", + "no-func-assign": "off", + "no-param-reassign": "off", + "no-shadow-restricted-names": "error", + "no-tabs": "error", "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": false }], - "no-func-assign": 0, - "no-cond-assign": 0, - "no-unreachable": 0, - "no-fallthrough": 0, - "no-useless-escape": 0, - "no-unsafe-finally": 0, - "no-extra-semi": 0, - "handle-callback-err": 0, - "no-buffer-constructor": 2, - "no-tabs": 2, + "no-use-before-define": ["error", { + "functions": false, + "classes": false + }], + "no-unreachable": "off", + "no-useless-escape": "off", + "no-unsafe-finally": "off", + "no-var": "error", "prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true - }] + }], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "strict": "error", + "valid-jsdoc": "error" } } diff --git a/bench/tx.js b/bench/tx.js index 07614609..af9db303 100644 --- a/bench/tx.js +++ b/bench/tx.js @@ -19,6 +19,7 @@ const btx = { tx: block.txs[397], view: new CoinView() }; const tx3 = parseTX('../test/data/tx3.hex'); const hex = fs.readFileSync(`${__dirname}/../test/data/wtx.hex`, 'utf8'); const raw = Buffer.from(hex.trim(), 'hex'); +const tx = TX.fromRaw(raw); { const tx = json.txs[397]; @@ -153,25 +154,25 @@ for (let i = 0; i < 100; i++) { }); } -const tx = mtx.toTX(); +const tx2 = mtx.toTX(); { const end = bench('input hashes'); for (let i = 0; i < 1000; i++) - tx.getInputHashes(null, 'hex'); + tx2.getInputHashes(null, 'hex'); end(1000); } { const end = bench('output hashes'); for (let i = 0; i < 1000; i++) - tx.getOutputHashes('hex'); + tx2.getOutputHashes('hex'); end(1000); } { const end = bench('all hashes'); for (let i = 0; i < 1000; i++) - tx.getHashes(null, 'hex'); + tx2.getHashes(null, 'hex'); end(1000); } diff --git a/bench/walletdb.js b/bench/walletdb.js index 9a134a62..226024c8 100644 --- a/bench/walletdb.js +++ b/bench/walletdb.js @@ -24,6 +24,7 @@ const walletdb = new WalletDB({ const wallet = await walletdb.create(); const addrs = []; + let tx; // Accounts { @@ -65,7 +66,7 @@ const walletdb = new WalletDB({ mtx.addOutput(addrs[(i + 1) % addrs.length], 50460); mtx.addOutput(addrs[(i + 2) % addrs.length], 50460); mtx.addOutput(addrs[(i + 3) % addrs.length], 50460); - const tx = mtx.toTX(); + tx = mtx.toTX(); jobs.push(walletdb.addTX(tx)); } @@ -88,7 +89,7 @@ const walletdb = new WalletDB({ mtx.addOutput(addrs[(i + 1) % addrs.length], 50460); mtx.addOutput(addrs[(i + 2) % addrs.length], 50460); mtx.addOutput(addrs[(i + 3) % addrs.length], 50460); - const tx = mtx.toTX(); + tx = mtx.toTX(); jobs.push(walletdb.addTX(tx)); } diff --git a/bin/node b/bin/node index e3b15645..3ed9a5ee 100755 --- a/bin/node +++ b/bin/node @@ -19,10 +19,9 @@ if (process.argv.indexOf('--version') !== -1 throw new Error('Could not exit.'); } -const bcoin = require('../'); -const plugin = require('../lib/wallet/plugin'); +const FullNode = require('../lib/node/fullnode'); -const node = new bcoin.fullnode({ +const node = new FullNode({ config: true, argv: true, env: true, @@ -37,8 +36,10 @@ const node = new bcoin.fullnode({ }); // Temporary hack -if (!node.config.bool('no-wallet') && !node.has('walletdb')) +if (!node.config.bool('no-wallet') && !node.has('walletdb')) { + const plugin = require('../lib/wallet/plugin'); node.use(plugin); +} process.on('unhandledRejection', (err, promise) => { throw err; diff --git a/bin/spvnode b/bin/spvnode index d10e981a..71fae73e 100755 --- a/bin/spvnode +++ b/bin/spvnode @@ -5,11 +5,11 @@ process.title = 'bcoin'; const assert = require('assert'); -const bcoin = require('../'); -const plugin = require('../lib/wallet/plugin'); -const util = bcoin.util; +const SPVNode = require('../lib/node/spvnode'); +const util = require('../lib/utils/util'); +const Outpoint = require('../lib/primitives/outpoint'); -const node = bcoin.spvnode({ +const node = SPVNode({ config: true, argv: true, env: true, @@ -24,8 +24,10 @@ const node = bcoin.spvnode({ }); // Temporary hack -if (!node.has('walletdb')) +if (!node.has('walletdb')) { + const plugin = require('../lib/wallet/plugin'); node.use(plugin); +} process.on('unhandledRejection', (err, promise) => { throw err; @@ -38,7 +40,7 @@ process.on('unhandledRejection', (err, promise) => { if (node.config.bool('test')) { node.pool.watchAddress('1VayNert3x1KzbpzMGt2qdqrAThiRovi8'); - node.pool.watchOutpoint(new bcoin.outpoint()); + node.pool.watchOutpoint(new Outpoint()); node.on('block', (block) => { assert(block.txs.length >= 1); if (block.txs.length > 1) diff --git a/lib/bip70/paymentdetails.js b/lib/bip70/paymentdetails.js index 746ca5b8..10107e34 100644 --- a/lib/bip70/paymentdetails.js +++ b/lib/bip70/paymentdetails.js @@ -141,7 +141,7 @@ PaymentDetails.prototype.getData = function getData(enc) { let data = this.merchantData; if (!data) - return; + return null; if (!enc) return data; diff --git a/lib/bip70/x509.js b/lib/bip70/x509.js index bf9f66d2..87bf3507 100644 --- a/lib/bip70/x509.js +++ b/lib/bip70/x509.js @@ -92,6 +92,8 @@ x509.getSubjectOID = function getSubjectOID(cert, oid) { if (entry.type === oid) return entry.value; } + + return null; }; /** diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 0607b551..3a4647fd 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -350,7 +350,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) { } // Check the commitment hash for segwit. - let commit = null; + let commit; if (state.hasWitness()) { commit = block.getCommitmentHash(); if (commit) { @@ -581,11 +581,12 @@ Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev, */ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) { + const view = new CoinView(); + if (this.options.spv) return view; const interval = this.network.halvingInterval; - const view = new CoinView(); const height = prev.height + 1; const historical = prev.isHistorical(); const jobs = []; @@ -1598,7 +1599,7 @@ Chain.prototype.resolveOrphan = function resolveOrphan(hash) { const orphan = this.orphanPrev.get(hash); if (!orphan) - return; + return null; return this.removeOrphan(orphan); }; @@ -1626,8 +1627,8 @@ Chain.prototype.purgeOrphans = function purgeOrphans() { Chain.prototype.limitOrphans = function limitOrphans() { const now = util.now(); - let oldest = null; + let oldest; for (const orphan of this.orphanMap.values()) { if (now < orphan.time + 60 * 60) { if (!oldest || orphan.time < oldest.time) @@ -1896,9 +1897,9 @@ Chain.prototype._getLocator = async function getLocator(start) { hash = await this.db.getHash(height); assert(hash); } else { - const entry = await entry.getAncestor(height); - assert(entry); - hash = entry.hash; + const ancestor = await entry.getAncestor(height); + assert(ancestor); + hash = ancestor.hash; } hashes.push(hash); diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index f74b6100..8dc842fb 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -309,7 +309,7 @@ ChainDB.prototype.getHash = async function getHash(height) { assert(typeof height === 'number'); if (height < 0) - return; + return null; const entry = this.cacheHeight.get(height); @@ -319,7 +319,7 @@ ChainDB.prototype.getHash = async function getHash(height) { const hash = await this.db.get(layout.H(height)); if (!hash) - return; + return null; return hash.toString('hex'); }; @@ -335,26 +335,25 @@ ChainDB.prototype.getEntryByHeight = async function getEntryByHeight(height) { assert(typeof height === 'number'); if (height < 0) - return; + return null; const cache = this.cacheHeight.get(height); if (cache) return cache; - let hash = await this.db.get(layout.H(height)); + const data = await this.db.get(layout.H(height)); - if (!hash) - return; + if (!data) + return null; - hash = hash.toString('hex'); + const hash = data.toString('hex'); const state = this.chain.state; - const entry = await this.getEntryByHash(hash); if (!entry) - return; + return null; // By the time getEntry has completed, // a reorg may have occurred. This entry @@ -376,7 +375,7 @@ ChainDB.prototype.getEntryByHash = async function getEntryByHash(hash) { assert(typeof hash === 'string'); if (hash === encoding.NULL_HASH) - return; + return null; const cache = this.cacheHash.get(hash); @@ -386,7 +385,7 @@ ChainDB.prototype.getEntryByHash = async function getEntryByHash(hash) { const raw = await this.db.get(layout.e(hash)); if (!raw) - return; + return null; const entry = ChainEntry.fromRaw(this.chain, raw); @@ -441,7 +440,7 @@ ChainDB.prototype.getState = async function getState() { const data = await this.db.get(layout.R); if (!data) - return; + return null; return ChainState.fromRaw(data); }; @@ -472,7 +471,7 @@ ChainDB.prototype.getFlags = async function getFlags() { const data = await this.db.get(layout.O); if (!data) - return; + return null; return ChainFlags.fromRaw(data); }; @@ -717,10 +716,11 @@ ChainDB.prototype.prune = async function prune(tip) { if (flags.prune) throw new Error('Chain is already pruned.'); + const height = await this.getHeight(tip); + if (height <= pruneAfter + keepBlocks) return false; - const height = await this.getHeight(tip); const start = pruneAfter + 1; const end = height - keepBlocks; const batch = this.db.batch(); @@ -765,7 +765,7 @@ ChainDB.prototype.getNextHash = async function getNextHash(hash) { const data = await this.db.get(layout.n(hash)); if (!data) - return; + return null; return data.toString('hex'); }; @@ -788,12 +788,12 @@ ChainDB.prototype.isMainChain = async function isMainChain(hash) { if (hash === encoding.NULL_HASH) return false; - let entry = this.cacheHash.get(hash); + const cacheHash = this.cacheHash.get(hash); - if (entry) { - entry = this.cacheHeight.get(entry.height); - if (entry) - return entry.hash === hash; + if (cacheHash) { + const cacheHeight = this.cacheHeight.get(cacheHash.height); + if (cacheHeight) + return cacheHeight.hash === hash; } if (await this.getNextHash(hash)) @@ -838,7 +838,7 @@ ChainDB.prototype.getTips = function getTips() { ChainDB.prototype.readCoin = async function readCoin(prevout) { if (this.options.spv) - return; + return null; const {hash, index} = prevout; const key = prevout.toKey(); @@ -852,7 +852,7 @@ ChainDB.prototype.readCoin = async function readCoin(prevout) { const raw = await this.db.get(layout.c(hash, index)); if (!raw) - return; + return null; if (state === this.state) this.coinCache.set(key, raw); @@ -873,7 +873,7 @@ ChainDB.prototype.getCoin = async function getCoin(hash, index) { const coin = await this.readCoin(prevout); if (!coin) - return; + return null; return coin.toCoin(prevout); }; @@ -971,7 +971,7 @@ ChainDB.prototype.getBlock = async function getBlock(hash) { const data = await this.getRawBlock(hash); if (!data) - return; + return null; return Block.fromRaw(data); }; @@ -985,12 +985,12 @@ ChainDB.prototype.getBlock = async function getBlock(hash) { ChainDB.prototype.getRawBlock = async function getRawBlock(block) { if (this.options.spv) - return; + return null; const hash = await this.getHash(block); if (!hash) - return; + return null; return await this.db.get(layout.b(hash)); }; @@ -1033,12 +1033,12 @@ ChainDB.prototype.getBlockView = async function getBlockView(block) { ChainDB.prototype.getMeta = async function getMeta(hash) { if (!this.options.indexTX) - return; + return null; const data = await this.db.get(layout.t(hash)); if (!data) - return; + return null; return TXMeta.fromRaw(data); }; @@ -1052,8 +1052,10 @@ ChainDB.prototype.getMeta = async function getMeta(hash) { ChainDB.prototype.getTX = async function getTX(hash) { const meta = await this.getMeta(hash); + if (!meta) - return; + return null; + return meta.tx; }; @@ -1064,7 +1066,7 @@ ChainDB.prototype.getTX = async function getTX(hash) { ChainDB.prototype.hasTX = function hasTX(hash) { if (!this.options.indexTX) - return Promise.resolve(); + return Promise.resolve(false); return this.db.has(layout.t(hash)); }; @@ -1077,14 +1079,14 @@ ChainDB.prototype.hasTX = function hasTX(hash) { */ ChainDB.prototype.getCoinsByAddress = async function getCoinsByAddress(addrs) { - const coins = []; - if (!this.options.indexAddress) - return coins; + return []; if (!Array.isArray(addrs)) addrs = [addrs]; + const coins = []; + for (const addr of addrs) { const hash = Address.getHash(addr); @@ -1112,11 +1114,11 @@ ChainDB.prototype.getCoinsByAddress = async function getCoinsByAddress(addrs) { */ ChainDB.prototype.getHashesByAddress = async function getHashesByAddress(addrs) { - const hashes = Object.create(null); - if (!this.options.indexTX || !this.options.indexAddress) return []; + const hashes = Object.create(null); + for (const addr of addrs) { const hash = Address.getHash(addr); @@ -1158,15 +1160,14 @@ ChainDB.prototype.getTXByAddress = async function getTXByAddress(addrs) { */ ChainDB.prototype.getMetaByAddress = async function getTXByAddress(addrs) { - const txs = []; - if (!this.options.indexTX || !this.options.indexAddress) - return txs; + return []; if (!Array.isArray(addrs)) addrs = [addrs]; const hashes = await this.getHashesByAddress(addrs); + const txs = []; for (const hash of hashes) { const tx = await this.getMeta(hash); @@ -1638,7 +1639,7 @@ ChainDB.prototype.saveBlock = async function saveBlock(entry, block, view) { ChainDB.prototype.removeBlock = async function removeBlock(entry) { if (this.options.spv) - return; + return new CoinView(); const block = await this.getBlock(entry.hash); @@ -1734,10 +1735,11 @@ ChainDB.prototype.connectBlock = async function connectBlock(entry, block, view) */ ChainDB.prototype.disconnectBlock = async function disconnectBlock(entry, block) { + const view = new CoinView(); + if (this.options.spv) return view; - const view = new CoinView(); const hash = block.hash(); const undo = await this.getUndoCoins(hash); diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index e64dca74..3425e866 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -130,8 +130,10 @@ ChainEntry.fromOptions = function fromOptions(chain, options, prev) { ChainEntry.prototype.getProof = function getProof() { const target = consensus.fromCompact(this.bits); + if (target.isNeg() || target.cmpn(0) === 0) return new BN(0); + return ChainEntry.MAX_CHAINWORK.div(target.iaddn(1)); }; @@ -194,7 +196,7 @@ ChainEntry.prototype.isMainChain = async function isMainChain() { ChainEntry.prototype.getAncestor = async function getAncestor(height) { if (height < 0) - return; + return null; assert(height >= 0); assert(height <= this.height); @@ -237,8 +239,10 @@ ChainEntry.prototype.getPrevCache = function getPrevCache() { ChainEntry.prototype.getNext = async function getNext() { const hash = await this.chain.db.getNextHash(this.hash); + if (!hash) - return; + return null; + return await this.chain.db.getEntry(hash); }; @@ -252,11 +256,11 @@ ChainEntry.prototype.getNextEntry = async function getNextEntry() { const entry = await this.chain.db.getEntry(this.height + 1); if (!entry) - return; + return null; // Not on main chain. if (entry.prevBlock !== this.hash) - return; + return null; return entry; }; diff --git a/lib/blockchain/layout-browser.js b/lib/blockchain/layout-browser.js index 03b3fd49..e26b4082 100644 --- a/lib/blockchain/layout-browser.js +++ b/lib/blockchain/layout-browser.js @@ -75,10 +75,9 @@ const layout = { return key.slice(1, 65); }, Cc: function Cc(key) { - let hash, index; - assert(typeof key === 'string'); + let hash, index; if (key.length === 139) { hash = key.slice(65, 129); index = +key.slice(129); diff --git a/lib/blockchain/layout.js b/lib/blockchain/layout.js index c3653121..677b2fe3 100644 --- a/lib/blockchain/layout.js +++ b/lib/blockchain/layout.js @@ -76,11 +76,11 @@ const layout = { }, T: function T(addr, hash) { let len = addr.length; - let key; if (typeof addr === 'string') len /= 2; + let key; if (len === 32) { key = Buffer.allocUnsafe(65); key[0] = 0xab; // W + T @@ -99,13 +99,13 @@ const layout = { }, C: function C(addr, hash, index) { let len = addr.length; - let key; assert(typeof index === 'number'); if (typeof addr === 'string') len /= 2; + let key; if (len === 32) { key = Buffer.allocUnsafe(69); key[0] = 0x9a; // W + C @@ -130,10 +130,9 @@ const layout = { return key.toString('hex', 1, 33); }, Cc: function Cc(key) { - let hash, index; - assert(Buffer.isBuffer(key)); + let hash, index; if (key.length === 69) { hash = key.toString('hex', 33, 65); index = key.readUInt32BE(65, 0); @@ -165,7 +164,7 @@ function write(data, str, off) { if (Buffer.isBuffer(str)) return str.copy(data, off); assert(typeof str === 'string'); - data.write(str, off, 'hex'); + return data.write(str, off, 'hex'); } function pair(prefix, hash) { diff --git a/lib/coins/compress.js b/lib/coins/compress.js index 74ed2029..108e6e8a 100644 --- a/lib/coins/compress.js +++ b/lib/coins/compress.js @@ -334,6 +334,10 @@ function decompressKey(key) { return out; } +// Make eslint happy. +compressValue; +decompressValue; + /* * Expose */ diff --git a/lib/crypto/aead.js b/lib/crypto/aead.js index 4295252d..84ee6b57 100644 --- a/lib/crypto/aead.js +++ b/lib/crypto/aead.js @@ -123,8 +123,7 @@ AEAD.prototype.auth = function auth(data) { AEAD.prototype.finish = function finish() { const len = Buffer.allocUnsafe(16); - let lo = 0; - let hi = 0; + let lo, hi; // The RFC says these are supposed to be // uint32le, but their own fucking test diff --git a/lib/crypto/aes-browser.js b/lib/crypto/aes-browser.js index 34060170..96b357be 100644 --- a/lib/crypto/aes-browser.js +++ b/lib/crypto/aes-browser.js @@ -8,6 +8,8 @@ * Entered into the public domain by Vincent Rijmen. */ +/* eslint no-use-before-define: "off" */ + 'use strict'; const assert = require('assert'); diff --git a/lib/crypto/ccmp.js b/lib/crypto/ccmp.js index 45c9eef7..dfc4ced1 100644 --- a/lib/crypto/ccmp.js +++ b/lib/crypto/ccmp.js @@ -6,6 +6,8 @@ 'use strict'; +const assert = require('assert'); + /** * memcmp in constant time (can only return true or false). * This protects us against timing attacks when @@ -19,11 +21,8 @@ */ module.exports = function ccmp(a, b) { - if (!Buffer.isBuffer(a)) - return false; - - if (!Buffer.isBuffer(b)) - return false; + assert(Buffer.isBuffer(a)); + assert(Buffer.isBuffer(b)); if (b.length === 0) return a.length === 0; diff --git a/lib/crypto/hmac-drbg.js b/lib/crypto/hmac-drbg.js index ead618c3..55980794 100644 --- a/lib/crypto/hmac-drbg.js +++ b/lib/crypto/hmac-drbg.js @@ -95,12 +95,12 @@ HmacDRBG.prototype.update = function update(seed) { }; HmacDRBG.prototype.generate = function generate(len) { - const data = Buffer.allocUnsafe(len); - let pos = 0; - if (this.rounds > RESEED_INTERVAL) throw new Error('Reseed is required.'); + const data = Buffer.allocUnsafe(len); + let pos = 0; + while (pos < len) { this.V = digest.hmac(HASH_ALG, this.V, this.K); this.V.copy(data, pos); diff --git a/lib/crypto/poly1305.js b/lib/crypto/poly1305.js index 33e7f96f..50ecdda9 100644 --- a/lib/crypto/poly1305.js +++ b/lib/crypto/poly1305.js @@ -155,15 +155,21 @@ Poly1305.prototype.update = function update(data) { // handle leftover if (this.leftover) { let want = 16 - this.leftover; + if (want > bytes) want = bytes; + for (let i = 0; i < want; i++) this.buffer[this.leftover + i] = data[m + i]; + bytes -= want; m += want; + this.leftover += want; + if (this.leftover < 16) return; + this.blocks(this.buffer, 16, 0); this.leftover = 0; } @@ -262,8 +268,10 @@ Poly1305.prototype.finish = function finish() { // zero out the state for (let i = 0; i < 10; i++) this.h[i] = 0; + for (let i = 0; i < 10; i++) this.r[i] = 0; + for (let i = 0; i < 8; i++) this.pad[i] = 0; diff --git a/lib/crypto/schnorr.js b/lib/crypto/schnorr.js index 75529c7d..f10657d8 100644 --- a/lib/crypto/schnorr.js +++ b/lib/crypto/schnorr.js @@ -101,12 +101,12 @@ schnorr.sign = function sign(msg, key, pubNonce) { const prv = new BN(key); const drbg = schnorr.drbg(msg, key, pubNonce); const len = curve.n.byteLength(); - let pn = null; - let sig = null; + let pn; if (pubNonce) pn = curve.decodePoint(pubNonce); + let sig; while (!sig) { const k = new BN(drbg.generate(len)); sig = schnorr.trySign(msg, prv, k, pn); @@ -210,8 +210,7 @@ schnorr.recover = function recover(signature, msg) { schnorr.combineSigs = function combineSigs(sigs) { let s = new BN(0); - let r = null; - let last = null; + let r, last; for (let i = 0; i < sigs.length; i++) { const sig = new Signature(sigs[i]); @@ -325,8 +324,8 @@ schnorr.drbg = function drbg(msg, priv, data) { schnorr.generateNoncePair = function generateNoncePair(msg, priv, data) { const drbg = schnorr.drbg(msg, priv, data); const len = curve.n.byteLength(); - let k; + let k; for (;;) { k = new BN(drbg.generate(len)); diff --git a/lib/crypto/secp256k1-browser.js b/lib/crypto/secp256k1-browser.js index b1e3908c..023e2953 100644 --- a/lib/crypto/secp256k1-browser.js +++ b/lib/crypto/secp256k1-browser.js @@ -146,7 +146,7 @@ ec.recover = function recover(msg, sig, j, compress) { try { point = secp256k1.recoverPubKey(msg, sig, j); } catch (e) { - return; + return null; } return Buffer.from(point.encode('array', compress)); @@ -191,7 +191,8 @@ ec.verify = function verify(msg, sig, key) { ec.publicKeyVerify = function publicKeyVerify(key) { try { - return secp256k1.keyPair({ pub: key }).validate(); + const pub = secp256k1.keyPair({ pub: key }); + return pub.validate(); } catch (e) { return false; } @@ -324,13 +325,14 @@ function normalizeLength(sig) { function getLength(buf, p) { const initial = buf[p.place++]; - const len = initial & 0xf; - let off = p.place; - let val = 0; if (!(initial & 0x80)) return initial; + const len = initial & 0xf; + let off = p.place; + let val = 0; + for (let i = 0; i < len; i++, off++) { val <<= 8; val |= buf[off]; diff --git a/lib/crypto/secp256k1-native.js b/lib/crypto/secp256k1-native.js index b8340155..139032a5 100644 --- a/lib/crypto/secp256k1-native.js +++ b/lib/crypto/secp256k1-native.js @@ -127,13 +127,13 @@ ec.recover = function recover(msg, sig, j, compress) { try { sig = secp256k1.signatureImport(sig); } catch (e) { - return; + return null; } try { key = secp256k1.recover(msg, sig, j, compress); } catch (e) { - return; + return null; } return key; @@ -195,13 +195,11 @@ ec.privateKeyVerify = function privateKeyVerify(key) { */ ec.sign = function sign(msg, key) { - let sig; - assert(Buffer.isBuffer(msg)); assert(Buffer.isBuffer(key)); // Sign message - sig = secp256k1.sign(msg, key); + let sig = secp256k1.sign(msg, key); // Ensure low S value sig = secp256k1.signatureNormalize(sig.signature); @@ -239,10 +237,10 @@ ec.toDER = function toDER(sig) { */ ec.isLowS = function isLowS(sig) { - let rs, s; + let s; try { - rs = secp256k1.signatureImport(sig); + const rs = secp256k1.signatureImport(sig); s = rs.slice(32, 64); } catch (e) { return false; diff --git a/lib/crypto/sha256.js b/lib/crypto/sha256.js index 0dc4c202..5262faca 100644 --- a/lib/crypto/sha256.js +++ b/lib/crypto/sha256.js @@ -167,6 +167,7 @@ SHA256.prototype._finish = function _finish(out) { */ SHA256.prototype.transform = function transform(chunk, pos) { + const w = this.w; let a = this.s[0]; let b = this.s[1]; let c = this.s[2]; @@ -175,7 +176,6 @@ SHA256.prototype.transform = function transform(chunk, pos) { let f = this.s[5]; let g = this.s[6]; let h = this.s[7]; - const w = this.w; let i = 0; for (; i < 16; i++) @@ -185,13 +185,11 @@ SHA256.prototype.transform = function transform(chunk, pos) { w[i] = sigma1(w[i - 2]) + w[i - 7] + sigma0(w[i - 15]) + w[i - 16]; for (i = 0; i < 64; i++) { - let t1, t2; - - t1 = h + Sigma1(e); + let t1 = h + Sigma1(e); t1 += Ch(e, f, g); t1 += K[i] + w[i]; - t2 = Sigma0(a); + let t2 = Sigma0(a); t2 += Maj(a, b, c); h = g; diff --git a/lib/crypto/siphash.js b/lib/crypto/siphash.js index 23b40328..08b5c4b3 100644 --- a/lib/crypto/siphash.js +++ b/lib/crypto/siphash.js @@ -33,7 +33,6 @@ function siphash24(data, key, shift) { const f1 = U64(0, 0xff); const k0 = U64.fromRaw(key, 0); const k1 = U64.fromRaw(key, 8); - let p = 0; // Init const v0 = c0.ixor(k0); @@ -42,6 +41,7 @@ function siphash24(data, key, shift) { const v3 = c3.ixor(k1); // Blocks + let p = 0; for (let i = 0; i < blocks; i++) { const d = U64.fromRaw(data, p); p += 8; diff --git a/lib/db/lowlevelup.js b/lib/db/lowlevelup.js index 97dda37e..8c3ec862 100644 --- a/lib/db/lowlevelup.js +++ b/lib/db/lowlevelup.js @@ -271,7 +271,7 @@ LowlevelUp.prototype.get = function get(key) { this.binding.get(key, (err, result) => { if (err) { if (isNotFound(err)) { - resolve(); + resolve(null); return; } reject(err); diff --git a/lib/db/memdb.js b/lib/db/memdb.js index 327a9b24..0c8a36b6 100644 --- a/lib/db/memdb.js +++ b/lib/db/memdb.js @@ -45,7 +45,7 @@ MemDB.prototype.search = function search(key) { const node = this.tree.search(key); if (!node) - return; + return undefined; return node.value; }; @@ -449,13 +449,13 @@ Iterator.prototype.init = function init() { Iterator.prototype.next = function next(callback) { const options = this.options; const iter = this.iter; - let key, value, result; if (!this.iter) { setImmediate(() => callback(new Error('Cannot call next.'))); return; } + let result; if (options.reverse) { result = iter.prev(); @@ -499,8 +499,8 @@ Iterator.prototype.next = function next(callback) { this.total += 1; } - key = iter.key; - value = iter.value; + let key = iter.key; + let value = iter.value; if (!options.keys) key = DUMMY; diff --git a/lib/hd/common.js b/lib/hd/common.js index 97f98e5f..038639cc 100644 --- a/lib/hd/common.js +++ b/lib/hd/common.js @@ -65,13 +65,12 @@ common.cache = new LRU(500); */ common.parsePath = function parsePath(path, max) { - const parts = path.split('/'); - const root = parts.shift(); - const result = []; - if (max == null) max = common.MAX_INDEX; + const parts = path.split('/'); + const root = parts.shift(); + if (root !== 'm' && root !== 'M' && root !== 'm\'' @@ -79,6 +78,8 @@ common.parsePath = function parsePath(path, max) { throw new Error('Bad path root.'); } + const result = []; + for (let index of parts) { const hardened = index[index.length - 1] === '\''; diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index f0a4bf12..a086cac1 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -177,11 +177,6 @@ Mnemonic.prototype.getPhrase = function getPhrase() { if (this.phrase) return this.phrase; - let phrase = []; - const wordlist = Mnemonic.getWordlist(this.language); - - const ent = this.getEntropy(); - // Include the first `ENT / 32` bits // of the hash (the checksum). const bits = this.bits + (this.bits / 32); @@ -189,12 +184,16 @@ Mnemonic.prototype.getPhrase = function getPhrase() { // Append the hash to the entropy to // make things easy when grabbing // the checksum bits. + const ent = this.getEntropy(); const entropy = Buffer.allocUnsafe(Math.ceil(bits / 8)); ent.copy(entropy, 0); digest.sha256(ent).copy(entropy, ent.length); // Build the mnemonic by reading // 11 bit indexes from the entropy. + const wordlist = Mnemonic.getWordlist(this.language); + + let phrase = []; for (let i = 0; i < bits / 11; i++) { let index = 0; for (let j = 0; j < 11; j++) { diff --git a/lib/hd/private.js b/lib/hd/private.js index 91b682f2..f1e48755 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -413,11 +413,9 @@ HDPrivateKey.prototype.equal = function equal(obj) { */ HDPrivateKey.prototype.compare = function compare(key) { - let cmp; - assert(HDPrivateKey.isHDPrivateKey(key)); - cmp = this.depth - key.depth; + let cmp = this.depth - key.depth; if (cmp !== 0) return cmp; diff --git a/lib/hd/public.js b/lib/hd/public.js index f2ae4b4c..33b014a0 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -335,11 +335,9 @@ HDPublicKey.prototype.equal = function equal(obj) { */ HDPublicKey.prototype.compare = function compare(key) { - let cmp; - assert(HDPublicKey.isHDPublicKey(key)); - cmp = this.depth - key.depth; + let cmp = this.depth - key.depth; if (cmp !== 0) return cmp; diff --git a/lib/http/base.js b/lib/http/base.js index fdda42b6..b5532e40 100644 --- a/lib/http/base.js +++ b/lib/http/base.js @@ -186,16 +186,22 @@ HTTPBase.prototype.basicAuth = function basicAuth(options) { let realm = options.realm; if (user) { - if (typeof user === 'string') + if (typeof user === 'string') { + assert(user.length <= 255, 'Username too long.'); user = Buffer.from(user, 'utf8'); + } assert(Buffer.isBuffer(user)); + assert(user.length <= 255, 'Username too long.'); user = digest.hash256(user); } - if (typeof pass === 'string') + if (typeof pass === 'string') { + assert(pass.length <= 255, 'Password too long.'); pass = Buffer.from(pass, 'utf8'); + } assert(Buffer.isBuffer(pass)); + assert(pass.length <= 255, 'Password too long.'); pass = digest.hash256(pass); if (!realm) @@ -215,6 +221,9 @@ HTTPBase.prototype.basicAuth = function basicAuth(options) { if (!hdr) return fail(res); + if (hdr.length > 1000) + return fail(res); + const parts = hdr.split(' '); if (parts.length !== 2) @@ -230,6 +239,9 @@ HTTPBase.prototype.basicAuth = function basicAuth(options) { const password = items.join(':'); if (user) { + if (username.length > 255) + return fail(res); + const raw = Buffer.from(username, 'utf8'); const hash = digest.hash256(raw); @@ -237,6 +249,9 @@ HTTPBase.prototype.basicAuth = function basicAuth(options) { return fail(res); } + if (password.length > 255) + return fail(res); + const raw = Buffer.from(password, 'utf8'); const hash = digest.hash256(raw); @@ -345,7 +360,9 @@ HTTPBase.prototype._readBody = function _readBody(req, enc, options, resolve, re reject(new Error('Request body timed out.')); }, options.timeout); - let cleanup = () => { + /* eslint no-use-before-define: "off" */ + + const cleanup = () => { req.removeListener('data', onData); req.removeListener('error', onError); req.removeListener('end', onEnd); @@ -356,7 +373,7 @@ HTTPBase.prototype._readBody = function _readBody(req, enc, options, resolve, re } }; - let onData = (data) => { + const onData = (data) => { total += data.length; hasData = true; @@ -368,12 +385,12 @@ HTTPBase.prototype._readBody = function _readBody(req, enc, options, resolve, re body += decode.write(data); }; - let onError = (err) => { + const onError = (err) => { cleanup(); reject(err); }; - let onEnd = () => { + const onEnd = () => { cleanup(); if (hasData) { @@ -493,11 +510,10 @@ HTTPBase.prototype.handleHooks = async function handleHooks(req, res) { */ HTTPBase.prototype._initSockets = function _initSockets() { - let IOServer; - if (!this.config.sockets) return; + let IOServer; try { IOServer = require('socket.io'); } catch (e) { @@ -608,12 +624,13 @@ HTTPBase.prototype.removeSocket = function removeSocket(socket) { */ HTTPBase.prototype.joinChannel = function joinChannel(socket, name) { - let list = this.channels.get(name); let item = socket.channels.get(name); if (item) return; + let list = this.channels.get(name); + if (!list) { list = new List(); this.channels.set(name, list); @@ -633,12 +650,13 @@ HTTPBase.prototype.joinChannel = function joinChannel(socket, name) { */ HTTPBase.prototype.leaveChannel = function leaveChannel(socket, name) { - const list = this.channels.get(name); const item = socket.channels.get(name); if (!item) return; + const list = this.channels.get(name); + assert(list); assert(list.remove(item)); @@ -658,7 +676,7 @@ HTTPBase.prototype.channel = function channel(name) { const list = this.channels.get(name); if (!list) - return; + return null; assert(list.size > 0); @@ -1088,7 +1106,7 @@ Route.prototype.match = function _match(pathname) { const match = this.regex.exec(pathname); if (!match) - return; + return null; const params = Object.create(null); @@ -1135,7 +1153,7 @@ function Routes() { Routes.prototype.getHandlers = function getHandlers(method) { if (!method) - return; + return null; method = method.toUpperCase(); @@ -1149,7 +1167,7 @@ Routes.prototype.getHandlers = function getHandlers(method) { case 'DELETE': return this.del; default: - return; + return null; } }; @@ -1535,13 +1553,14 @@ WebSocket.prototype.init = function init() { WebSocket.prototype.onevent = async function onevent(packet) { const args = (packet.data || []).slice(); const type = args.shift() || ''; - let ack, result; + let ack; if (typeof args[args.length - 1] === 'function') ack = args.pop(); else ack = this.socket.ack(packet.id); + let result; try { result = await this.fire(type, args); } catch (e) { @@ -1568,7 +1587,7 @@ WebSocket.prototype.fire = async function fire(type, args) { const handler = this.hooks[type]; if (!handler) - return; + return undefined; return await handler.call(this.context, args); }; @@ -1617,8 +1636,8 @@ function parsePairs(str, limit) { for (const pair of parts) { const index = pair.indexOf('='); - let key, value; + let key, value; if (index === -1) { key = pair; value = ''; diff --git a/lib/http/client.js b/lib/http/client.js index f80a28bf..c5f850dc 100644 --- a/lib/http/client.js +++ b/lib/http/client.js @@ -195,14 +195,13 @@ HTTPClient.prototype.onDisconnect = function onDisconnect() { */ HTTPClient.prototype._request = async function _request(method, endpoint, json) { - let query; - if (this.token) { if (!json) json = {}; json.token = this.token; } + let query; if (json && method === 'get') { query = json; json = null; @@ -221,7 +220,7 @@ HTTPClient.prototype._request = async function _request(method, endpoint, json) }); if (res.statusCode === 404) - return; + return null; if (res.statusCode === 401) throw new Error('Unauthorized (bad API key).'); @@ -457,22 +456,6 @@ HTTPClient.prototype.leave = function leave(id) { }); }; -/** - * Listen for events on all wallets. - */ - -HTTPClient.prototype.all = function all(token) { - return this.join('!all', token); -}; - -/** - * Unlisten for events on all wallets. - */ - -HTTPClient.prototype.none = function none() { - return this.leave('!all'); -}; - /** * Get list of all wallet IDs. * @returns {Promise} @@ -974,7 +957,7 @@ HTTPClient.prototype.createNested = function createNested(id, options) { function toHex(obj) { if (!obj) - return; + return null; if (obj.toRaw) obj = obj.toRaw(); diff --git a/lib/http/request.js b/lib/http/request.js index 2cc3f509..bcc1da38 100644 --- a/lib/http/request.js +++ b/lib/http/request.js @@ -424,8 +424,6 @@ Request.prototype.finish = function finish(err) { }; Request.prototype._onResponse = function _onResponse(response) { - let type = response.headers['content-type']; - const length = response.headers['content-length']; const location = response.headers['location']; if (location) { @@ -440,13 +438,16 @@ Request.prototype._onResponse = function _onResponse(response) { return; } - type = parseType(type); + const contentType = response.headers['content-type']; + const type = parseType(contentType); if (!this.options.isExpected(type)) { this.finish(new Error('Wrong content-type for response.')); return; } + const length = response.headers['content-length']; + if (this.options.isOverflow(length)) { this.finish(new Error('Response exceeded limit.')); return; diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 2449059b..2f3aba88 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -24,6 +24,7 @@ const KeyRing = require('../primitives/keyring'); const MerkleBlock = require('../primitives/merkleblock'); const MTX = require('../primitives/mtx'); const Network = require('../protocol/network'); +const Outpoint = require('../primitives/outpoint'); const Output = require('../primitives/output'); const TX = require('../primitives/tx'); const IP = require('../utils/ip'); @@ -214,12 +215,12 @@ RPC.prototype.stop = async function stop(args, help) { */ RPC.prototype.getNetworkInfo = async function getNetworkInfo(args, help) { - const hosts = this.pool.hosts; - const locals = []; - if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'getnetworkinfo'); + const hosts = this.pool.hosts; + const locals = []; + for (const local of hosts.local.values()) { locals.push({ address: local.addr.host, @@ -246,13 +247,13 @@ RPC.prototype.getNetworkInfo = async function getNetworkInfo(args, help) { }; RPC.prototype.addNode = async function addNode(args, help) { + if (help || args.length !== 2) + throw new RPCError(errs.MISC_ERROR, 'addnode "node" "add|remove|onetry"'); + const valid = new Validator([args]); const node = valid.str(0, ''); const cmd = valid.str(1, ''); - if (help || args.length !== 2) - throw new RPCError(errs.MISC_ERROR, 'addnode "node" "add|remove|onetry"'); - switch (cmd) { case 'add': { this.pool.hosts.addNode(node); @@ -278,12 +279,12 @@ RPC.prototype.addNode = async function addNode(args, help) { }; RPC.prototype.disconnectNode = async function disconnectNode(args, help) { - const valid = new Validator([args]); - const str = valid.str(0, ''); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'disconnectnode "node"'); + const valid = new Validator([args]); + const str = valid.str(0, ''); + const addr = parseIP(str, this.network); const peer = this.pool.peers.get(addr.hostname); @@ -294,18 +295,19 @@ RPC.prototype.disconnectNode = async function disconnectNode(args, help) { }; RPC.prototype.getAddedNodeInfo = async function getAddedNodeInfo(args, help) { - const hosts = this.pool.hosts; - const valid = new Validator([args]); - const addr = valid.str(0, ''); - const result = []; - let target = null; - if (help || args.length > 1) throw new RPCError(errs.MISC_ERROR, 'getaddednodeinfo ( "node" )'); + const hosts = this.pool.hosts; + const valid = new Validator([args]); + const addr = valid.str(0, ''); + + let target; if (args.length === 1) target = parseIP(addr, this.network); + const result = []; + for (const node of hosts.nodes) { if (target) { if (node.host !== target.host) @@ -375,11 +377,11 @@ RPC.prototype.getNetTotals = async function getNetTotals(args, help) { }; RPC.prototype.getPeerInfo = async function getPeerInfo(args, help) { - const peers = []; - if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'getpeerinfo'); + const peers = []; + for (let peer = this.pool.peers.head(); peer; peer = peer.next) { let offset = this.network.time.known.get(peer.hostname()); const hashes = []; @@ -462,11 +464,11 @@ RPC.prototype.setBan = async function setBan(args, help) { }; RPC.prototype.listBanned = async function listBanned(args, help) { - const banned = []; - if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'listbanned'); + const banned = []; + for (const [host, time] of this.pool.hosts.banned) { banned.push({ address: host, @@ -528,14 +530,14 @@ RPC.prototype.getBlockCount = async function getBlockCount(args, help) { }; RPC.prototype.getBlock = async function getBlock(args, help) { + if (help || args.length < 1 || args.length > 3) + throw new RPCError(errs.MISC_ERROR, 'getblock "hash" ( verbose )'); + const valid = new Validator([args]); const hash = valid.hash(0); const verbose = valid.bool(1, true); const details = valid.bool(2, false); - if (help || args.length < 1 || args.length > 3) - throw new RPCError(errs.MISC_ERROR, 'getblock "hash" ( verbose )'); - if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.'); @@ -563,16 +565,16 @@ RPC.prototype.getBlock = async function getBlock(args, help) { }; RPC.prototype.getBlockByHeight = async function getBlockByHeight(args, help) { - const valid = new Validator([args]); - const height = valid.u32(0, -1); - const verbose = valid.bool(1, true); - const details = valid.bool(2, false); - if (help || args.length < 1 || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'getblockbyheight "height" ( verbose )'); } + const valid = new Validator([args]); + const height = valid.u32(0, -1); + const verbose = valid.bool(1, true); + const details = valid.bool(2, false); + if (height === -1) throw new RPCError(errs.TYPE_ERROR, 'Invalid block height.'); @@ -600,12 +602,12 @@ RPC.prototype.getBlockByHeight = async function getBlockByHeight(args, help) { }; RPC.prototype.getBlockHash = async function getBlockHash(args, help) { - const valid = new Validator([args]); - const height = valid.u32(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'getblockhash index'); + const valid = new Validator([args]); + const height = valid.u32(0); + if (height == null || height > this.chain.height) throw new RPCError(errs.INVALID_PARAMETER, 'Block height out of range.'); @@ -618,13 +620,13 @@ RPC.prototype.getBlockHash = async function getBlockHash(args, help) { }; RPC.prototype.getBlockHeader = async function getBlockHeader(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'getblockheader "hash" ( verbose )'); + const valid = new Validator([args]); const hash = valid.hash(0); const verbose = valid.bool(1, true); - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getblockheader "hash" ( verbose )'); - if (!hash) throw new RPCError(errs.MISC_ERROR, 'Invalid block hash.'); @@ -689,13 +691,12 @@ RPC.prototype.getMempoolInfo = async function getMempoolInfo(args, help) { }; RPC.prototype.getMempoolAncestors = async function getMempoolAncestors(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'getmempoolancestors txid (verbose)'); + const valid = new Validator([args]); const hash = valid.hash(0); const verbose = valid.bool(1, false); - const out = []; - - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getmempoolancestors txid (verbose)'); if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); @@ -709,6 +710,7 @@ RPC.prototype.getMempoolAncestors = async function getMempoolAncestors(args, hel throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.'); const entries = this.mempool.getAncestors(entry); + const out = []; if (verbose) { for (const entry of entries) @@ -722,13 +724,12 @@ RPC.prototype.getMempoolAncestors = async function getMempoolAncestors(args, hel }; RPC.prototype.getMempoolDescendants = async function getMempoolDescendants(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'getmempooldescendants txid (verbose)'); + const valid = new Validator([args]); const hash = valid.hash(0); const verbose = valid.bool(1, false); - const out = []; - - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getmempooldescendants txid (verbose)'); if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); @@ -742,6 +743,7 @@ RPC.prototype.getMempoolDescendants = async function getMempoolDescendants(args, throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.'); const entries = this.mempool.getDescendants(entry); + const out = []; if (verbose) { for (const entry of entries) @@ -755,12 +757,12 @@ RPC.prototype.getMempoolDescendants = async function getMempoolDescendants(args, }; RPC.prototype.getMempoolEntry = async function getMempoolEntry(args, help) { - const valid = new Validator([args]); - const hash = valid.hash(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'getmempoolentry txid'); + const valid = new Validator([args]); + const hash = valid.hash(0); + if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); @@ -776,17 +778,18 @@ RPC.prototype.getMempoolEntry = async function getMempoolEntry(args, help) { }; RPC.prototype.getRawMempool = async function getRawMempool(args, help) { - const valid = new Validator([args]); - const verbose = valid.bool(0, false); - const out = {}; - if (help || args.length > 1) throw new RPCError(errs.MISC_ERROR, 'getrawmempool ( verbose )'); + const valid = new Validator([args]); + const verbose = valid.bool(0, false); + if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); if (verbose) { + const out = {}; + for (const entry of this.mempool.map.values()) out[entry.txid()] = this.entryToJSON(entry); @@ -799,14 +802,13 @@ RPC.prototype.getRawMempool = async function getRawMempool(args, help) { }; RPC.prototype.getTXOut = async function getTXOut(args, help) { + if (help || args.length < 2 || args.length > 3) + throw new RPCError(errs.MISC_ERROR, 'gettxout "txid" n ( includemempool )'); + const valid = new Validator([args]); const hash = valid.hash(0); const index = valid.u32(1); const mempool = valid.bool(2, true); - let coin; - - if (help || args.length < 2 || args.length > 3) - throw new RPCError(errs.MISC_ERROR, 'gettxout "txid" n ( includemempool )'); if (this.chain.options.spv) throw new RPCError(errs.MISC_ERROR, 'Cannot get coins in SPV mode.'); @@ -817,6 +819,7 @@ RPC.prototype.getTXOut = async function getTXOut(args, help) { if (!hash || index == null) throw new RPCError(errs.TYPE_ERROR, 'Invalid outpoint.'); + let coin; if (mempool) { if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); @@ -840,16 +843,15 @@ RPC.prototype.getTXOut = async function getTXOut(args, help) { }; RPC.prototype.getTXOutProof = async function getTXOutProof(args, help) { - const valid = new Validator([args]); - const txids = valid.array(0); - const hash = valid.hash(1); - const uniq = new Set(); - if (help || (args.length !== 1 && args.length !== 2)) { throw new RPCError(errs.MISC_ERROR, 'gettxoutproof ["txid",...] ( blockhash )'); } + const valid = new Validator([args]); + const txids = valid.array(0); + const hash = valid.hash(1); + if (this.chain.options.spv) throw new RPCError(errs.MISC_ERROR, 'Cannot get coins in SPV mode.'); @@ -860,8 +862,8 @@ RPC.prototype.getTXOutProof = async function getTXOutProof(args, help) { throw new RPCError(errs.INVALID_PARAMETER, 'Invalid TXIDs.'); const items = new Validator([txids]); - let last = null; - let block = null; + const uniq = new Set(); + let last, block; for (let i = 0; i < txids.length; i++) { const txid = items.hash(i); @@ -881,14 +883,12 @@ RPC.prototype.getTXOutProof = async function getTXOutProof(args, help) { block = await this.chain.db.getBlock(hash); } else if (this.chain.options.indexTX) { const tx = await this.chain.db.getMeta(last); - if (!tx) - return; - block = await this.chain.db.getBlock(tx.block); + if (tx) + block = await this.chain.db.getBlock(tx.block); } else { const coins = await this.chain.db.getCoins(last); - if (!coins) - return; - block = await this.chain.db.getBlock(coins.height); + if (coins) + block = await this.chain.db.getBlock(coins.height); } if (!block) @@ -907,20 +907,19 @@ RPC.prototype.getTXOutProof = async function getTXOutProof(args, help) { }; RPC.prototype.verifyTXOutProof = async function verifyTXOutProof(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - const out = []; - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'verifytxoutproof "proof"'); + const valid = new Validator([args]); + const data = valid.buf(0); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.'); const block = MerkleBlock.fromRaw(data); if (!block.verify()) - return out; + return []; const entry = await this.chain.db.getEntry(block.hash('hex')); @@ -928,6 +927,7 @@ RPC.prototype.verifyTXOutProof = async function verifyTXOutProof(args, help) { throw new RPCError(errs.MISC_ERROR, 'Block not found in chain.'); const tree = block.getTree(); + const out = []; for (const hash of tree.matches) out.push(util.revHex(hash)); @@ -974,13 +974,13 @@ RPC.prototype.pruneBlockchain = async function pruneBlockchain(args, help) { }; RPC.prototype.verifyChain = async function verifyChain(args, help) { + if (help || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'verifychain ( checklevel numblocks )'); + const valid = new Validator([args]); const level = valid.u32(0); const blocks = valid.u32(1); - if (help || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'verifychain ( checklevel numblocks )'); - if (level == null || blocks == null) throw new RPCError(errs.TYPE_ERROR, 'Missing parameters.'); @@ -1043,8 +1043,8 @@ RPC.prototype._submitWork = async function _submitWork(data) { return false; const block = attempt.commit(proof); - let entry = null; + let entry; try { entry = await this.chain.add(block); } catch (err) { @@ -1106,13 +1106,13 @@ RPC.prototype.getWorkLongpoll = async function getWorkLongpoll(args, help) { }; RPC.prototype.getWork = async function getWork(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (args.length > 1) throw new RPCError(errs.MISC_ERROR, 'getwork ( "data" )'); if (args.length === 1) { + const valid = new Validator([args]); + const data = valid.buf(0); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid work data.'); @@ -1123,42 +1123,36 @@ RPC.prototype.getWork = async function getWork(args, help) { }; RPC.prototype.submitBlock = async function submitBlock(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'submitblock "hexdata" ( "jsonparametersobject" )'); } + const valid = new Validator([args]); + const data = valid.buf(0); + const block = Block.fromRaw(data); return await this.addBlock(block); }; RPC.prototype.getBlockTemplate = async function getBlockTemplate(args, help) { - const validator = new Validator([args]); - const options = validator.obj(0, {}); - const valid = new Validator([options]); - const mode = valid.str('mode', 'template'); - const lpid = valid.str('longpollid'); - const data = valid.buf('data'); - let rules = valid.array('rules'); - const capabilities = valid.array('capabilities'); - let maxVersion = valid.u32('maxversion', -1); - let coinbase = false; - let txnCap = false; - let valueCap = false; - if (help || args.length > 1) { throw new RPCError(errs.MISC_ERROR, 'getblocktemplate ( "jsonrequestobject" )'); } + const validator = new Validator([args]); + const options = validator.obj(0, {}); + const valid = new Validator([options]); + const mode = valid.str('mode', 'template'); + if (mode !== 'template' && mode !== 'proposal') throw new RPCError(errs.INVALID_PARAMETER, 'Invalid mode.'); if (mode === 'proposal') { + const data = valid.buf('data'); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Missing data parameter.'); @@ -1178,10 +1172,19 @@ RPC.prototype.getBlockTemplate = async function getBlockTemplate(args, help) { return null; } + let maxVersion = valid.u32('maxversion', -1); + let rules = valid.array('rules'); + if (rules) maxVersion = -1; + const capabilities = valid.array('capabilities'); + let coinbase = false; + if (capabilities) { + let txnCap = false; + let valueCap = false; + for (const capability of capabilities) { if (typeof capability !== 'string') throw new RPCError(errs.TYPE_ERROR, 'Invalid capability.'); @@ -1228,6 +1231,8 @@ RPC.prototype.getBlockTemplate = async function getBlockTemplate(args, help) { } } + const lpid = valid.str('longpollid'); + if (lpid) await this.handleLongpoll(lpid); @@ -1248,13 +1253,10 @@ RPC.prototype.createTemplate = async function createTemplate(maxVersion, coinbas RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinbase, rules) { const attempt = await this.getTemplate(); - let version = attempt.version; const scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR; + + // Default mutable fields. const mutable = ['time', 'transactions', 'prevblock']; - const txs = []; - const index = new Map(); - const vbavailable = {}; - const vbrules = []; // The miner doesn't support // versionbits. Force them to @@ -1273,12 +1275,14 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb } // Build an index of every transaction. + const index = new Map(); for (let i = 0; i < attempt.items.length; i++) { const entry = attempt.items[i]; index.set(entry.hash, i + 1); } // Calculate dependencies for each transaction. + const txs = []; for (let i = 0; i < attempt.items.length; i++) { const entry = attempt.items[i]; const tx = entry.tx; @@ -1317,6 +1321,10 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb rules.push('segwit'); // Calculate version based on given rules. + let version = attempt.version; + const vbavailable = {}; + const vbrules = []; + for (const deploy of this.network.deploys) { const state = await this.chain.getState(this.chain.tip, deploy); let name = deploy.name; @@ -1433,15 +1441,16 @@ RPC.prototype._createTemplate = async function _createTemplate(maxVersion, coinb }; RPC.prototype.getMiningInfo = async function getMiningInfo(args, help) { + if (help || args.length !== 0) + throw new RPCError(errs.MISC_ERROR, 'getmininginfo'); + const attempt = this.attempt; + let size = 0; let weight = 0; let txs = 0; let diff = 0; - if (help || args.length !== 0) - throw new RPCError(errs.MISC_ERROR, 'getmininginfo'); - if (attempt) { weight = attempt.weight; txs = attempt.items.length + 1; @@ -1470,27 +1479,27 @@ RPC.prototype.getMiningInfo = async function getMiningInfo(args, help) { }; RPC.prototype.getNetworkHashPS = async function getNetworkHashPS(args, help) { + if (help || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'getnetworkhashps ( blocks height )'); + const valid = new Validator([args]); const lookup = valid.u32(0, 120); const height = valid.u32(1); - if (help || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getnetworkhashps ( blocks height )'); - return await this.getHashRate(lookup, height); }; RPC.prototype.prioritiseTransaction = async function prioritiseTransaction(args, help) { - const valid = new Validator([args]); - const hash = valid.hash(0); - const pri = valid.num(1); - const fee = valid.i64(2); - if (help || args.length !== 3) { throw new RPCError(errs.MISC_ERROR, 'prioritisetransaction '); } + const valid = new Validator([args]); + const hash = valid.hash(0); + const pri = valid.num(1); + const fee = valid.i64(2); + if (!this.mempool) throw new RPCError(errs.MISC_ERROR, 'No mempool available.'); @@ -1511,12 +1520,12 @@ RPC.prototype.prioritiseTransaction = async function prioritiseTransaction(args, }; RPC.prototype.verifyBlock = async function verifyBlock(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'verifyblock "block-hex"'); + const valid = new Validator([args]); + const data = valid.buf(0); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid block hex.'); @@ -1547,13 +1556,13 @@ RPC.prototype.getGenerate = async function getGenerate(args, help) { }; RPC.prototype.setGenerate = async function setGenerate(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'setgenerate mine ( proclimit )'); + const valid = new Validator([args]); const mine = valid.bool(0, false); const limit = valid.u32(1, 0); - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'setgenerate mine ( proclimit )'); - if (mine && this.miner.addresses.length === 0) { throw new RPCError(errs.MISC_ERROR, 'No addresses available for coinbase.'); @@ -1573,13 +1582,13 @@ RPC.prototype.setGenerate = async function setGenerate(args, help) { }; RPC.prototype.generate = async function generate(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'generate numblocks ( maxtries )'); + const valid = new Validator([args]); const blocks = valid.u32(0, 1); const tries = valid.u32(1); - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'generate numblocks ( maxtries )'); - if (this.miner.addresses.length === 0) { throw new RPCError(errs.MISC_ERROR, 'No addresses available for coinbase.'); @@ -1589,16 +1598,16 @@ RPC.prototype.generate = async function generate(args, help) { }; RPC.prototype.generateToAddress = async function _generateToAddress(args, help) { - const valid = new Validator([args]); - const blocks = valid.u32(0, 1); - const str = valid.str(1, ''); - const tries = valid.u32(2); - if (help || args.length < 2 || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'generatetoaddress numblocks address ( maxtries )'); } + const valid = new Validator([args]); + const blocks = valid.u32(0, 1); + const str = valid.str(1, ''); + const tries = valid.u32(2); + const addr = parseAddress(str, this.network); return await this.mineBlocks(blocks, addr, tries); @@ -1609,12 +1618,6 @@ RPC.prototype.generateToAddress = async function _generateToAddress(args, help) */ RPC.prototype.createRawTransaction = async function createRawTransaction(args, help) { - const valid = new Validator([args]); - const inputs = valid.array(0); - const sendTo = valid.obj(1); - const locktime = valid.u32(2); - const uniq = new Set(); - if (help || args.length < 2 || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'createrawtransaction' @@ -1623,6 +1626,11 @@ RPC.prototype.createRawTransaction = async function createRawTransaction(args, h + ' ( locktime )'); } + const valid = new Validator([args]); + const inputs = valid.array(0); + const sendTo = valid.obj(1); + const locktime = valid.u32(2); + if (!inputs || !sendTo) { throw new RPCError(errs.TYPE_ERROR, 'Invalid parameters (inputs and sendTo).'); @@ -1654,6 +1662,7 @@ RPC.prototype.createRawTransaction = async function createRawTransaction(args, h } const sends = new Validator([sendTo]); + const uniq = new Set(); for (const key of Object.keys(sendTo)) { if (key === 'data') { @@ -1694,12 +1703,12 @@ RPC.prototype.createRawTransaction = async function createRawTransaction(args, h }; RPC.prototype.decodeRawTransaction = async function decodeRawTransaction(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'decoderawtransaction "hexstring"'); + const valid = new Validator([args]); + const data = valid.buf(0); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.'); @@ -1709,12 +1718,12 @@ RPC.prototype.decodeRawTransaction = async function decodeRawTransaction(args, h }; RPC.prototype.decodeScript = async function decodeScript(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'decodescript "hex"'); + const valid = new Validator([args]); + const data = valid.buf(0); + let script = new Script(); if (data) @@ -1729,13 +1738,13 @@ RPC.prototype.decodeScript = async function decodeScript(args, help) { }; RPC.prototype.getRawTransaction = async function getRawTransaction(args, help) { + if (help || args.length < 1 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'getrawtransaction "txid" ( verbose )'); + const valid = new Validator([args]); const hash = valid.hash(0); const verbose = valid.bool(1, false); - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getrawtransaction "txid" ( verbose )'); - if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.'); @@ -1749,8 +1758,7 @@ RPC.prototype.getRawTransaction = async function getRawTransaction(args, help) { if (!verbose) return tx.toRaw().toString('hex'); - let entry = null; - + let entry; if (meta.block) entry = await this.chain.db.getEntry(meta.block); @@ -1762,14 +1770,14 @@ RPC.prototype.getRawTransaction = async function getRawTransaction(args, help) { }; RPC.prototype.sendRawTransaction = async function sendRawTransaction(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'sendrawtransaction "hexstring" ( allowhighfees )'); } + const valid = new Validator([args]); + const data = valid.buf(0); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.'); @@ -1781,15 +1789,6 @@ RPC.prototype.sendRawTransaction = async function sendRawTransaction(args, help) }; RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - const prevout = valid.array(1); - const secrets = valid.array(2); - const sighash = valid.str(3); - const type = Script.hashType.ALL; - const map = new Map(); - const keys = []; - if (help || args.length < 1 || args.length > 4) { throw new RPCError(errs.MISC_ERROR, 'signrawtransaction' @@ -1799,6 +1798,12 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) + ' sighashtype )'); } + const valid = new Validator([args]); + const data = valid.buf(0); + const prevout = valid.array(1); + const secrets = valid.array(2); + const sighash = valid.str(3); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.'); @@ -1808,6 +1813,9 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) const tx = MTX.fromRaw(data); tx.view = await this.mempool.getSpentView(tx); + const map = new Map(); + const keys = []; + if (secrets) { const valid = new Validator([secrets]); for (let i = 0; i < secrets.length; i++) { @@ -1823,33 +1831,32 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) const valid = new Validator([prev]); const hash = valid.hash('txid'); const index = valid.u32('index'); - let script = valid.buf('scriptPubKey'); + const scriptRaw = valid.buf('scriptPubKey'); const value = valid.btc('amount'); - let redeem = valid.buf('redeemScript'); + const redeemRaw = valid.buf('redeemScript'); - if (!hash || index == null || !script || value == null) + if (!hash || index == null || !scriptRaw || value == null) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid UTXO.'); - script = Script.fromRaw(script); + const outpoint = new Outpoint(hash, index); - const coin = new Output(); - coin.script = script; - coin.value = value; + const script = Script.fromRaw(scriptRaw); + const coin = Output.fromScript(script, value); - tx.view.addOutput(hash, index, coin); + tx.view.addOutput(outpoint, coin); - if (keys.length === 0 || !redeem) + if (keys.length === 0 || !redeemRaw) continue; if (!script.isScripthash() && !script.isWitnessScripthash()) continue; - if (!redeem) { + if (!redeemRaw) { throw new RPCError(errs.INVALID_PARAMETER, 'P2SH requires redeem script.'); } - redeem = Script.fromRaw(redeem); + const redeem = Script.fromRaw(redeemRaw); for (const op of redeem.code) { if (!op.data) @@ -1867,14 +1874,16 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) } } + let type = Script.hashType.ALL; if (sighash) { const parts = sighash.split('|'); - let type = Script.hashType[parts[0]]; - if (type == null) + if (parts.length < 1 || parts.length > 2) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid sighash type.'); - if (parts.length > 2) + type = Script.hashType[parts[0]]; + + if (type == null) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid sighash type.'); if (parts.length === 2) { @@ -1897,14 +1906,14 @@ RPC.prototype.signRawTransaction = async function signRawTransaction(args, help) */ RPC.prototype.createMultisig = async function createMultisig(args, help) { + if (help || args.length < 2 || args.length > 2) + throw new RPCError(errs.MISC_ERROR, 'createmultisig nrequired ["key",...]'); + const valid = new Validator([args]); const keys = valid.array(1, []); const m = valid.u32(0, 0); const n = keys.length; - if (help || args.length < 2 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'createmultisig nrequired ["key",...]'); - if (m < 1 || n < m || n > 16) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid m and n values.'); @@ -1936,12 +1945,12 @@ RPC.prototype.createMultisig = async function createMultisig(args, help) { }; RPC.prototype.createWitnessAddress = async function createWitnessAddress(args, help) { - const valid = new Validator([args]); - const raw = valid.buf(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'createwitnessaddress "script"'); + const valid = new Validator([args]); + const raw = valid.buf(0); + if (!raw) throw new RPCError(errs.TYPE_ERROR, 'Invalid script hex.'); @@ -1956,13 +1965,13 @@ RPC.prototype.createWitnessAddress = async function createWitnessAddress(args, h }; RPC.prototype.validateAddress = async function validateAddress(args, help) { - const valid = new Validator([args]); - const str = valid.str(0, ''); - let addr; - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'validateaddress "bitcoinaddress"'); + const valid = new Validator([args]); + const str = valid.str(0, ''); + + let addr; try { addr = Address.fromString(str, this.network); } catch (e) { @@ -1983,16 +1992,16 @@ RPC.prototype.validateAddress = async function validateAddress(args, help) { }; RPC.prototype.verifyMessage = async function verifyMessage(args, help) { - const valid = new Validator([args]); - const b58 = valid.str(0, ''); - const sig = valid.buf(1, null, 'base64'); - const str = valid.str(2); - if (help || args.length !== 3) { throw new RPCError(errs.MISC_ERROR, 'verifymessage "bitcoinaddress" "signature" "message"'); } + const valid = new Validator([args]); + const b58 = valid.str(0, ''); + const sig = valid.buf(1, null, 'base64'); + const str = valid.str(2); + if (!sig || !str) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameters.'); @@ -2009,15 +2018,15 @@ RPC.prototype.verifyMessage = async function verifyMessage(args, help) { }; RPC.prototype.signMessageWithPrivkey = async function signMessageWithPrivkey(args, help) { - const valid = new Validator([args]); - const wif = valid.str(0, ''); - const str = valid.str(1, ''); - if (help || args.length !== 2) { throw new RPCError(errs.MISC_ERROR, 'signmessagewithprivkey "privkey" "message"'); } + const valid = new Validator([args]); + const wif = valid.str(0, ''); + const str = valid.str(1, ''); + const key = parseSecret(wif, this.network); const msg = Buffer.from(MAGIC_STRING + str, 'utf8'); const hash = digest.hash256(msg); @@ -2027,18 +2036,15 @@ RPC.prototype.signMessageWithPrivkey = async function signMessageWithPrivkey(arg }; RPC.prototype.estimateFee = async function estimateFee(args, help) { - const valid = new Validator([args]); - let blocks = valid.u32(0, 1); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'estimatefee nblocks'); + const valid = new Validator([args]); + const blocks = valid.u32(0, 1); + if (!this.fees) throw new RPCError(errs.MISC_ERROR, 'Fee estimation not available.'); - if (blocks < 1) - blocks = 1; - const fee = this.fees.estimateFee(blocks, false); if (fee === 0) @@ -2048,34 +2054,28 @@ RPC.prototype.estimateFee = async function estimateFee(args, help) { }; RPC.prototype.estimatePriority = async function estimatePriority(args, help) { - const valid = new Validator([args]); - let blocks = valid.u32(0, 1); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'estimatepriority nblocks'); + const valid = new Validator([args]); + const blocks = valid.u32(0, 1); + if (!this.fees) throw new RPCError(errs.MISC_ERROR, 'Priority estimation not available.'); - if (blocks < 1) - blocks = 1; - return this.fees.estimatePriority(blocks, false); }; RPC.prototype.estimateSmartFee = async function estimateSmartFee(args, help) { - const valid = new Validator([args]); - let blocks = valid.u32(0, 1); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'estimatesmartfee nblocks'); + const valid = new Validator([args]); + const blocks = valid.u32(0, 1); + if (!this.fees) throw new RPCError(errs.MISC_ERROR, 'Fee estimation not available.'); - if (blocks < 1) - blocks = 1; - let fee = this.fees.estimateFee(blocks, true); if (fee === 0) @@ -2090,18 +2090,15 @@ RPC.prototype.estimateSmartFee = async function estimateSmartFee(args, help) { }; RPC.prototype.estimateSmartPriority = async function estimateSmartPriority(args, help) { - const valid = new Validator([args]); - let blocks = valid.u32(0, 1); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'estimatesmartpriority nblocks'); + const valid = new Validator([args]); + const blocks = valid.u32(0, 1); + if (!this.fees) throw new RPCError(errs.MISC_ERROR, 'Priority estimation not available.'); - if (blocks < 1) - blocks = 1; - const pri = this.fees.estimatePriority(blocks, true); return { @@ -2111,12 +2108,12 @@ RPC.prototype.estimateSmartPriority = async function estimateSmartPriority(args, }; RPC.prototype.invalidateBlock = async function invalidateBlock(args, help) { - const valid = new Validator([args]); - const hash = valid.hash(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'invalidateblock "hash"'); + const valid = new Validator([args]); + const hash = valid.hash(0); + if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.'); @@ -2126,12 +2123,12 @@ RPC.prototype.invalidateBlock = async function invalidateBlock(args, help) { }; RPC.prototype.reconsiderBlock = async function reconsiderBlock(args, help) { - const valid = new Validator([args]); - const hash = valid.hash(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'reconsiderblock "hash"'); + const valid = new Validator([args]); + const hash = valid.hash(0); + if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.'); @@ -2141,12 +2138,12 @@ RPC.prototype.reconsiderBlock = async function reconsiderBlock(args, help) { }; RPC.prototype.setMockTime = async function setMockTime(args, help) { - const valid = new Validator([args]); - const time = valid.u32(0); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'setmocktime timestamp'); + const valid = new Validator([args]); + const time = valid.u32(0); + if (time == null) throw new RPCError(errs.TYPE_ERROR, 'Invalid timestamp.'); @@ -2167,12 +2164,12 @@ RPC.prototype.getMemoryInfo = async function getMemoryInfo(args, help) { }; RPC.prototype.setLogLevel = async function setLogLevel(args, help) { - const valid = new Validator([args]); - const level = valid.str(0, ''); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'setloglevel "level"'); + const valid = new Validator([args]); + const level = valid.str(0, ''); + this.logger.setLevel(level); return null; @@ -2246,10 +2243,10 @@ RPC.prototype.bindChain = function bindChain() { }; RPC.prototype.getTemplate = async function getTemplate() { - let attempt = this.attempt; - this.bindChain(); + let attempt = this.attempt; + if (attempt) { this.miner.updateTime(attempt); } else { @@ -2262,10 +2259,10 @@ RPC.prototype.getTemplate = async function getTemplate() { }; RPC.prototype.updateWork = async function updateWork() { - let attempt = this.attempt; - this.bindChain(); + let attempt = this.attempt; + if (attempt) { if (attempt.address.isNull()) { throw new RPCError(errs.MISC_ERROR, @@ -2346,8 +2343,7 @@ RPC.prototype._addBlock = async function _addBlock(block) { } } - let entry = null; - + let entry; try { entry = await this.chain._add(block); } catch (err) { @@ -2386,7 +2382,7 @@ RPC.prototype.getBIP9Softforks = async function getBIP9Softforks() { for (const deployment of this.network.deploys) { const state = await this.chain.getState(tip, deployment); - let status = null; + let status; switch (state) { case common.thresholdStates.DEFINED: @@ -2437,23 +2433,22 @@ RPC.prototype.getHashRate = async function getHashRate(lookup, height) { let minTime = tip.time; let maxTime = minTime; - let last = tip; + let entry = tip; for (let i = 0; i < lookup; i++) { - const entry = await entry.getPrevious(); + entry = await entry.getPrevious(); if (!entry) throw new RPCError(errs.DATABASE_ERROR, 'Not found.'); minTime = Math.min(entry.time, minTime); maxTime = Math.max(entry.time, maxTime); - last = entry; } if (minTime === maxTime) return 0; - const workDiff = tip.chainwork.sub(last.chainwork); + const workDiff = tip.chainwork.sub(entry.chainwork); const timeDiff = maxTime - minTime; const ps = +workDiff.toString(10) / timeDiff; @@ -2492,11 +2487,9 @@ RPC.prototype.findFork = async function findFork(entry) { RPC.prototype.txToJSON = function txToJSON(tx, entry) { let height = -1; - let conf = 0; let time = 0; let hash = null; - const vin = []; - const vout = []; + let conf = 0; if (entry) { height = entry.height; @@ -2505,6 +2498,8 @@ RPC.prototype.txToJSON = function txToJSON(tx, entry) { conf = this.chain.height - height + 1; } + const vin = []; + for (const input of tx.inputs) { const json = { coinbase: undefined, @@ -2534,6 +2529,8 @@ RPC.prototype.txToJSON = function txToJSON(tx, entry) { vin.push(json); } + const vout = []; + for (let i = 0; i < tx.outputs.length; i++) { const output = tx.outputs[i]; vout.push({ @@ -2562,7 +2559,6 @@ RPC.prototype.txToJSON = function txToJSON(tx, entry) { RPC.prototype.scriptToJSON = function scriptToJSON(script, hex) { const type = script.getType(); - const addr = script.getAddress(); const json = { asm: script.toASM(), @@ -2579,6 +2575,8 @@ RPC.prototype.scriptToJSON = function scriptToJSON(script, hex) { if (script.isMultisig()) json.reqSigs = script.getSmall(0); + const addr = script.getAddress(); + if (addr) { const str = addr.toString(this.network); json.addresses.push(str); diff --git a/lib/http/rpcbase.js b/lib/http/rpcbase.js index f618fa12..987924b5 100644 --- a/lib/http/rpcbase.js +++ b/lib/http/rpcbase.js @@ -112,8 +112,6 @@ RPCBase.prototype.call = async function call(body, query) { } for (const cmd of cmds) { - let result; - if (!cmd || typeof cmd !== 'object') { out.push({ result: null, @@ -183,6 +181,7 @@ RPCBase.prototype.call = async function call(body, query) { cmd.method = 'getworklp'; } + let result; try { result = await this.execute(cmd); } catch (err) { diff --git a/lib/http/server.js b/lib/http/server.js index cc47fc06..f8c6b589 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -143,12 +143,12 @@ HTTPServer.prototype.initRouter = function initRouter() { this.get('/coin/address/:address', async (req, res) => { const valid = req.valid(); const address = valid.str('address'); - const result = []; enforce(address, 'Address is required.'); enforce(!this.chain.options.spv, 'Cannot get coins in SPV mode.'); const coins = await this.node.getCoinsByAddress(address); + const result = []; for (const coin of coins) result.push(coin.getJSON(this.network)); @@ -180,12 +180,12 @@ HTTPServer.prototype.initRouter = function initRouter() { this.post('/coin/address', async (req, res) => { const valid = req.valid(); const address = valid.array('addresses'); - const result = []; enforce(address, 'Address is required.'); enforce(!this.chain.options.spv, 'Cannot get coins in SPV mode.'); const coins = await this.node.getCoinsByAddress(address); + const result = []; for (const coin of coins) result.push(coin.getJSON(this.network)); @@ -217,12 +217,12 @@ HTTPServer.prototype.initRouter = function initRouter() { this.get('/tx/address/:address', async (req, res) => { const valid = req.valid(); const address = valid.str('address'); - const result = []; enforce(address, 'Address is required.'); enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.'); const metas = await this.node.getMetaByAddress(address); + const result = []; for (const meta of metas) { const view = await this.node.getMetaView(meta); @@ -236,12 +236,12 @@ HTTPServer.prototype.initRouter = function initRouter() { this.post('/tx/address', async (req, res) => { const valid = req.valid(); const address = valid.array('address'); - const result = []; enforce(address, 'Address is required.'); enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.'); const metas = await this.node.getMetaByAddress(address); + const result = []; for (const meta of metas) { const view = await this.node.getMetaView(meta); @@ -285,11 +285,10 @@ HTTPServer.prototype.initRouter = function initRouter() { // Mempool snapshot this.get('/mempool', async (req, res) => { - const result = []; - enforce(this.mempool, 'No mempool available.'); const hashes = this.mempool.getSnapshot(); + const result = []; for (const hash of hashes) result.push(util.revHex(hash)); @@ -361,16 +360,21 @@ HTTPServer.prototype.initSockets = function initSockets() { HTTPServer.prototype.handleSocket = function handleSocket(socket) { socket.hook('auth', (args) => { - const valid = new Validator([args]); - const hash = this.options.apiHash; - const key = valid.str(0); - if (socket.auth) throw new Error('Already authed.'); if (!this.options.noAuth) { - if (!ccmp(hash256(key), hash)) - throw new Error('Bad key.'); + const valid = new Validator([args]); + const key = valid.str(0, ''); + + if (key.length > 255) + throw new Error('Invalid API key.'); + + const data = Buffer.from(key, 'utf8'); + const hash = digest.hash256(data); + + if (!ccmp(hash, this.options.apiHash)) + throw new Error('Invalid API key.'); } socket.auth = true; @@ -593,10 +597,10 @@ HTTPServer.prototype.bindChain = function bindChain() { */ HTTPServer.prototype.filterBlock = function filterBlock(socket, block) { - const txs = []; - if (!socket.filter) - return txs; + return []; + + const txs = []; for (const tx of block.txs) { if (this.filterTX(socket, tx)) @@ -615,11 +619,11 @@ HTTPServer.prototype.filterBlock = function filterBlock(socket, block) { */ HTTPServer.prototype.filterTX = function filterTX(socket, tx) { - let found = false; - if (!socket.filter) return false; + let found = false; + for (let i = 0; i < tx.outputs.length; i++) { const output = tx.outputs[i]; const hash = output.getHash(); @@ -697,7 +701,7 @@ function HTTPOptions(options) { this.logger = null; this.node = null; this.apiKey = base58.encode(random.randomBytes(20)); - this.apiHash = hash256(this.apiKey); + this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'utf8')); this.noAuth = false; this.prefix = null; @@ -736,10 +740,10 @@ HTTPOptions.prototype.fromOptions = function fromOptions(options) { if (options.apiKey != null) { assert(typeof options.apiKey === 'string', 'API key must be a string.'); - assert(options.apiKey.length <= 200, - 'API key must be under 200 bytes.'); + assert(options.apiKey.length <= 255, + 'API key must be under 256 bytes.'); this.apiKey = options.apiKey; - this.apiHash = hash256(this.apiKey); + this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'utf8')); } if (options.noAuth != null) { @@ -804,16 +808,6 @@ HTTPOptions.fromOptions = function fromOptions(options) { * Helpers */ -function hash256(data) { - if (typeof data !== 'string') - return Buffer.alloc(0); - - if (data.length > 200) - return Buffer.alloc(0); - - return digest.hash256(Buffer.from(data, 'utf8')); -} - function enforce(value, msg) { if (!value) { const err = new Error(msg); diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index c5226189..1598c34e 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -447,14 +447,16 @@ PolicyEstimator.VERSION = 0; PolicyEstimator.prototype.init = function init() { const minFee = this.minTrackedFee; const minPri = this.minTrackedPri; + const fee = []; - const priority = []; for (let b = minFee; b <= MAX_FEERATE; b *= FEE_SPACING) fee.push(b); fee.push(INF_FEERATE); + const priority = []; + for (let b = minPri; b <= MAX_PRIORITY; b *= PRI_SPACING) priority.push(b); @@ -694,7 +696,7 @@ PolicyEstimator.prototype.estimateFee = function estimateFee(target, smart) { 'Too many confirmations for estimate.'); if (!smart) { - rate = this.feeStats.estimateMedian( + const rate = this.feeStats.estimateMedian( target, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, this.bestHeight); diff --git a/lib/mempool/layout.js b/lib/mempool/layout.js index 916dae74..3d009a56 100644 --- a/lib/mempool/layout.js +++ b/lib/mempool/layout.js @@ -41,7 +41,7 @@ function write(data, str, off) { if (Buffer.isBuffer(str)) return str.copy(data, off); assert(typeof str === 'string'); - data.write(str, off, 'hex'); + return data.write(str, off, 'hex'); } /* diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 45f42ad8..7f82faa1 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -105,8 +105,6 @@ util.inherits(Mempool, AsyncObject); */ Mempool.prototype._open = async function open() { - const size = (this.options.maxSize / 1024).toFixed(2); - await this.chain.open(); await this.cache.open(); @@ -143,6 +141,8 @@ Mempool.prototype._open = async function open() { this.tip = this.chain.tip.hash; + const size = (this.options.maxSize / 1024).toFixed(2); + this.logger.info('Mempool loaded (maxsize=%dkb).', size); }; @@ -361,12 +361,13 @@ Mempool.prototype._reset = async function reset() { Mempool.prototype.limitSize = function limitSize(added) { const maxSize = this.options.maxSize; - const threshold = maxSize - (maxSize / 10); - const expiryTime = this.options.expiryTime; if (this.size <= maxSize) return false; + const threshold = maxSize - (maxSize / 10); + const expiryTime = this.options.expiryTime; + const now = util.now(); let start = util.hrtime(); const queue = new Heap(cmpRate); @@ -431,8 +432,10 @@ Mempool.prototype.limitSize = function limitSize(added) { Mempool.prototype.getTX = function getTX(hash) { const entry = this.map.get(hash); + if (!entry) - return; + return null; + return entry.tx; }; @@ -457,13 +460,13 @@ Mempool.prototype.getCoin = function getCoin(hash, index) { const entry = this.map.get(hash); if (!entry) - return; + return null; if (this.isSpent(hash, index)) - return; + return null; if (index >= entry.tx.outputs.length) - return; + return null; return Coin.fromTX(entry.tx, index, -1); }; @@ -507,7 +510,7 @@ Mempool.prototype.getSpentTX = function getSpentTX(hash, index) { const entry = this.spents.get(key); if (!entry) - return; + return null; return entry.tx; }; @@ -519,11 +522,11 @@ Mempool.prototype.getSpentTX = function getSpentTX(hash, index) { */ Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addrs) { - const out = []; - if (!Array.isArray(addrs)) addrs = [addrs]; + const out = []; + for (const addr of addrs) { const hash = Address.getHash(addr, 'hex'); const coins = this.coinIndex.get(hash); @@ -542,11 +545,11 @@ Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addrs) { */ Mempool.prototype.getTXByAddress = function getTXByAddress(addrs) { - const out = []; - if (!Array.isArray(addrs)) addrs = [addrs]; + const out = []; + for (const addr of addrs) { const hash = Address.getHash(addr, 'hex'); const txs = this.txIndex.get(hash); @@ -565,11 +568,11 @@ Mempool.prototype.getTXByAddress = function getTXByAddress(addrs) { */ Mempool.prototype.getMetaByAddress = function getMetaByAddress(addrs) { - const out = []; - if (!Array.isArray(addrs)) addrs = [addrs]; + const out = []; + for (const addr of addrs) { const hash = Address.getHash(addr, 'hex'); const txs = this.txIndex.getMeta(hash); @@ -591,7 +594,7 @@ Mempool.prototype.getMeta = function getMeta(hash) { const entry = this.getEntry(hash); if (!entry) - return; + return null; const meta = TXMeta.fromTX(entry.tx); meta.mtime = entry.time; @@ -685,11 +688,10 @@ Mempool.prototype.addTX = async function addTX(tx, id) { */ Mempool.prototype._addTX = async function _addTX(tx, id) { - let missing; - if (id == null) id = -1; + let missing; try { missing = await this.insertTX(tx, id); } catch (err) { @@ -859,7 +861,6 @@ Mempool.prototype.insertTX = async function insertTX(tx, id) { Mempool.prototype.verify = async function verify(entry, view) { const height = this.chain.height + 1; const lockFlags = common.lockFlags.STANDARD_LOCKTIME_FLAGS; - let flags = Script.flags.STANDARD_VERIFY_FLAGS; const tx = entry.tx; // Verify sequence locks. @@ -949,6 +950,7 @@ Mempool.prototype.verify = async function verify(entry, view) { throw new VerifyError(tx, 'invalid', reason, score); // Script verification. + let flags = Script.flags.STANDARD_VERIFY_FLAGS; try { await this.verifyInputs(tx, view, flags); } catch (err) { @@ -1414,8 +1416,6 @@ Mempool.prototype.hasOrphan = function hasOrphan(hash) { */ Mempool.prototype.storeOrphan = function storeOrphan(tx, missing, id) { - const hash = tx.hash('hex'); - if (tx.getWeight() > policy.MAX_TX_WEIGHT) { this.logger.debug('Ignoring large orphan: %s', tx.txid()); if (!tx.hasWitness()) @@ -1436,6 +1436,8 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, missing, id) { this.limitOrphans(); + const hash = tx.hash('hex'); + for (const prev of missing) { if (!this.waiting.has(prev)) this.waiting.set(prev, new Set()); @@ -1510,13 +1512,14 @@ Mempool.prototype.handleOrphans = async function handleOrphans(parent) { Mempool.prototype.resolveOrphans = function resolveOrphans(parent) { const hash = parent.hash('hex'); const set = this.waiting.get(hash); - const resolved = []; if (!set) - return resolved; + return []; assert(set.size > 0); + const resolved = []; + for (const orphanHash of set.keys()) { const orphan = this.getOrphan(orphanHash); @@ -1541,11 +1544,11 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(parent) { Mempool.prototype.removeOrphan = function removeOrphan(hash) { const orphan = this.getOrphan(hash); - let tx; if (!orphan) return false; + let tx; try { tx = orphan.toTX(); } catch (e) { @@ -1553,7 +1556,7 @@ Mempool.prototype.removeOrphan = function removeOrphan(hash) { this.logger.warning('%s %s', 'Warning: possible memory corruption.', 'Orphan failed deserialization.'); - return; + return false; } for (const prev of tx.getPrevout()) { @@ -1587,8 +1590,8 @@ Mempool.prototype.limitOrphans = function limitOrphans() { return false; let index = random.randomRange(0, this.orphans.size); - let hash = null; + let hash; for (hash of this.orphans.keys()) { if (index === 0) break; @@ -1690,7 +1693,7 @@ Mempool.prototype.findMissing = function findMissing(tx, view) { } if (missing.length === 0) - return; + return null; return missing; }; @@ -2115,10 +2118,11 @@ TXIndex.prototype.reset = function reset() { TXIndex.prototype.get = function get(addr) { const items = this.index.get(addr); - const out = []; if (!items) - return out; + return []; + + const out = []; for (const entry of items.values()) out.push(entry.tx); @@ -2128,10 +2132,11 @@ TXIndex.prototype.get = function get(addr) { TXIndex.prototype.getMeta = function getMeta(addr) { const items = this.index.get(addr); - const out = []; if (!items) - return out; + return []; + + const out = []; for (const entry of items.values()) { const meta = TXMeta.fromTX(entry.tx); @@ -2207,10 +2212,11 @@ CoinIndex.prototype.reset = function reset() { CoinIndex.prototype.get = function get(addr) { const items = this.index.get(addr); - const out = []; if (!items) - return out; + return []; + + const out = []; for (const coin of items.values()) out.push(coin.toCoin()); @@ -2332,18 +2338,18 @@ MempoolCache.prototype.getTip = async function getTip() { const hash = await this.db.get(layout.R); if (!hash) - return; + return null; return hash.toString('hex'); }; MempoolCache.prototype.getFees = async function getFees() { const data = await this.db.get(layout.F); - let fees; if (!data) - return; + return null; + let fees; try { fees = Fees.fromRaw(data); } catch (e) { diff --git a/lib/mempool/mempoolentry.js b/lib/mempool/mempoolentry.js index 1416c009..2ca6f637 100644 --- a/lib/mempool/mempoolentry.js +++ b/lib/mempool/mempoolentry.js @@ -97,8 +97,8 @@ MempoolEntry.prototype.fromTX = function fromTX(tx, view, height) { const size = tx.getSigopsSize(sigops); const priority = tx.getPriority(view, height, size); const fee = tx.getFee(view); - let dependencies = false; + let dependencies = false; for (const {prevout} of tx.inputs) { if (view.getHeight(prevout) === -1) { dependencies = true; diff --git a/lib/mining/cpuminer.js b/lib/mining/cpuminer.js index 9abc0fa0..15cd3a7d 100644 --- a/lib/mining/cpuminer.js +++ b/lib/mining/cpuminer.js @@ -128,8 +128,7 @@ CPUMiner.prototype._start = async function start() { if (this.stopping) break; - let block = null; - + let block; try { block = await this.mineAsync(this.job); } catch (e) { @@ -145,8 +144,7 @@ CPUMiner.prototype._start = async function start() { if (!block) continue; - let entry = null; - + let entry; try { entry = await this.chain.add(block); } catch (e) { @@ -295,6 +293,7 @@ CPUMiner.prototype.findNonce = function findNonce(job) { const data = job.getHeader(); const target = job.attempt.target; const interval = CPUMiner.INTERVAL; + let min = 0; let max = interval; let nonce; @@ -322,16 +321,17 @@ CPUMiner.prototype.findNonce = function findNonce(job) { */ CPUMiner.prototype.findNonceAsync = async function findNonceAsync(job) { + if (!this.workers) + return this.findNonce(job); + const data = job.getHeader(); const target = job.attempt.target; const interval = CPUMiner.INTERVAL; + let min = 0; let max = interval; let nonce; - if (!this.workers) - return this.findNonce(job); - while (max <= 0xffffffff) { nonce = await this.workers.mine(data, target, min, max); @@ -357,10 +357,9 @@ CPUMiner.prototype.findNonceAsync = async function findNonceAsync(job) { */ CPUMiner.prototype.mine = function mine(job) { - let nonce; - job.start = util.now(); + let nonce; for (;;) { nonce = this.findNonce(job); @@ -394,7 +393,7 @@ CPUMiner.prototype.mineAsync = async function mineAsync(job) { break; if (job.destroyed) - return; + return null; job.updateNonce(); diff --git a/lib/mining/miner.js b/lib/mining/miner.js index 55d2b4b4..496f4f38 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -259,7 +259,7 @@ Miner.prototype.assemble = function assemble(attempt) { if (!this.mempool) { attempt.refresh(); - return []; + return; } assert(this.mempool.tip === this.chain.tip.hash, diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 5cab81d0..469b2cd5 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -178,7 +178,7 @@ BIP150.prototype.reply = function reply(data) { if (this.isAuthed()) { this.auth = true; this.emit('auth'); - return; + return null; } assert(this.outbound, 'No challenge received before reply on inbound.'); @@ -321,6 +321,8 @@ BIP150.prototype.findAuthorized = function findAuthorized(hash) { if (ccmp(msg, hash)) return key; } + + return null; }; /** @@ -630,12 +632,11 @@ AuthDB.prototype.lookup = async function lookup() { */ AuthDB.prototype.populate = async function populate(addr, key) { - let hosts; - assert(addr.type === IP.types.DNS, 'Resolved host passed.'); this.logger.info('Resolving authorized hosts from: %s.', addr.host); + let hosts; try { hosts = await this.resolve(addr.host); } catch (e) { @@ -665,8 +666,8 @@ AuthDB.prototype.readKnown = async function readKnown() { return; const file = path.join(this.prefix, 'known-peers'); - let text; + let text; try { text = await fs.readFile(file, 'utf8'); } catch (e) { @@ -702,8 +703,8 @@ AuthDB.prototype.parseKnown = function parseKnown(text) { continue; const hostname = parts[0].trim().split(','); - let host, ip; + let host, ip; if (hostname.length >= 2) { host = hostname[0]; ip = hostname[1]; @@ -742,8 +743,8 @@ AuthDB.prototype.readAuth = async function readAuth() { return; const file = path.join(this.prefix, 'authorized-peers'); - let text; + let text; try { text = await fs.readFile(file, 'utf8'); } catch (e) { diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 090fb613..f2e44d72 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -300,7 +300,7 @@ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { if (this.count === this.totalTX) return true; - const have = new Set(); + const set = new Set(); for (const {tx} of mempool.map.values()) { let hash = tx.hash(); @@ -314,7 +314,7 @@ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { if (index == null) continue; - if (have.has(index)) { + if (set.has(index)) { // Siphash collision, just request it. this.available[index] = null; this.count--; @@ -322,7 +322,7 @@ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { } this.available[index] = tx; - have.add(index); + set.add(index); this.count++; // We actually may have a siphash collision @@ -619,7 +619,6 @@ TXRequest.prototype.fromReader = function fromReader(br) { this.hash = br.readHash('hex'); const count = br.readVarint(); - let offset = 0; for (let i = 0; i < count; i++) { const index = br.readVarint(); @@ -627,6 +626,8 @@ TXRequest.prototype.fromReader = function fromReader(br) { this.indexes.push(index); } + let offset = 0; + for (let i = 0; i < count; i++) { let index = this.indexes[i]; index += offset; diff --git a/lib/net/external.js b/lib/net/external.js index 37510dbc..b455e5cb 100644 --- a/lib/net/external.js +++ b/lib/net/external.js @@ -22,34 +22,24 @@ const external = exports; */ external.getIPv4 = async function getIPv4() { - let res = null; - try { - res = await request({ + const res = await request({ method: 'GET', uri: 'http://ipv4.icanhazip.com', expect: 'txt', timeout: 2000 }); - } catch (e) { - return await external.getIPv42(); - } - let ip = null; - - try { const str = res.body.trim(); const raw = IP.toBuffer(str); if (!IP.isIPv4(raw)) throw new Error('Could not find IPv4.'); - ip = IP.toString(raw); + return IP.toString(raw); } catch (e) { return await external.getIPv42(); } - - return ip; }; /** diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index a7561b50..15fb9dd2 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -403,9 +403,7 @@ HostList.prototype.isBanned = function isBanned(host) { */ HostList.prototype.getHost = function getHost() { - const now = this.network.now(); let buckets = null; - let factor = 1; if (this.totalFresh > 0) buckets = this.fresh; @@ -416,7 +414,10 @@ HostList.prototype.getHost = function getHost() { } if (!buckets) - return; + return null; + + const now = this.network.now(); + let factor = 1; for (;;) { let index = util.random(0, buckets.length); @@ -653,7 +654,7 @@ HostList.prototype.remove = function remove(hostname) { const entry = this.map.get(hostname); if (!entry) - return; + return null; if (entry.used) { let head = entry; @@ -832,7 +833,7 @@ HostList.prototype.addSeed = function addSeed(host) { if (ip.type === IP.types.DNS) { // Defer for resolution. this.dnsSeeds.push(ip); - return; + return null; } const addr = NetAddress.fromHost(ip.host, ip.port, this.network); @@ -854,7 +855,7 @@ HostList.prototype.addNode = function addNode(host) { if (ip.type === IP.types.DNS) { // Defer for resolution. this.dnsNodes.push(ip); - return; + return null; } const addr = NetAddress.fromHost(ip.host, ip.port, this.network); diff --git a/lib/net/parser.js b/lib/net/parser.js index a53c1915..a1801f31 100644 --- a/lib/net/parser.js +++ b/lib/net/parser.js @@ -102,8 +102,7 @@ Parser.prototype.parse = function parse(data) { return; } - let payload = null; - + let payload; try { payload = this.parsePayload(this.header.cmd, data); } catch (e) { diff --git a/lib/net/peer.js b/lib/net/peer.js index 5e70dba7..43a9390c 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -511,10 +511,12 @@ Peer.prototype._open = async function open() { Peer.prototype.initConnect = function initConnect() { if (this.connected) { assert(!this.outbound); - return; + return Promise.resolve(); } return new Promise((resolve, reject) => { + /* eslint no-use-before-define: "off" */ + const cleanup = () => { if (this.connectTimeout != null) { clearTimeout(this.connectTimeout); @@ -703,8 +705,6 @@ Peer.prototype.finalize = async function finalize() { */ Peer.prototype.announceBlock = function announceBlock(blocks) { - const inv = []; - if (!this.handshake) return; @@ -714,6 +714,8 @@ Peer.prototype.announceBlock = function announceBlock(blocks) { if (!Array.isArray(blocks)) blocks = [blocks]; + const inv = []; + for (const block of blocks) { assert(block instanceof Block); @@ -753,8 +755,6 @@ Peer.prototype.announceBlock = function announceBlock(blocks) { */ Peer.prototype.announceTX = function announceTX(txs) { - const inv = []; - if (!this.handshake) return; @@ -769,6 +769,8 @@ Peer.prototype.announceTX = function announceTX(txs) { if (!Array.isArray(txs)) txs = [txs]; + const inv = []; + for (const tx of txs) { assert(tx instanceof TX); @@ -830,20 +832,21 @@ Peer.prototype.queueInv = function queueInv(items) { */ Peer.prototype.flushInv = function flushInv() { - const queue = this.invQueue.slice(); - const items = []; - if (this.destroyed) return; + const queue = this.invQueue; + if (queue.length === 0) return; - this.invQueue.length = 0; + this.invQueue = []; this.logger.spam('Serving %d inv items to %s.', queue.length, this.hostname()); + const items = []; + for (const item of queue) { if (!this.invFilter.added(item.hash, 'hex')) continue; @@ -1336,7 +1339,7 @@ Peer.prototype.maybeTimeout = function maybeTimeout() { Peer.prototype.request = function request(type, timeout) { if (this.destroyed) - return; + return null; let entry = this.responseMap.get(type); @@ -1361,7 +1364,7 @@ Peer.prototype.response = function response(type, payload) { const entry = this.responseMap.get(type); if (!entry) - return; + return null; this.responseMap.delete(type); @@ -1989,12 +1992,12 @@ Peer.prototype.handleAuthPropose = async function handleAuthPropose(packet) { Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { const packet = new packets.GetHeadersPacket(locator, stop); - let hash = null; - let end = null; + let hash = null; if (packet.locator.length > 0) hash = util.revHex(packet.locator[0]); + let end = null; if (stop) end = util.revHex(stop); @@ -2017,12 +2020,12 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) { const packet = new packets.GetBlocksPacket(locator, stop); - let hash = null; - let end = null; + let hash = null; if (packet.locator.length > 0) hash = util.revHex(packet.locator[0]); + let end = null; if (stop) end = util.revHex(stop); diff --git a/lib/net/pool.js b/lib/net/pool.js index 43160360..58033f2b 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -463,15 +463,14 @@ Pool.prototype.discoverGateway = async function discoverGateway() { // Pointless if we're not listening. if (!this.options.listen) - return; + return false; // UPNP is always optional, since // it's likely to not work anyway. if (!this.options.upnp) - return; - - let wan = null; + return false; + let wan; try { this.logger.debug('Discovering internet gateway (upnp).'); wan = await UPNP.discover(); @@ -481,8 +480,7 @@ Pool.prototype.discoverGateway = async function discoverGateway() { return false; } - let host = null; - + let host; try { host = await wan.getExternalIP(); } catch (e) { @@ -523,7 +521,6 @@ Pool.prototype.discoverSeeds = async function discoverSeeds(checkPeers) { const size = this.hosts.size(); let total = 0; - for (let peer = this.peers.head(); peer; peer = peer.next) { if (!peer.outbound) continue; @@ -569,8 +566,7 @@ Pool.prototype.discoverExternal = async function discoverExternal() { if (this.hosts.local.size > 0) return; - let host4 = null; - + let host4; try { host4 = await external.getIPv4(); } catch (e) { @@ -581,8 +577,7 @@ Pool.prototype.discoverExternal = async function discoverExternal() { if (host4 && this.hosts.addLocal(host4, port, scores.HTTP)) this.logger.info('External IPv4 found (http): %s.', host4); - let host6 = null; - + let host6; try { host6 = await external.getIPv6(); } catch (e) { @@ -767,8 +762,7 @@ Pool.prototype.resync = async function resync(force) { if (!this.syncing) return; - let locator = null; - + let locator; try { locator = await this.chain.getLocator(); } catch (e) { @@ -834,8 +828,7 @@ Pool.prototype.sendSync = async function sendSync(peer) { peer.syncing = true; peer.blockTime = util.ms(); - let locator = null; - + let locator; try { locator = await this.chain.getLocator(); } catch (e) { @@ -1000,13 +993,13 @@ Pool.prototype.getBroadcasted = function getBroadcasted(peer, item) { const entry = this.invMap.get(item.hash); if (!entry) - return; + return null; if (type !== entry.type) { this.logger.debug( 'Peer requested item with the wrong type (%s).', peer.hostname()); - return; + return null; } this.logger.debug( @@ -1037,19 +1030,19 @@ Pool.prototype.getItem = async function getItem(peer, item) { return entry; if (this.options.selfish) - return; + return null; if (item.isTX()) { if (!this.mempool) - return; + return null; return this.mempool.getTX(item.hash); } if (this.chain.options.spv) - return; + return null; if (this.chain.options.prune) - return; + return null; return await this.chain.db.getBlock(item.hash); }; @@ -1683,8 +1676,6 @@ Pool.prototype._handleInv = async function handleInv(peer, packet) { */ Pool.prototype.handleBlockInv = async function handleBlockInv(peer, hashes) { - const items = []; - assert(hashes.length > 0); if (!this.syncing) @@ -1712,7 +1703,8 @@ Pool.prototype.handleBlockInv = async function handleBlockInv(peer, hashes) { hashes.length, peer.hostname()); - let exists = null; + const items = []; + let exists; for (let i = 0; i < hashes.length; i++) { const hash = hashes[i]; @@ -1968,8 +1960,6 @@ Pool.prototype.handleNotFound = async function handleNotFound(peer, packet) { */ Pool.prototype.handleGetBlocks = async function handleGetBlocks(peer, packet) { - const blocks = []; - if (!this.chain.synced) return; @@ -1987,6 +1977,8 @@ Pool.prototype.handleGetBlocks = async function handleGetBlocks(peer, packet) { if (hash) hash = await this.chain.db.getNextHash(hash); + const blocks = []; + while (hash) { blocks.push(new InvItem(invTypes.BLOCK, hash)); @@ -2025,8 +2017,7 @@ Pool.prototype.handleGetHeaders = async function handleGetHeaders(peer, packet) if (this.chain.options.prune) return; - let hash = null; - + let hash; if (packet.locator.length > 0) { hash = await this.chain.findLocator(packet.locator); if (hash) @@ -2035,8 +2026,7 @@ Pool.prototype.handleGetHeaders = async function handleGetHeaders(peer, packet) hash = packet.stop; } - let entry = null; - + let entry; if (hash) entry = await this.chain.db.getEntry(hash); @@ -2270,8 +2260,7 @@ Pool.prototype._addBlock = async function addBlock(peer, block, flags) { peer.blockTime = util.ms(); - let entry = null; - + let entry; try { entry = await this.chain.add(block, flags, peer.id); } catch (err) { @@ -2535,8 +2524,7 @@ Pool.prototype._handleTX = async function handleTX(peer, packet) { return; } - let missing = null; - + let missing; try { missing = await this.mempool.addTX(tx, peer.id); } catch (err) { @@ -2594,8 +2582,6 @@ Pool.prototype.handleReject = async function handleReject(peer, packet) { */ Pool.prototype.handleMempool = async function handleMempool(peer, packet) { - const items = []; - if (!this.mempool) return; @@ -2613,6 +2599,8 @@ Pool.prototype.handleMempool = async function handleMempool(peer, packet) { return; } + const items = []; + for (const hash of this.mempool.map.keys()) items.push(new InvItem(invTypes.TX, hash)); @@ -2775,7 +2763,6 @@ Pool.prototype.handleCmpctBlock = async function handleCmpctBlock(peer, packet) const block = packet.block; const hash = block.hash('hex'); const witness = peer.compactWitness; - const flags = chainCommon.flags.VERIFY_BODY; if (!this.syncing) return; @@ -2862,6 +2849,7 @@ Pool.prototype.handleCmpctBlock = async function handleCmpctBlock(peer, packet) this.logger.debug( 'Received full compact block %s (%s).', block.rhash(), peer.hostname()); + const flags = chainCommon.flags.VERIFY_BODY; await this.addBlock(peer, block.toBlock(), flags); return; } @@ -3080,9 +3068,6 @@ Pool.prototype.addInbound = function addInbound(socket) { */ Pool.prototype.getHost = function getHost() { - const services = this.options.getRequiredServices(); - const now = this.network.now(); - for (const addr of this.hosts.nodes) { if (this.peers.has(addr.hostname)) continue; @@ -3090,6 +3075,9 @@ Pool.prototype.getHost = function getHost() { return addr; } + const services = this.options.getRequiredServices(); + const now = this.network.now(); + for (let i = 0; i < 100; i++) { const entry = this.hosts.getHost(); @@ -3121,6 +3109,8 @@ Pool.prototype.getHost = function getHost() { return entry.addr; } + + return null; }; /** @@ -4389,20 +4379,20 @@ function NonceList() { } NonceList.prototype.alloc = function alloc(hostname) { - let nonce, key; - for (;;) { - nonce = util.nonce(); - key = nonce.toString('hex'); - if (!this.map.has(key)) { - this.map.set(key, hostname); - assert(!this.hosts.has(hostname)); - this.hosts.set(hostname, key); - break; - } - } + const nonce = util.nonce(); + const key = nonce.toString('hex'); - return nonce; + if (this.map.has(key)) + continue; + + this.map.set(key, hostname); + + assert(!this.hosts.has(hostname)); + this.hosts.set(hostname, key); + + return nonce; + } }; NonceList.prototype.has = function has(nonce) { diff --git a/lib/net/proxysocket.js b/lib/net/proxysocket.js index c2464721..f6949acd 100644 --- a/lib/net/proxysocket.js +++ b/lib/net/proxysocket.js @@ -101,8 +101,6 @@ ProxySocket.prototype._init = function _init() { }; ProxySocket.prototype.connect = function connect(port, host) { - let nonce = 0; - this.remoteAddress = host; this.remotePort = port; @@ -116,14 +114,17 @@ ProxySocket.prototype.connect = function connect(port, host) { return; } - if (this.info.pow) { - let pow = new BufferWriter(); + let nonce = 0; - pow.writeU32(nonce); - pow.writeBytes(this.snonce); - pow.writeU32(port); - pow.writeString(host, 'ascii'); - pow = pow.render(); + if (this.info.pow) { + const bw = new BufferWriter(); + + bw.writeU32(nonce); + bw.writeBytes(this.snonce); + bw.writeU32(port); + bw.writeString(host, 'ascii'); + + const pow = bw.render(); util.log( 'Solving proof of work to create socket (%d, %s) -- please wait.', diff --git a/lib/net/socks.js b/lib/net/socks.js index 92d121c1..f6380029 100644 --- a/lib/net/socks.js +++ b/lib/net/socks.js @@ -428,8 +428,7 @@ SOCKS.prototype.handleProxy = function handleProxy(data) { return; } - let addr = null; - + let addr; try { addr = parseAddr(data, 3); } catch (e) { @@ -487,8 +486,7 @@ SOCKS.prototype.handleResolve = function handleResolve(data) { return; } - let addr = null; - + let addr; try { addr = parseAddr(data, 3); } catch (e) { @@ -569,8 +567,7 @@ Proxy.prototype.connect = async function connect(port, host) { destPort: port }; - let socket = null; - + let socket; try { socket = await SOCKS.proxy(options); } catch (e) { @@ -708,9 +705,7 @@ function parseAddr(data, offset) { br.seek(offset); const type = br.readU8(); - - let host = null; - let port = 0; + let host, port; switch (type) { case 0x01: { diff --git a/lib/net/upnp.js b/lib/net/upnp.js index ba30f4ab..806c2671 100644 --- a/lib/net/upnp.js +++ b/lib/net/upnp.js @@ -191,8 +191,7 @@ UPNP.prototype.handleMsg = async function handleMsg(msg) { if (!this.socket) return; - let headers = null; - + let headers; try { headers = UPNP.parseHeader(msg); } catch (e) { @@ -271,7 +270,7 @@ UPNP.parseHeader = function parseHeader(str) { const index = line.indexOf(':'); if (index === -1) { - left = line.toLowerCase(); + const left = line.toLowerCase(); headers[left] = ''; continue; } @@ -615,6 +614,8 @@ XMLElement.prototype.find = function find(name) { if (child) return child; } + + return null; }; /* @@ -649,6 +650,8 @@ function findService(services, name) { if (service.serviceType === name) return service; } + + return null; } function extractServices(services, targets) { @@ -657,13 +660,15 @@ function extractServices(services, targets) { if (service) return service; } + + return null; } function findIP(el) { const child = el.find('NewExternalIPAddress'); if (!child) - return; + return null; return IP.normalize(child.text); } @@ -672,7 +677,7 @@ function findError(el) { const child = el.find('UPnPError'); if (!child) - return; + return null; let code = -1; const ccode = child.find('errorCode'); diff --git a/lib/node/config.js b/lib/node/config.js index 40dcd826..dbf8d9d0 100644 --- a/lib/node/config.js +++ b/lib/node/config.js @@ -702,8 +702,7 @@ Config.prototype.parseArg = function parseArg(argv) { assert(Array.isArray(argv)); - let key = null; - + let key; for (let i = 2; i < argv.length; i++) { let arg = argv[i]; let value, alias, equals; diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 852c5688..96de2d57 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -379,16 +379,16 @@ FullNode.prototype.getBlock = function getBlock(hash) { * @returns {Promise} - Returns {@link Coin}. */ -FullNode.prototype.getCoin = function getCoin(hash, index) { +FullNode.prototype.getCoin = async function getCoin(hash, index) { const coin = this.mempool.getCoin(hash, index); if (coin) - return Promise.resolve(coin); + return coin; if (this.mempool.isSpent(hash, index)) - return Promise.resolve(); + return null; - return this.chain.db.getCoin(hash, index); + return await this.chain.db.getCoin(hash, index); }; /** @@ -483,8 +483,10 @@ FullNode.prototype.getTXByAddress = async function getTXByAddress(addrs) { FullNode.prototype.getTX = async function getTX(hash) { const mtx = await this.getMeta(hash); + if (!mtx) - return; + return null; + return mtx.tx; }; diff --git a/lib/node/logger.js b/lib/node/logger.js index f9950e20..215b5a28 100644 --- a/lib/node/logger.js +++ b/lib/node/logger.js @@ -253,8 +253,6 @@ Logger.prototype._close = async function close() { */ Logger.prototype.truncate = async function truncate() { - const maxSize = Logger.MAX_FILE_SIZE; - if (!this.filename) return; @@ -263,8 +261,7 @@ Logger.prototype.truncate = async function truncate() { assert(!this.stream); - let stat = null; - + let stat; try { stat = await fs.stat(this.filename); } catch (e) { @@ -273,6 +270,8 @@ Logger.prototype.truncate = async function truncate() { throw e; } + const maxSize = Logger.MAX_FILE_SIZE; + if (stat.size <= maxSize + (maxSize / 10)) return; @@ -390,11 +389,11 @@ Logger.prototype.setLevel = function setLevel(name) { */ Logger.prototype.error = function error(...args) { - const err = args[0]; - if (this.level < Logger.levels.ERROR) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.ERROR, null, err); @@ -408,11 +407,11 @@ Logger.prototype.error = function error(...args) { */ Logger.prototype.warning = function warning(...args) { - const err = args[0]; - if (this.level < Logger.levels.WARNING) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.WARNING, null, err); @@ -426,11 +425,11 @@ Logger.prototype.warning = function warning(...args) { */ Logger.prototype.info = function info(...args) { - const err = args[0]; - if (this.level < Logger.levels.INFO) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.INFO, null, err); @@ -444,11 +443,11 @@ Logger.prototype.info = function info(...args) { */ Logger.prototype.debug = function debug(...args) { - const err = args[0]; - if (this.level < Logger.levels.DEBUG) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.DEBUG, null, err); @@ -462,11 +461,11 @@ Logger.prototype.debug = function debug(...args) { */ Logger.prototype.spam = function spam(...args) { - const err = args[0]; - if (this.level < Logger.levels.SPAM) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.SPAM, null, err); @@ -518,7 +517,6 @@ Logger.prototype.context = function _context(module) { Logger.prototype.writeConsole = function writeConsole(level, module, args) { const name = Logger.levelsByVal[level]; - let msg = ''; assert(name, 'Invalid log level.'); @@ -526,7 +524,7 @@ Logger.prototype.writeConsole = function writeConsole(level, module, args) { return; if (!process.stdout) { - msg += `[${name}] `; + let msg = `[${name}] `; if (module) msg += `(${module}) `; @@ -544,13 +542,15 @@ Logger.prototype.writeConsole = function writeConsole(level, module, args) { : console.log(msg); } + let msg; + if (this.colors) { const color = Logger.styles[level]; assert(color); - msg += `\x1b[${color}m[${name}]\x1b[m `; + msg = `\x1b[${color}m[${name}]\x1b[m `; } else { - msg += `[${name}] `; + msg = `[${name}] `; } if (module) @@ -573,7 +573,6 @@ Logger.prototype.writeConsole = function writeConsole(level, module, args) { Logger.prototype.writeStream = function writeStream(level, module, args) { const name = Logger.prefixByVal[level]; - let msg = ''; assert(name, 'Invalid log level.'); @@ -583,7 +582,7 @@ Logger.prototype.writeStream = function writeStream(level, module, args) { if (this.closing) return; - msg += `[${name}:${util.date()}] `; + let msg = `[${name}:${util.date()}] `; if (module) msg += `(${module}) `; @@ -703,11 +702,11 @@ LoggerContext.prototype.setLevel = function setLevel(name) { */ LoggerContext.prototype.error = function error(...args) { - const err = args[0]; - if (this.logger.level < Logger.levels.ERROR) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.ERROR, err); @@ -721,11 +720,11 @@ LoggerContext.prototype.error = function error(...args) { */ LoggerContext.prototype.warning = function warning(...args) { - const err = args[0]; - if (this.logger.level < Logger.levels.WARNING) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.WARNING, err); @@ -739,11 +738,11 @@ LoggerContext.prototype.warning = function warning(...args) { */ LoggerContext.prototype.info = function info(...args) { - const err = args[0]; - if (this.logger.level < Logger.levels.INFO) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.INFO, err); @@ -757,11 +756,11 @@ LoggerContext.prototype.info = function info(...args) { */ LoggerContext.prototype.debug = function debug(...args) { - const err = args[0]; - if (this.logger.level < Logger.levels.DEBUG) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.DEBUG, err); @@ -775,11 +774,11 @@ LoggerContext.prototype.debug = function debug(...args) { */ LoggerContext.prototype.spam = function spam(...args) { - const err = args[0]; - if (this.logger.level < Logger.levels.SPAM) return; + const err = args[0]; + if (err instanceof Error) return this.logError(Logger.levels.SPAM, err); @@ -839,12 +838,9 @@ Logger.global = new Logger(); function openStream(filename) { return new Promise((resolve, reject) => { - const stream = fs.createWriteStream(filename, { flags: 'a' }); + /* eslint no-use-before-define: "off" */ - const cleanup = () => { - stream.removeListener('error', onError); - stream.removeListener('open', onOpen); - }; + const stream = fs.createWriteStream(filename, { flags: 'a' }); const onError = (err) => { try { @@ -861,6 +857,11 @@ function openStream(filename) { resolve(stream); }; + const cleanup = () => { + stream.removeListener('error', onError); + stream.removeListener('open', onOpen); + }; + stream.once('error', onError); stream.once('open', onOpen); }); @@ -868,10 +869,7 @@ function openStream(filename) { function closeStream(stream) { return new Promise((resolve, reject) => { - const cleanup = () => { - stream.removeListener('error', onError); - stream.removeListener('close', onClose); - }; + /* eslint no-use-before-define: "off" */ const onError = (err) => { cleanup(); @@ -883,6 +881,11 @@ function closeStream(stream) { resolve(stream); }; + const cleanup = () => { + stream.removeListener('error', onError); + stream.removeListener('close', onClose); + }; + stream.removeAllListeners('error'); stream.removeAllListeners('close'); stream.once('error', onError); diff --git a/lib/node/node.js b/lib/node/node.js index fe03392a..d27615cb 100644 --- a/lib/node/node.js +++ b/lib/node/node.js @@ -352,7 +352,7 @@ Node.prototype.get = function get(name) { return this.http; } - return this.plugins[name]; + return this.plugins[name] || null; }; /** diff --git a/lib/primitives/address.js b/lib/primitives/address.js index d6487a11..483201c5 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -460,6 +460,8 @@ Address.prototype.fromScript = function fromScript(script) { this.version = -1; return this; } + + return null; }; /** @@ -484,6 +486,8 @@ Address.prototype.fromWitness = function fromWitness(witness) { this.version = 0; return this; } + + return null; }; /** @@ -506,6 +510,8 @@ Address.prototype.fromInputScript = function fromInputScript(script) { this.version = -1; return this; } + + return null; }; /** @@ -824,11 +830,11 @@ Address.prototype.isUnknown = function isUnknown() { */ Address.getHash = function getHash(data, enc, network) { - let hash; - if (!data) throw new Error('Object is not an address.'); + let hash; + if (typeof data === 'string') { if (data.length === 40 || data.length === 64) return enc === 'hex' ? data : Buffer.from(data, 'hex'); diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 385bcba0..937aec2a 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -379,7 +379,7 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) { return null; const coinbase = this.txs[0]; - let hash = null; + let hash; for (let i = coinbase.outputs.length - 1; i >= 0; i--) { const output = coinbase.outputs[i]; diff --git a/lib/primitives/input.js b/lib/primitives/input.js index 6fa4932c..5178bcd7 100644 --- a/lib/primitives/input.js +++ b/lib/primitives/input.js @@ -123,7 +123,7 @@ Input.prototype.getType = function getType(coin) { Input.prototype.getRedeem = function getRedeem(coin) { if (this.isCoinbase()) - return; + return null; if (!coin) { if (this.witness.isScripthashInput()) @@ -132,7 +132,7 @@ Input.prototype.getRedeem = function getRedeem(coin) { if (this.script.isScripthashInput()) return this.script.getRedeem(); - return; + return null; } let prev = coin.script; @@ -159,12 +159,12 @@ Input.prototype.getRedeem = function getRedeem(coin) { Input.prototype.getSubtype = function getSubtype(coin) { if (this.isCoinbase()) - return; + return null; const redeem = this.getRedeem(coin); if (!redeem) - return; + return null; const type = redeem.getType(); @@ -181,7 +181,7 @@ Input.prototype.getSubtype = function getSubtype(coin) { Input.prototype.getAddress = function getAddress(coin) { if (this.isCoinbase()) - return; + return null; if (coin) return coin.getAddress(); @@ -202,7 +202,7 @@ Input.prototype.getHash = function getHash(enc) { const addr = this.getAddress(); if (!addr) - return; + return null; return addr.getHash(enc); }; @@ -284,10 +284,9 @@ Input.prototype.toJSON = function toJSON(network, coin) { */ Input.prototype.getJSON = function getJSON(network, coin) { - let addr; - network = Network.get(network); + let addr; if (!coin) { addr = this.getAddress(); if (addr) diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index 49e08a3b..9234e42b 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -58,13 +58,11 @@ function KeyRing(options, network) { */ KeyRing.prototype.fromOptions = function fromOptions(options, network) { - let key = toKey(options); - const script = options.script; - const compressed = options.compressed; - if (!network) network = options.network; + let key = toKey(options); + if (Buffer.isBuffer(key)) return this.fromKey(key, network); @@ -86,10 +84,13 @@ KeyRing.prototype.fromOptions = function fromOptions(options, network) { this.nested = options.nested; } - if (script) - return this.fromScript(key, script, compressed, network); + const script = options.script; + const compress = options.compressed; - this.fromKey(key, compressed, network); + if (script) + return this.fromScript(key, script, compress, network); + + return this.fromKey(key, compress, network); }; /** @@ -121,22 +122,22 @@ KeyRing.prototype.refresh = function refresh() { * Inject data from private key. * @private * @param {Buffer} key - * @param {Boolean?} compressed + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network */ -KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, network) { +KeyRing.prototype.fromPrivate = function fromPrivate(key, compress, network) { assert(Buffer.isBuffer(key), 'Private key must be a buffer.'); assert(secp256k1.privateKeyVerify(key), 'Not a valid private key.'); - if (typeof compressed !== 'boolean') { - network = compressed; - compressed = null; + if (typeof compress !== 'boolean') { + network = compress; + compress = null; } this.network = Network.get(network); this.privateKey = key; - this.publicKey = secp256k1.publicKeyCreate(key, compressed !== false); + this.publicKey = secp256k1.publicKeyCreate(key, compress !== false); return this; }; @@ -144,13 +145,13 @@ KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, network) { /** * Instantiate keyring from a private key. * @param {Buffer} key - * @param {Boolean?} compressed + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ -KeyRing.fromPrivate = function fromPrivate(key, compressed, network) { - return new KeyRing().fromPrivate(key, compressed, network); +KeyRing.fromPrivate = function fromPrivate(key, compress, network) { + return new KeyRing().fromPrivate(key, compress, network); }; /** @@ -171,29 +172,31 @@ KeyRing.prototype.fromPublic = function fromPublic(key, network) { /** * Generate a keyring. * @private + * @param {Boolean?} compress * @param {(Network|NetworkType)?} network * @returns {KeyRing} */ -KeyRing.prototype.generate = function generate(compressed, network) { - if (typeof compressed !== 'boolean') { - network = compressed; - compressed = null; +KeyRing.prototype.generate = function generate(compress, network) { + if (typeof compress !== 'boolean') { + network = compress; + compress = null; } const key = secp256k1.generatePrivateKey(); - return this.fromKey(key, compressed, network); + return this.fromKey(key, compress, network); }; /** * Generate a keyring. + * @param {Boolean?} compress * @param {(Network|NetworkType)?} network * @returns {KeyRing} */ -KeyRing.generate = function generate(compressed, network) { - return new KeyRing().generate(compressed, network); +KeyRing.generate = function generate(compress, network) { + return new KeyRing().generate(compress, network); }; /** @@ -211,19 +214,20 @@ KeyRing.fromPublic = function fromPublic(key, network) { * Inject data from public key. * @private * @param {Buffer} privateKey + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network */ -KeyRing.prototype.fromKey = function fromKey(key, compressed, network) { +KeyRing.prototype.fromKey = function fromKey(key, compress, network) { assert(Buffer.isBuffer(key), 'Key must be a buffer.'); - if (typeof compressed !== 'boolean') { - network = compressed; - compressed = null; + if (typeof compress !== 'boolean') { + network = compress; + compress = null; } if (key.length === 32) - return this.fromPrivate(key, compressed !== false, network); + return this.fromPrivate(key, compress !== false, network); return this.fromPublic(key, network); }; @@ -231,12 +235,13 @@ KeyRing.prototype.fromKey = function fromKey(key, compressed, network) { /** * Instantiate keyring from a public key. * @param {Buffer} publicKey + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ -KeyRing.fromKey = function fromKey(key, compressed, network) { - return new KeyRing().fromKey(key, compressed, network); +KeyRing.fromKey = function fromKey(key, compress, network) { + return new KeyRing().fromKey(key, compress, network); }; /** @@ -244,18 +249,19 @@ KeyRing.fromKey = function fromKey(key, compressed, network) { * @private * @param {Buffer} key * @param {Script} script + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network */ -KeyRing.prototype.fromScript = function fromScript(key, script, compressed, network) { +KeyRing.prototype.fromScript = function fromScript(key, script, compress, network) { assert(script instanceof Script, 'Non-script passed into KeyRing.'); - if (typeof compressed !== 'boolean') { - network = compressed; - compressed = null; + if (typeof compress !== 'boolean') { + network = compress; + compress = null; } - this.fromKey(key, compressed, network); + this.fromKey(key, compress, network); this.script = script; return this; @@ -265,12 +271,13 @@ KeyRing.prototype.fromScript = function fromScript(key, script, compressed, netw * Instantiate keyring from script. * @param {Buffer} key * @param {Script} script + * @param {Boolean?} compress * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ -KeyRing.fromScript = function fromScript(key, script, compressed, network) { - return new KeyRing().fromScript(key, script, compressed, network); +KeyRing.fromScript = function fromScript(key, script, compress, network) { + return new KeyRing().fromScript(key, script, compress, network); }; /** @@ -336,16 +343,16 @@ KeyRing.prototype.fromSecret = function fromSecret(data, network) { const key = br.readBytes(32); - let compressed = false; + let compress = false; if (br.left() > 4) { assert(br.readU8() === 1, 'Bad compression flag.'); - compressed = true; + compress = true; } br.verifyChecksum(); - return this.fromPrivate(key, compressed, network); + return this.fromPrivate(key, compress, network); }; /** @@ -367,7 +374,7 @@ KeyRing.fromSecret = function fromSecret(data, network) { KeyRing.prototype.getPrivateKey = function getPrivateKey(enc) { if (!this.privateKey) - return; + return null; if (enc === 'base58') return this.toSecret(); @@ -410,7 +417,7 @@ KeyRing.prototype.getScript = function getScript() { KeyRing.prototype.getProgram = function getProgram() { if (!this.witness) - return; + return null; if (!this._program) { let program; @@ -436,7 +443,7 @@ KeyRing.prototype.getProgram = function getProgram() { KeyRing.prototype.getNestedHash = function getNestedHash(enc) { if (!this.witness) - return; + return null; if (!this._nestedHash) this._nestedHash = this.getProgram().hash160(); @@ -454,7 +461,7 @@ KeyRing.prototype.getNestedHash = function getNestedHash(enc) { KeyRing.prototype.getNestedAddress = function getNestedAddress(enc) { if (!this.witness) - return; + return null; if (!this._nestedAddress) { const hash = this.getNestedHash(); @@ -491,7 +498,7 @@ KeyRing.prototype.getScriptHash = function getScriptHash(enc) { KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) { if (!this.script) - return; + return null; if (!this._scriptHash160) this._scriptHash160 = this.script.hash160(); @@ -509,7 +516,7 @@ KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) { KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) { if (!this.script) - return; + return null; if (!this._scriptHash256) this._scriptHash256 = this.script.sha256(); @@ -527,7 +534,7 @@ KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) { KeyRing.prototype.getScriptAddress = function getScriptAddress(enc) { if (!this.script) - return; + return null; if (!this._scriptAddress) { let addr; @@ -602,8 +609,10 @@ KeyRing.prototype.getKeyAddress = function getKeyAddress(enc) { KeyRing.prototype.getHash = function getHash(enc) { if (this.nested) return this.getNestedHash(enc); + if (this.script) return this.getScriptHash(enc); + return this.getKeyHash(enc); }; @@ -616,8 +625,10 @@ KeyRing.prototype.getHash = function getHash(enc) { KeyRing.prototype.getAddress = function getAddress(enc) { if (this.nested) return this.getNestedAddress(enc); + if (this.script) return this.getScriptAddress(enc); + return this.getKeyAddress(enc); }; @@ -885,9 +896,9 @@ KeyRing.prototype.fromReader = function fromReader(br, network) { const key = br.readVarBytes(); if (key.length === 32) { - const compressed = br.readU8() === 1; + const compress = br.readU8() === 1; this.privateKey = key; - this.publicKey = secp256k1.publicKeyCreate(key, compressed); + this.publicKey = secp256k1.publicKeyCreate(key, compress); } else { this.publicKey = key; assert(secp256k1.publicKeyVerify(key), 'Invalid public key.'); diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index fc913a14..c72f0e67 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -109,25 +109,24 @@ MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() { MemBlock.prototype.parseCoinbaseHeight = function parseCoinbaseHeight() { const br = new BufferReader(this._raw, true); - let count; br.seek(80); - count = br.readVarint(); + const txCount = br.readVarint(); - if (count === 0) + if (txCount === 0) return -1; br.seek(4); - count = br.readVarint(); + let inCount = br.readVarint(); - if (count === 0) { + if (inCount === 0) { if (br.readU8() !== 0) - count = br.readVarint(); + inCount = br.readVarint(); } - if (count === 0) + if (inCount === 0) return -1; br.seek(36); diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index f397dcc9..07fcca76 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -228,7 +228,7 @@ MerkleBlock.prototype.extractTree = function extractTree() { } const left = traverse(height - 1, pos * 2); - let right = null; + let right; if (pos * 2 + 1 < width(height - 1)) { right = traverse(height - 1, pos * 2 + 1); @@ -563,7 +563,7 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) { return leaves[pos]; const left = hash(height - 1, pos * 2, leaves); - let right = null; + let right; if (pos * 2 + 1 < width(height - 1)) right = hash(height - 1, pos * 2 + 1, leaves); diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index f4a23d62..c145215e 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -733,22 +733,12 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) { // Multisig if (prev.isMultisig()) { - const keys = []; - - // Grab `m` value (number of sigs required). - const m = prev.getSmall(0); + if (vector.getSmall(0) !== 0) + throw new Error('Input has not been templated.'); // Grab `n` value (number of keys). const n = prev.getSmall(prev.length - 2); - // Grab the redeem script's keys to figure - // out where our key should go. - for (let i = 1; i < prev.length - 2; i++) - keys.push(prev.get(i)); - - if (vector.getSmall(0) !== 0) - throw new Error('Input has not been templated.'); - // Too many signature slots. Abort. if (vector.length - 1 > n) return false; @@ -760,6 +750,9 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) { total++; } + // Grab `m` value (number of sigs required). + const m = prev.getSmall(0); + // Signatures are already finalized. if (total === m && vector.length - 1 === m) return true; @@ -769,6 +762,12 @@ MTX.prototype.signVector = function signVector(prev, vector, sig, ring) { while (vector.length - 1 < n) vector.push(opcodes.OP_0); + // Grab the redeem script's keys to figure + // out where our key should go. + const keys = []; + for (let i = 1; i < prev.length - 2; i++) + keys.push(prev.get(i)); + // Find the key index so we can place // the signature in the same index. let keyIndex = util.indexOf(keys, ring.publicKey); @@ -919,17 +918,17 @@ MTX.prototype.isVectorSigned = function isVectorSigned(prev, vector) { // Grab `m` value (number of required sigs). const m = prev.getSmall(0); + // Ensure we have the correct number + // of required signatures. + if (vector.length - 1 !== m) + return false; + // Ensure all members are signatures. for (let i = 1; i < vector.length; i++) { if (!Script.isSignature(vector.get(i))) return false; } - // Ensure we have the correct number - // of required signatures. - if (vector.length - 1 !== m) - return false; - return true; } @@ -1839,8 +1838,10 @@ function sortRandom(a, b) { function sortValue(a, b) { if (a.height === -1 && b.height !== -1) return 1; + if (a.height !== -1 && b.height === -1) return -1; + return b.value - a.value; } diff --git a/lib/primitives/output.js b/lib/primitives/output.js index 74cfeba1..1c76949f 100644 --- a/lib/primitives/output.js +++ b/lib/primitives/output.js @@ -144,8 +144,10 @@ Output.prototype.getAddress = function getAddress() { Output.prototype.getHash = function getHash(enc) { const addr = this.getAddress(); + if (!addr) - return; + return null; + return addr.getHash(enc); }; @@ -204,12 +206,11 @@ Output.prototype.getJSON = function getJSON(network) { Output.prototype.getDustThreshold = function getDustThreshold(rate) { const scale = consensus.WITNESS_SCALE_FACTOR; - let size; if (this.script.isUnspendable()) return 0; - size = this.getSize(); + let size = this.getSize(); if (this.script.isProgram()) { // 75% segwit discount applied to script size. diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 19c67779..7e7fca5d 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -303,7 +303,6 @@ TX.prototype.frame = function frame() { } let raw; - if (this.hasWitness()) raw = this.frameWitness(); else @@ -1116,14 +1115,13 @@ TX.prototype.getAddresses = function getAddresses(view) { */ TX.prototype.getInputHashes = function getInputHashes(view, enc) { - const hashes = []; - if (enc === 'hex') { const [, table] = this._getInputAddresses(view); return Object.keys(table); } const addrs = this.getInputAddresses(view); + const hashes = []; for (const addr of addrs) hashes.push(addr.getHash()); @@ -1137,14 +1135,13 @@ TX.prototype.getInputHashes = function getInputHashes(view, enc) { */ TX.prototype.getOutputHashes = function getOutputHashes(enc) { - const hashes = []; - if (enc === 'hex') { const [, table] = this._getOutputAddresses(); return Object.keys(table); } const addrs = this.getOutputAddresses(); + const hashes = []; for (const addr of addrs) hashes.push(addr.getHash()); @@ -1159,14 +1156,13 @@ TX.prototype.getOutputHashes = function getOutputHashes(enc) { */ TX.prototype.getHashes = function getHashes(view, enc) { - const hashes = []; - if (enc === 'hex') { const [, table] = this._getAddresses(view); return Object.keys(table); } const addrs = this.getAddresses(view); + const hashes = []; for (const addr of addrs) hashes.push(addr.getHash()); @@ -1407,9 +1403,6 @@ TX.prototype.isSane = function isSane() { */ TX.prototype.checkSanity = function checkSanity() { - const prevout = new Set(); - let total = 0; - if (this.inputs.length === 0) return [false, 'bad-txns-vin-empty', 100]; @@ -1419,6 +1412,8 @@ TX.prototype.checkSanity = function checkSanity() { if (this.getBaseSize() > consensus.MAX_BLOCK_SIZE) return [false, 'bad-txns-oversize', 100]; + let total = 0; + for (const output of this.outputs) { if (output.value < 0) return [false, 'bad-txns-vout-negative', 100]; @@ -1432,10 +1427,14 @@ TX.prototype.checkSanity = function checkSanity() { return [false, 'bad-txns-txouttotal-toolarge', 100]; } + const prevout = new Set(); + for (const input of this.inputs) { const key = input.prevout.toKey(); + if (prevout.has(key)) return [false, 'bad-txns-inputs-duplicate', 100]; + prevout.add(key); } @@ -1479,8 +1478,6 @@ TX.prototype.isStandard = function isStandard() { */ TX.prototype.checkStandard = function checkStandard() { - let nulldata = 0; - if (this.version < 1 || this.version > policy.MAX_TX_VERSION) return [false, 'version', 0]; @@ -1495,6 +1492,8 @@ TX.prototype.checkStandard = function checkStandard() { return [false, 'scriptsig-not-pushonly', 0]; } + let nulldata = 0; + for (const output of this.outputs) { if (!output.script.isStandard()) return [false, 'scriptpubkey', 0]; @@ -1706,10 +1705,10 @@ TX.prototype.verifyInputs = function verifyInputs(view, height) { */ TX.prototype.checkInputs = function checkInputs(view, height) { - let total = 0; - assert(typeof height === 'number'); + let total = 0; + for (const {prevout} of this.inputs) { let coin = view.getEntry(prevout); @@ -1781,16 +1780,16 @@ TX.prototype.getModifiedSize = function getModifiedSize(size) { */ TX.prototype.getPriority = function getPriority(view, height, size) { - let sum = 0; - assert(typeof height === 'number', 'Must pass in height.'); if (this.isCoinbase()) - return sum; + return 0; if (size == null) size = this.getVirtualSize(); + let sum = 0; + for (const {prevout} of this.inputs) { const coin = view.getOutput(prevout); @@ -1818,10 +1817,10 @@ TX.prototype.getPriority = function getPriority(view, height, size) { */ TX.prototype.getChainValue = function getChainValue(view) { - let value = 0; - if (this.isCoinbase()) - return value; + return 0; + + let value = 0; for (const {prevout} of this.inputs) { const coin = view.getOutput(prevout); @@ -1829,9 +1828,9 @@ TX.prototype.getChainValue = function getChainValue(view) { if (!coin) continue; - const coinHeight = view.getHeight(prevout); + const height = view.getHeight(prevout); - if (coinHeight === -1) + if (height === -1) continue; value += coin.value; @@ -1917,11 +1916,11 @@ TX.prototype.getRate = function getRate(view, size) { */ TX.prototype.getPrevout = function getPrevout() { - const prevout = Object.create(null); - if (this.isCoinbase()) return []; + const prevout = Object.create(null); + for (const input of this.inputs) prevout[input.prevout.hash] = true; diff --git a/lib/protocol/network.js b/lib/protocol/network.js index 7330299e..fd973947 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -116,8 +116,10 @@ Network.prototype._init = function _init() { Network.prototype.byBit = function byBit(bit) { const index = util.binarySearch(this.deploys, bit, cmpBit); + if (index === -1) return null; + return this.deploys[index]; }; diff --git a/lib/protocol/policy.js b/lib/protocol/policy.js index 8d9ffecc..8a3652c3 100644 --- a/lib/protocol/policy.js +++ b/lib/protocol/policy.js @@ -217,8 +217,6 @@ exports.BLOCK_PRIORITY_THRESHOLD = exports.FREE_THRESHOLD; */ exports.getMinFee = function getMinFee(size, rate) { - let fee; - if (rate == null) rate = exports.MIN_RELAY; @@ -228,7 +226,7 @@ exports.getMinFee = function getMinFee(size, rate) { if (size === 0) return 0; - fee = Math.floor(rate * size / 1000); + let fee = Math.floor(rate * size / 1000); if (fee === 0 && rate > 0) fee = rate; @@ -246,8 +244,6 @@ exports.getMinFee = function getMinFee(size, rate) { */ exports.getRoundFee = function getRoundFee(size, rate) { - let fee; - if (rate == null) rate = exports.MIN_RELAY; @@ -257,7 +253,7 @@ exports.getRoundFee = function getRoundFee(size, rate) { if (size === 0) return 0; - fee = rate * Math.ceil(size / 1000); + let fee = rate * Math.ceil(size / 1000); if (fee === 0 && rate > 0) fee = rate; diff --git a/lib/protocol/timedata.js b/lib/protocol/timedata.js index 2dfe1b5a..8d8a30e7 100644 --- a/lib/protocol/timedata.js +++ b/lib/protocol/timedata.js @@ -49,14 +49,14 @@ util.inherits(TimeData, EventEmitter); */ TimeData.prototype.add = function add(id, time) { - const sample = time - util.now(); - if (this.samples.length >= this.limit) return; if (this.known.has(id)) return; + const sample = time - util.now(); + this.known.set(id, sample); util.binaryInsert(this.samples, sample, compare); diff --git a/lib/script/common.js b/lib/script/common.js index 4a51265b..227f0dae 100644 --- a/lib/script/common.js +++ b/lib/script/common.js @@ -555,50 +555,49 @@ exports.formatCode = function formatCode(code) { const out = []; for (const op of code) { - const data = op.data; - let value = op.value; + if (op.data) { + // Direct push + if (!exports.opcodesByVal[op.value]) { + let size = op.value.toString(16); + if (size.length < 2) + size = '0' + size; + out.push(`0x${size} 0x${op.data.toString('hex')}`); + continue; + } - if (data) { - let size = data.length.toString(16); + // Pushdatas + const symbol = exports.opcodesByVal[op.value]; + + let size = op.data.length.toString(16); while (size.length % 2 !== 0) size = '0' + size; - if (!exports.opcodesByVal[value]) { - value = value.toString(16); - if (value.length < 2) - value = '0' + value; - value = `0x${value} 0x${data.toString('hex')}`; - out.push(value); - continue; - } + out.push(`${symbol} 0x${size} 0x${op.data.toString('hex')}`); - value = exports.opcodesByVal[value]; - value = `${value} 0x${size} 0x${data.toString('hex')}`; - out.push(value); continue; } - assert(typeof value === 'number'); - - if (exports.opcodesByVal[value]) { - value = exports.opcodesByVal[value]; - out.push(value); + // Opcodes + if (exports.opcodesByVal[op.value]) { + const symbol = exports.opcodesByVal[op.value]; + out.push(symbol); continue; } - if (value === -1) { + // Bad push + if (op.value === -1) { out.push('OP_INVALIDOPCODE'); break; } - value = value.toString(16); + // Unknown opcodes + let symbol = op.value.toString(16); - if (value.length < 2) - value = '0' + value; + if (symbol.length < 2) + symbol = '0' + symbol; - value = `0x${value}`; - out.push(value); + out.push(`0x${symbol}`); } return out.join(' '); @@ -646,29 +645,28 @@ exports.formatItem = function formatItem(data, decode) { */ exports.formatASM = function formatASM(code, decode) { + if (code.length > 0) { + if (code[0].value === exports.opcodes.OP_RETURN) + decode = false; + } + const out = []; - if (code.length > 0 && code[0].value === exports.opcodes.OP_RETURN) - decode = false; - for (const op of code) { - let data = op.data; - let value = op.value; - - if (value === -1) { + if (op.value === -1) { out.push('[error]'); break; } - if (data) { - data = exports.formatItem(data, decode); + if (op.data) { + const data = exports.formatItem(op.data, decode); out.push(data); continue; } - value = exports.opcodesByVal[value] || 'OP_UNKNOWN'; + const symbol = exports.opcodesByVal[op.value] || 'OP_UNKNOWN'; - out.push(value); + out.push(symbol); } return out.join(' '); diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 49481f30..8eae5dc5 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -186,7 +186,6 @@ Opcode.prototype.getSize = function getSize() { Opcode.prototype.fromReader = function fromReader(br) { const op = br.readU8(); - let size; if (op >= 0x01 && op <= 0x4b) { if (br.left() < op) { @@ -199,6 +198,8 @@ Opcode.prototype.fromReader = function fromReader(br) { return this; } + let size; + switch (op) { case opcodes.OP_PUSHDATA1: if (br.left() < 1) { diff --git a/lib/script/script.js b/lib/script/script.js index c82d5b2b..616b9fd2 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -411,11 +411,11 @@ Script.fromJSON = function fromJSON(json) { */ Script.prototype.getSubscript = function getSubscript(lastSep) { - const code = []; - if (lastSep === 0) return this.clone(); + const code = []; + for (let i = lastSep; i < this.code.length; i++) { const op = this.code[i]; @@ -483,25 +483,26 @@ Script.prototype.removeSeparators = function removeSeparators() { */ Script.prototype.execute = function execute(stack, flags, tx, index, value, version) { - const state = []; - const alt = []; - let lastSep = 0; - let opCount = 0; - let negate = 0; - let minimal = false; - if (flags == null) flags = Script.flags.STANDARD_VERIFY_FLAGS; if (version == null) version = 0; - if (flags & Script.flags.VERIFY_MINIMALDATA) - minimal = true; - if (this.getSize() > consensus.MAX_SCRIPT_SIZE) throw new ScriptError('SCRIPT_SIZE'); + const state = []; + const alt = []; + + let lastSep = 0; + let opCount = 0; + let negate = 0; + let minimal = false; + + if (flags & Script.flags.VERIFY_MINIMALDATA) + minimal = true; + for (let ip = 0; ip < this.code.length; ip++) { const op = this.code[ip]; @@ -1667,15 +1668,15 @@ Script.fromCommitment = function fromCommitment(hash, flags) { Script.prototype.getRedeem = function getRedeem() { if (this.code.length === 0) - return; + return null; if (!this.isPushOnly()) - return; + return null; const redeem = this.code[this.code.length - 1]; if (!redeem.data) - return; + return null; return Script.fromRaw(redeem.data); }; @@ -1987,7 +1988,7 @@ Script.prototype.isCommitment = function isCommitment() { Script.prototype.getCommitmentHash = function getCommitmentHash() { if (!this.isCommitment()) - return; + return null; return this.raw.slice(6, 38); }; @@ -2021,7 +2022,7 @@ Script.prototype.isProgram = function isProgram() { Script.prototype.toProgram = function toProgram() { if (!this.isProgram()) - return; + return null; const version = common.getSmall(this.raw[0]); const data = this.raw.slice(2); @@ -2671,12 +2672,11 @@ Script.prototype.fromString = function fromString(code) { if (code.length === 0) return this; - code = code.split(/\s+/); - + const items = code.split(/\s+/); const bw = new BufferWriter(); - for (let op of code) { - let symbol = op; + for (const item of items) { + let symbol = item; if (!util.isUpperCase(symbol)) symbol = symbol.toUpperCase(); @@ -2684,29 +2684,37 @@ Script.prototype.fromString = function fromString(code) { if (!util.startsWith(symbol, 'OP_')) symbol = `OP_${symbol}`; - if (opcodes[symbol] == null) { - if (op[0] === '\'') { - assert(op[op.length - 1] === '\'', 'Unknown opcode.'); - op = op.slice(1, -1); - op = Opcode.fromString(op); + const value = opcodes[symbol]; + + if (value == null) { + if (item[0] === '\'') { + assert(item[item.length - 1] === '\'', 'Unknown opcode.'); + const str = item.slice(1, -1); + const op = Opcode.fromString(str); bw.writeBytes(op.toRaw()); continue; } - if (/^-?\d+$/.test(op)) { - op = new BN(op, 10); - op = Opcode.fromNumber(op); + + if (/^-?\d+$/.test(item)) { + const num = new BN(item, 10); + const op = Opcode.fromNumber(num); bw.writeBytes(op.toRaw()); continue; } - assert(op.indexOf('0x') === 0, 'Unknown opcode.'); - op = op.substring(2); - assert(util.isHex(op), 'Unknown opcode.'); - op = Buffer.from(op, 'hex'); - bw.writeBytes(op); + + assert(item.indexOf('0x') === 0, 'Unknown opcode.'); + + const str = item.substring(2); + const data = Buffer.from(str, 'hex'); + + assert(data.length === str.length / 2, 'Unknown opcode.'); + + bw.writeBytes(data); + continue; } - bw.writeU8(opcodes[symbol]); + bw.writeU8(value); } return this.fromRaw(bw.render()); @@ -2738,8 +2746,6 @@ Script.fromString = function fromString(code) { */ Script.verify = function verify(input, witness, output, tx, i, value, flags) { - let hadWitness = false; - if (flags == null) flags = Script.flags.STANDARD_VERIFY_FLAGS; @@ -2766,6 +2772,8 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { if (stack.length === 0 || !Script.bool(stack.top(-1))) throw new ScriptError('EVAL_FALSE'); + let hadWitness = false; + if ((flags & Script.flags.VERIFY_WITNESS) && output.isProgram()) { hadWitness = true; @@ -2852,12 +2860,13 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i, value) { const program = output.toProgram(); - const stack = witness.toStack(); - let redeem; assert(program, 'verifyProgram called on non-witness-program.'); assert((flags & Script.flags.VERIFY_WITNESS) !== 0); + const stack = witness.toStack(); + let redeem; + if (program.version === 0) { if (program.data.length === 32) { if (stack.length === 0) @@ -2923,11 +2932,6 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i, val */ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, value) { - let mastRoot = new BufferWriter(); - let scriptRoot = new BufferWriter(); - let scripts = new BufferWriter(); - let version = 0; - assert(program.version === 1); assert((flags & Script.flags.VERIFY_MAST) !== 0); @@ -2943,11 +2947,14 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va throw new ScriptError('INVALID_MAST_STACK'); let ops = subscripts; + let scriptRoot = new BufferWriter(); scriptRoot.writeU8(subscripts); if (metadata[metadata.length - 1] === 0x00) throw new ScriptError('INVALID_MAST_STACK'); + let version = 0; + for (let j = 1; j < metadata.length; j++) version |= metadata[i] << 8 * (j - 1); @@ -2959,6 +2966,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va throw new ScriptError('DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM'); } + let mastRoot = new BufferWriter(); mastRoot.writeU32(version); const pathdata = stack.top(-2); @@ -3004,6 +3012,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va throw new ScriptError('INVALID_MAST_STACK'); } + let scripts = new BufferWriter(); scripts.writeBytes(output.raw); for (let j = 0; j < subscripts; j++) { diff --git a/lib/script/scriptnum.js b/lib/script/scriptnum.js index 32cdcef8..89b71fc2 100644 --- a/lib/script/scriptnum.js +++ b/lib/script/scriptnum.js @@ -162,12 +162,11 @@ ScriptNum.prototype.toJSON = function toJSON() { }; ScriptNum.prototype.fromString = function fromString(str, base) { - let nonzero = 0; - let negative = false; - if (!base) base = 10; + let negative = false; + if (str[0] === '-') { assert(str.length > 1, 'Non-numeric string passed.'); str = str.substring(1); @@ -179,6 +178,7 @@ ScriptNum.prototype.fromString = function fromString(str, base) { this.value = 0; if (base === 10 || base === 'dec') { + let nonzero = 0; for (let i = 0; i < str.length; i++) { let ch = str[i]; @@ -204,6 +204,7 @@ ScriptNum.prototype.fromString = function fromString(str, base) { } if (base === 16 || base === 'hex') { + let nonzero = 0; for (let i = 0; i < str.length; i++) { let ch = str[i]; @@ -320,20 +321,20 @@ ScriptNum.fromRaw = function fromRaw(data, minimal, limit) { ScriptNum.prototype.toRaw = function toRaw() { let value = this.value; - let negative = false; - let offset, size; // Zeroes are always empty arrays. if (value === 0) return EMPTY_ARRAY; // Need to append sign bit. + let negative = false; if (value < 0) { negative = true; value = -value; } // Gauge buffer size. + let offset, size; if (value <= 0xff) { offset = (value & 0x80) ? 1 : 0; size = 1; diff --git a/lib/script/stack.js b/lib/script/stack.js index 0cfddacf..753353de 100644 --- a/lib/script/stack.js +++ b/lib/script/stack.js @@ -183,7 +183,7 @@ Stack.prototype.remove = function remove(i) { i = this.items.length + i; if (i >= this.items.length) - return; + return undefined; return this.items.splice(i, 1)[0]; }; diff --git a/lib/script/witness.js b/lib/script/witness.js index 7af3aa64..6e4defa3 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -78,13 +78,11 @@ Witness.prototype.__defineSetter__('length', function(length) { Witness.prototype.fromOptions = function fromOptions(options) { assert(options, 'Witness data is required.'); - let items = options.items; + if (Array.isArray(options)) + return this.fromArray(options); - if (!items) - items = options; - - if (items) - this.fromArray(items); + if (options.items) + return this.fromArray(options.items); return this; }; @@ -294,12 +292,12 @@ Witness.prototype.test = function test(filter) { Witness.prototype.getRedeem = function getRedeem() { if (this.items.length === 0) - return; + return null; const redeem = this.items[this.items.length - 1]; if (!redeem) - return; + return null; return Script.fromRaw(redeem); }; @@ -496,8 +494,10 @@ Witness.prototype.getSmall = function getSmall(i) { Witness.prototype.getNumber = function getNumber(i) { const item = this.items[i]; + if (!item || item.length > 5) - return; + return null; + return common.num(item, false, 5); }; @@ -509,8 +509,10 @@ Witness.prototype.getNumber = function getNumber(i) { Witness.prototype.getString = function getString(i) { const item = this.items[i]; + if (!item) - return; + return null; + return item.toString('utf8'); }; diff --git a/lib/utils/asn1.js b/lib/utils/asn1.js index 26841774..8dae485b 100644 --- a/lib/utils/asn1.js +++ b/lib/utils/asn1.js @@ -123,8 +123,10 @@ ASN1.readSeq = function readSeq(br) { ASN1.implicit = function implicit(br, type) { const tag = ASN1.readTag(br); + if (tag.type !== type) throw new Error(`Unexpected tag: ${tag.type}.`); + return tag; }; @@ -138,10 +140,12 @@ ASN1.implicit = function implicit(br, type) { ASN1.explicit = function explicit(br, type) { const offset = br.offset; const tag = ASN1.readTag(br); + if (tag.type !== type) { br.offset = offset; return false; } + return true; }; @@ -158,15 +162,15 @@ ASN1.seq = function seq(br) { /** * Read implicit int. * @param {BufferReader} br - * @param {Boolean?} readNum + * @param {Boolean?} cast * @returns {Buffer|Number} */ -ASN1.readInt = function readInt(br, readNum) { +ASN1.readInt = function readInt(br, cast) { const tag = ASN1.implicit(br, 0x02); const num = br.readBytes(tag.size); - if (readNum) + if (cast) return num.readUIntBE(0, num.length); return num; @@ -183,6 +187,7 @@ ASN1.readInt = function readInt(br, readNum) { ASN1.readExplicitInt = function readExplicitInt(br, type, readNum) { if (!ASN1.explicit(br, type)) return -1; + return ASN1.readInt(br, readNum); }; @@ -210,7 +215,7 @@ ASN1.readString = function readString(br) { switch (tag.type) { case 0x03: { // bitstr const str = br.readBytes(tag.size); - return ASN1.alignBitstr(str); + return ASN1.alignBitstr(str).toString('utf8'); } // Note: // Fuck all these. diff --git a/lib/utils/asyncemitter.js b/lib/utils/asyncemitter.js index 78e99a9c..32d0654c 100644 --- a/lib/utils/asyncemitter.js +++ b/lib/utils/asyncemitter.js @@ -183,7 +183,7 @@ AsyncEmitter.prototype.listeners = function listeners(type) { const listeners = this._events[type]; if (!listeners) - return result; + return []; const result = []; @@ -236,7 +236,7 @@ AsyncEmitter.prototype.emit = function emit(type) { return; } - let args = null; + let args; for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; @@ -299,7 +299,7 @@ AsyncEmitter.prototype.fire = async function fire(type) { return; } - let args = null; + let args; for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; diff --git a/lib/utils/asyncobject.js b/lib/utils/asyncobject.js index 86089291..eb11befb 100644 --- a/lib/utils/asyncobject.js +++ b/lib/utils/asyncobject.js @@ -197,7 +197,7 @@ AsyncObject.prototype.fireHook = async function fireHook(type) { if (!listeners || listeners.length === 0) return; - let args = null; + let args; for (const handler of listeners) { switch (arguments.length) { diff --git a/lib/utils/base32.js b/lib/utils/base32.js index 8d344523..a70f4f92 100644 --- a/lib/utils/base32.js +++ b/lib/utils/base32.js @@ -27,10 +27,9 @@ exports.encode = function encode(data) { let str = ''; let mode = 0; let left = 0; - let i, ch; - for (i = 0; i < data.length; i++) { - ch = data[i]; + for (let i = 0; i < data.length; i++) { + const ch = data[i]; switch (mode) { case 0: str += base32[ch >>> 3]; @@ -64,7 +63,7 @@ exports.encode = function encode(data) { if (mode > 0) { str += base32[left]; - for (i = 0; i < padding[mode]; i++) + for (let i = 0; i < padding[mode]; i++) str += '='; } @@ -82,10 +81,10 @@ exports.decode = function decode(str) { let mode = 0; let left = 0; let j = 0; - let i, ch; + let i; for (i = 0; i < str.length; i++) { - ch = unbase32[str[i]]; + const ch = unbase32[str[i]]; if (ch == null) break; diff --git a/lib/utils/bech32.js b/lib/utils/bech32.js index 82c87799..b4d08df6 100644 --- a/lib/utils/bech32.js +++ b/lib/utils/bech32.js @@ -74,7 +74,6 @@ function polymod(pre) { */ function serialize(hrp, data) { - let str = ''; let chk = 1; let i; @@ -92,6 +91,8 @@ function serialize(hrp, data) { chk = polymod(chk); + let str = ''; + for (let i = 0; i < hrp.length; i++) { const ch = hrp.charCodeAt(i); chk = polymod(chk) ^ (ch & 0x1f); diff --git a/lib/utils/bloom.js b/lib/utils/bloom.js index 936d7d33..3d84d83b 100644 --- a/lib/utils/bloom.js +++ b/lib/utils/bloom.js @@ -262,8 +262,8 @@ Bloom.fromRate = function fromRate(items, rate, update) { assert(typeof rate === 'number', '`rate` must be a number.'); assert(rate >= 0 && rate <= 1, '`rate` must be between 0.0 and 1.0.'); - let size = (-1 / LN2SQUARED * items * Math.log(rate)) | 0; - size = Math.max(8, size); + const bits = (-1 / LN2SQUARED * items * Math.log(rate)) | 0; + const size = Math.max(8, bits); if (update !== -1) { assert(size <= Bloom.MAX_BLOOM_FILTER_SIZE * 8, diff --git a/lib/utils/co.js b/lib/utils/co.js index 9fad1b29..b1d44ea5 100644 --- a/lib/utils/co.js +++ b/lib/utils/co.js @@ -21,6 +21,8 @@ const assert = require('assert'); function exec(gen) { return new Promise((resolve, reject) => { + /* eslint no-use-before-define: "off" */ + const step = (value, rejection) => { let next; diff --git a/lib/utils/encoding.js b/lib/utils/encoding.js index 245b096e..d56d7e06 100644 --- a/lib/utils/encoding.js +++ b/lib/utils/encoding.js @@ -899,12 +899,12 @@ encoding.readVarint2BN = function readVarint2BN(data, off) { */ encoding.writeVarint2BN = function writeVarint2BN(dst, num, off) { - const tmp = []; - let len = 0; - if (num.bitLength() <= 53) return encoding.writeVarint2(dst, num.toNumber()); + const tmp = []; + let len = 0; + for (;;) { tmp[len] = (num.words[0] & 0x7f) | (len ? 0x80 : 0x00); if (num.cmpn(0x7f) <= 0) @@ -929,13 +929,13 @@ encoding.writeVarint2BN = function writeVarint2BN(dst, num, off) { */ encoding.sizeVarint2BN = function sizeVarint2BN(num) { - let size = 0; - if (num.bitLength() <= 53) return encoding.sizeVarint(num.toNumber()); num = num.clone(); + let size = 0; + for (;;) { size++; if (num.cmpn(0x7f) <= 0) diff --git a/lib/utils/fs.js b/lib/utils/fs.js index bc0c02c8..28f71067 100644 --- a/lib/utils/fs.js +++ b/lib/utils/fs.js @@ -85,11 +85,11 @@ exports.writeFile = co.promisify(fs.writeFile); exports.writeFileSync = fs.writeFileSync; exports.mkdirpSync = function mkdirpSync(dir, mode) { - let [path, parts] = getParts(dir); - if (mode == null) mode = 0o750; + let [path, parts] = getParts(dir); + for (const part of parts) { path += part; @@ -109,11 +109,11 @@ exports.mkdirpSync = function mkdirpSync(dir, mode) { }; exports.mkdirp = async function mkdirp(dir, mode) { - let [path, parts] = getParts(dir); - if (mode == null) mode = 0o750; + let [path, parts] = getParts(dir); + for (const part of parts) { path += part; @@ -138,6 +138,7 @@ function getParts(path) { path = path.replace(/\/+\.?$/, ''); const parts = path.split(/\/+/); + let root = ''; if (process.platform === 'win32') { diff --git a/lib/utils/gcs.js b/lib/utils/gcs.js index bb1f0f71..29e7efaa 100644 --- a/lib/utils/gcs.js +++ b/lib/utils/gcs.js @@ -63,12 +63,11 @@ GCSFilter.prototype.match = function match(key, data) { }; GCSFilter.prototype.matchAny = function matchAny(key, items) { + assert(items.length > 0); + const br = new BitReader(this.data); const last1 = new Int64(0); const values = []; - let i, last2; - - assert(items.length > 0); for (const item of items) { const hash = siphash(item, key).imod(this.m); @@ -77,8 +76,8 @@ GCSFilter.prototype.matchAny = function matchAny(key, items) { values.sort(compare); - last2 = values[0]; - i = 1; + let last2 = values[0]; + let i = 1; for (;;) { const cmp = last1.cmp(last2); @@ -160,10 +159,6 @@ GCSFilter.prototype.toRaw = function toRaw() { }; GCSFilter.prototype.fromItems = function fromItems(P, key, items) { - const bw = new BitWriter(); - let last = new Int64(0); - const values = []; - assert(typeof P === 'number' && isFinite(P)); assert(P >= 0 && P <= 32); @@ -178,6 +173,8 @@ GCSFilter.prototype.fromItems = function fromItems(P, key, items) { this.p = P; this.m = Int64(this.n).ishln(this.p); + const values = []; + for (const item of items) { assert(Buffer.isBuffer(item)); const hash = siphash(item, key).imod(this.m); @@ -186,6 +183,9 @@ GCSFilter.prototype.fromItems = function fromItems(P, key, items) { values.sort(compare); + const bw = new BitWriter(); + let last = new Int64(0); + for (const hash of values) { const rem = hash.sub(last).imaskn(this.p); const value = hash.sub(last).isub(rem).ishrn(this.p); @@ -439,8 +439,6 @@ BitReader.prototype.readBit = function readBit() { }; BitReader.prototype.readByte = function readByte() { - let ch; - if (this.pos >= this.stream.length) throw new Error('EOF'); @@ -454,12 +452,12 @@ BitReader.prototype.readByte = function readByte() { } if (this.remain === 8) { - ch = this.stream[this.pos]; + const ch = this.stream[this.pos]; this.pos += 1; return ch; } - ch = this.stream[this.pos] & ((1 << this.remain) - 1); + let ch = this.stream[this.pos] & ((1 << this.remain) - 1); ch <<= 8 - this.remain; this.pos += 1; @@ -473,11 +471,11 @@ BitReader.prototype.readByte = function readByte() { }; BitReader.prototype.readBits = function readBits(count) { - let num = 0; - assert(count >= 0); assert(count <= 32); + let num = 0; + while (count >= 8) { num <<= 8; num |= this.readByte(); @@ -494,11 +492,11 @@ BitReader.prototype.readBits = function readBits(count) { }; BitReader.prototype.readBits64 = function readBits(count) { - const num = new Int64(); - assert(count >= 0); assert(count <= 64); + const num = new Int64(); + if (count > 32) { num.hi = this.readBits(count - 32); num.lo = this.readBits(32); diff --git a/lib/utils/heap.js b/lib/utils/heap.js index a09e9f3e..17eef4cd 100644 --- a/lib/utils/heap.js +++ b/lib/utils/heap.js @@ -80,7 +80,7 @@ Heap.prototype.insert = function insert(item) { Heap.prototype.shift = function shift() { if (this.items.length === 0) - return; + return null; const n = this.items.length - 1; @@ -98,12 +98,12 @@ Heap.prototype.shift = function shift() { Heap.prototype.remove = function remove(i) { if (this.items.length === 0) - return; + return null; const n = this.items.length - 1; if (i < 0 || i > n) - return; + return null; if (n !== i) { this.swap(i, n); diff --git a/lib/utils/ip.js b/lib/utils/ip.js index 50384343..18937391 100644 --- a/lib/utils/ip.js +++ b/lib/utils/ip.js @@ -269,10 +269,10 @@ IP.isMapped = function isMapped(raw) { */ IP.toBuffer = function toBuffer(str) { - const raw = Buffer.allocUnsafe(16); - assert(typeof str === 'string'); + const raw = Buffer.allocUnsafe(16); + if (IP.isV4String(str)) { raw.fill(0); raw[10] = 0xff; @@ -329,8 +329,6 @@ IP.parseV4 = function parseV4(str, raw, offset) { IP.parseV6 = function parseV6(str, raw, offset) { const parts = str.split(':'); let missing = 8 - parts.length; - const start = offset; - let colon = false; assert(parts.length >= 2, 'Not an IPv6 address.'); @@ -339,6 +337,9 @@ IP.parseV6 = function parseV6(str, raw, offset) { missing--; } + const start = offset; + let colon = false; + for (let i = 0; i < parts.length; i++) { let word = parts[i]; @@ -855,13 +856,13 @@ IP.getReachability = function getReachability(src, dest) { const IPV6_STRONG = 5; const PRIVATE = 6; + if (!IP.isRoutable(src)) + return UNREACHABLE; + const srcNet = IP.getNetwork(src); const destNet = IP.getNetwork(dest); const types = IP.types; - if (!IP.isRoutable(src)) - return UNREACHABLE; - switch (destNet) { case types.IPV4: switch (srcNet) { @@ -958,16 +959,7 @@ IP.hasPrefix = function hasPrefix(raw, prefix) { IP.isEqual = function isEqual(a, b) { assert(a.length === 16); assert(b.length === 16); - - if (a.compare) - return a.compare(b) === 0; - - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) - return false; - } - - return true; + return a.equals(b); }; /** @@ -994,7 +986,6 @@ IP.getInterfaces = function _getInterfaces(name, family) { continue; let raw; - try { raw = IP.toBuffer(details.address); } catch (e) { diff --git a/lib/utils/list.js b/lib/utils/list.js index 03815eef..75844e17 100644 --- a/lib/utils/list.js +++ b/lib/utils/list.js @@ -55,7 +55,7 @@ List.prototype.shift = function shift() { const item = this.head; if (!item) - return; + return null; this.remove(item); @@ -91,7 +91,7 @@ List.prototype.pop = function pop() { const item = this.tail; if (!item) - return; + return null; this.remove(item); diff --git a/lib/utils/lock.js b/lib/utils/lock.js index 3e341cb1..8b0e7fa7 100644 --- a/lib/utils/lock.js +++ b/lib/utils/lock.js @@ -174,10 +174,10 @@ Lock.prototype.destroy = function destroy() { this.destroyed = true; - const jobs = this.jobs.slice(); + const jobs = this.jobs; this.busy = false; - this.jobs.length = 0; + this.jobs = []; this.map.clear(); this.current = null; diff --git a/lib/utils/lru.js b/lib/utils/lru.js index 43f5dd51..4c0b2ef0 100644 --- a/lib/utils/lru.js +++ b/lib/utils/lru.js @@ -62,7 +62,6 @@ LRU.prototype._compact = function _compact() { return; let item, next; - for (item = this.head; item; item = next) { if (this.size <= this.capacity) break; @@ -150,14 +149,14 @@ LRU.prototype.set = function set(key, value) { LRU.prototype.get = function get(key) { if (this.capacity === 0) - return; + return null; key = key + ''; const item = this.map.get(key); if (!item) - return; + return null; this._removeList(item); this._appendList(item); @@ -185,7 +184,7 @@ LRU.prototype.has = function get(key) { LRU.prototype.remove = function remove(key) { if (this.capacity === 0) - return; + return false; key = key + ''; diff --git a/lib/utils/mappedlock.js b/lib/utils/mappedlock.js index b15fd63e..a9d7fe97 100644 --- a/lib/utils/mappedlock.js +++ b/lib/utils/mappedlock.js @@ -132,10 +132,10 @@ MappedLock.prototype.unlock = function unlock(key) { */ MappedLock.prototype.destroy = function destroy() { - const map = this.jobs; - assert(!this.destroyed, 'Lock is already destroyed.'); + const map = this.jobs; + this.destroyed = true; this.jobs = new Map(); diff --git a/lib/utils/murmur3.js b/lib/utils/murmur3.js index 0bb4c56c..57bda5db 100644 --- a/lib/utils/murmur3.js +++ b/lib/utils/murmur3.js @@ -72,14 +72,14 @@ function mul32(a, b) { const blo = b & 0xffff; const ahi = a >>> 16; const bhi = b >>> 16; - let r, lo, hi; - lo = alo * blo; - hi = (ahi * blo + bhi * alo) & 0xffff; + let lo = alo * blo; + let hi = (ahi * blo + bhi * alo) & 0xffff; hi += lo >>> 16; lo &= 0xffff; - r = (hi << 16) | lo; + + let r = (hi << 16) | lo; if (r < 0) r += 0x100000000; diff --git a/lib/utils/pem.js b/lib/utils/pem.js index 12338f58..72218529 100644 --- a/lib/utils/pem.js +++ b/lib/utils/pem.js @@ -22,41 +22,56 @@ const PEM = exports; */ PEM.parse = function parse(pem) { - let buf = ''; const chunks = []; - let s, tag, type; + let chunk = ''; + let tag; while (pem.length) { - if (s = /^-----BEGIN ([^\-]+)-----/.exec(pem)) { - pem = pem.substring(s[0].length); - tag = s[1]; + let m; + + if (m = /^-----BEGIN ([^\-]+)-----/.exec(pem)) { + pem = pem.substring(m[0].length); + tag = m[1]; continue; } - if (s = /^-----END ([^\-]+)-----/.exec(pem)) { - pem = pem.substring(s[0].length); - assert(tag === s[1], 'Tag mismatch.'); - buf = Buffer.from(buf, 'base64'); - type = tag.split(' ')[0].toLowerCase(); - chunks.push({ tag: tag, type: type, data: buf }); - buf = ''; + + if (m = /^-----END ([^\-]+)-----/.exec(pem)) { + pem = pem.substring(m[0].length); + + assert(tag === m[1], 'Tag mismatch.'); + + const type = tag.split(' ')[0].toLowerCase(); + const data = Buffer.from(chunk, 'base64'); + + chunks.push({ + tag: tag, + type: type, + data: data + }); + + chunk = ''; tag = null; + continue; } - if (s = /^[a-zA-Z0-9\+=\/]+/.exec(pem)) { - pem = pem.substring(s[0].length); - buf += s[0]; + + if (m = /^[a-zA-Z0-9\+=\/]+/.exec(pem)) { + pem = pem.substring(m[0].length); + chunk += m[0]; continue; } - if (s = /^\s+/.exec(pem)) { - pem = pem.substring(s[0].length); + + if (m = /^\s+/.exec(pem)) { + pem = pem.substring(m[0].length); continue; } + throw new Error('PEM parse error.'); } assert(chunks.length !== 0, 'PEM parse error.'); assert(!tag, 'Un-ended tag.'); - assert(buf.length === 0, 'Trailing data.'); + assert(chunk.length === 0, 'Trailing data.'); return chunks; }; @@ -72,13 +87,16 @@ PEM.decode = function decode(pem) { const chunks = PEM.parse(pem); const body = chunks[0]; const extra = chunks[1]; - let params, alg; + + let params = null; if (extra) { if (extra.tag.indexOf('PARAMETERS') !== -1) params = extra.data; } + let alg = null; + switch (body.type) { case 'dsa': alg = 'dsa'; @@ -121,6 +139,6 @@ PEM.encode = function encode(der, type, suffix) { return '' + `-----BEGIN ${type}-----\n` - + `${pem}` + + pem + `-----END ${type}-----\n`; }; diff --git a/lib/utils/protoreader.js b/lib/utils/protoreader.js index 6dd66bf2..8a34f128 100644 --- a/lib/utils/protoreader.js +++ b/lib/utils/protoreader.js @@ -46,41 +46,56 @@ ProtoReader.prototype.readVarint = function _readVarint() { ProtoReader.prototype.readFieldValue = function readFieldValue(tag, opt) { const field = this.readField(tag, opt); + if (!field) return -1; + assert(field.value != null); + return field.value; }; ProtoReader.prototype.readFieldU64 = function readFieldU64(tag, opt) { const field = this.readField(tag, opt); + if (!field) return -1; + assert(field.type === wireType.VARINT || field.type === wireType.FIXED64); + return field.value; }; ProtoReader.prototype.readFieldU32 = function readFieldU32(tag, opt) { const field = this.readField(tag, opt); + if (!field) return -1; + assert(field.type === wireType.VARINT || field.type === wireType.FIXED32); + return field.value; }; ProtoReader.prototype.readFieldBytes = function readFieldBytes(tag, opt) { const field = this.readField(tag, opt); + if (!field) return null; + assert(field.data); + return field.data; }; ProtoReader.prototype.readFieldString = function readFieldString(tag, opt, enc) { const field = this.readField(tag, opt); + if (!field) return null; + assert(field.data); + return field.data.toString(enc || 'utf8'); }; diff --git a/lib/utils/protowriter.js b/lib/utils/protowriter.js index 974b983d..b7894921 100644 --- a/lib/utils/protowriter.js +++ b/lib/utils/protowriter.js @@ -44,44 +44,50 @@ util.inherits(ProtoWriter, BufferWriter); ProtoWriter.prototype.writeVarint = function _writeVarint(num) { const size = sizeVarint(num); - let value; // Avoid an extra allocation until // we make bufferwriter more hackable. // More insanity here... switch (size) { - case 6: - value = slipVarint(num); + case 6: { + const value = slipVarint(num); this.writeU32BE(value / 0x10000 | 0); this.writeU16BE(value & 0xffff); break; - case 5: - value = slipVarint(num); + } + case 5: { + const value = slipVarint(num); this.writeU32BE(value / 0x100 | 0); this.writeU8(value & 0xff); break; - case 4: - value = slipVarint(num); + } + case 4: { + const value = slipVarint(num); this.writeU32BE(value); break; - case 3: - value = slipVarint(num); + } + case 3: { + const value = slipVarint(num); this.writeU16BE(value >> 8); this.writeU8(value & 0xff); break; - case 2: - value = slipVarint(num); + } + case 2: { + const value = slipVarint(num); this.writeU16BE(value); break; - case 1: - value = slipVarint(num); + } + case 1: { + const value = slipVarint(num); this.writeU8(value); break; - default: - value = Buffer.allocUnsafe(size); + } + default: { + const value = Buffer.allocUnsafe(size); writeVarint(value, num, 0); this.writeBytes(value); break; + } } }; @@ -119,13 +125,11 @@ ProtoWriter.prototype.writeFieldString = function writeFieldString(tag, data, en */ function writeVarint(data, num, off) { - let ch; - assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); do { assert(off < data.length); - ch = num & 0x7f; + let ch = num & 0x7f; num -= num % 0x80; num /= 0x80; if (num !== 0) @@ -138,15 +142,14 @@ function writeVarint(data, num, off) { }; function slipVarint(num) { + assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); + let data = 0; let size = 0; - let ch; - - assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); do { assert(size < 7); - ch = num & 0x7f; + let ch = num & 0x7f; num -= num % 0x80; num /= 0x80; if (num !== 0) @@ -160,10 +163,10 @@ function slipVarint(num) { } function sizeVarint(num) { - let size = 0; - assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.'); + let size = 0; + do { num -= num % 0x80; num /= 0x80; diff --git a/lib/utils/rbt.js b/lib/utils/rbt.js index 6b16cd39..17454395 100644 --- a/lib/utils/rbt.js +++ b/lib/utils/rbt.js @@ -58,6 +58,8 @@ RBT.prototype.search = function search(key) { else current = current.right; } + + return null; }; /** @@ -118,13 +120,11 @@ RBT.prototype.insert = function insert(key, value) { */ RBT.prototype.insertFixup = function insertFixup(x) { - let y; - x.color = RED; while (x !== this.root && x.parent.color === RED) { if (x.parent === x.parent.parent.left) { - y = x.parent.parent.right; + const y = x.parent.parent.right; if (!y.isNull() && y.color === RED) { x.parent.color = BLACK; y.color = BLACK; @@ -140,7 +140,7 @@ RBT.prototype.insertFixup = function insertFixup(x) { this.rotr(x.parent.parent); } } else { - y = x.parent.parent.left; + const y = x.parent.parent.left; if (!y.isNull() && y.color === RED) { x.parent.color = BLACK; y.color = BLACK; @@ -183,6 +183,8 @@ RBT.prototype.remove = function remove(key) { else current = current.right; } + + return null; }; /** @@ -225,11 +227,9 @@ RBT.prototype.removeNode = function removeNode(z) { */ RBT.prototype.removeFixup = function removeFixup(x) { - let w; - while (x !== this.root && x.color === BLACK) { if (x === x.parent.left) { - w = x.parent.right; + let w = x.parent.right; if (w.color === RED) { w.color = BLACK; @@ -255,7 +255,7 @@ RBT.prototype.removeFixup = function removeFixup(x) { x = this.root; } } else { - w = x.parent.left; + let w = x.parent.left; if (w.color === RED) { w.color = BLACK; @@ -294,6 +294,7 @@ RBT.prototype.removeFixup = function removeFixup(x) { RBT.prototype.rotl = function rotl(x) { const y = x.right; + x.right = y.left; if (!y.left.isNull()) @@ -322,6 +323,7 @@ RBT.prototype.rotl = function rotl(x) { RBT.prototype.rotr = function rotr(x) { const y = x.left; + x.left = y.right; if (!y.right.isNull()) @@ -352,8 +354,10 @@ RBT.prototype.rotr = function rotr(x) { RBT.prototype.min = function min(z) { if (z.isNull()) return z; + while (!z.left.isNull()) z = z.left; + return z; }; @@ -367,8 +371,10 @@ RBT.prototype.min = function min(z) { RBT.prototype.max = function max(z) { if (z.isNull()) return z; + while (!z.right.isNull()) z = z.right; + return z; }; @@ -380,18 +386,21 @@ RBT.prototype.max = function max(z) { */ RBT.prototype.successor = function successor(x) { - let y; if (!x.right.isNull()) { x = x.right; + while (!x.left.isNull()) x = x.left; + return x; } - y = x.parent; + + let y = x.parent; while (!y.isNull() && x === y.right) { x = y; y = y.parent; } + return y; }; @@ -403,18 +412,21 @@ RBT.prototype.successor = function successor(x) { */ RBT.prototype.predecessor = function predecessor(x) { - let y; if (!x.left.isNull()) { x = x.left; + while (!x.right.isNull()) x = x.right; + return x; } - y = x.parent; + + let y = x.parent; while (!y.isNull() && x === y.left) { x = y; y = y.parent; } + return y; }; @@ -425,14 +437,18 @@ RBT.prototype.predecessor = function predecessor(x) { */ RBT.prototype.clone = function clone() { - let current = this.root; + if (this.root.isNull()) + return SENTINEL; + const stack = []; + + let current = this.root; let left = true; - let parent, copy, snapshot; + let parent, snapshot; for (;;) { if (!current.isNull()) { - copy = current.clone(); + const copy = current.clone(); if (parent) copy.parent = parent; @@ -465,6 +481,8 @@ RBT.prototype.clone = function clone() { current = current.right; } + assert(snapshot); + return snapshot; }; @@ -475,12 +493,10 @@ RBT.prototype.clone = function clone() { */ RBT.prototype.snapshot = function snapshot() { - let node = SENTINEL; - if (this.root.isNull()) - return node; + return SENTINEL; - node = this.root.clone(); + const node = this.root.clone(); copyLeft(node, node.left); copyRight(node, node.right); @@ -607,11 +623,11 @@ Iterator.prototype.seek = function seek(key) { */ Iterator.prototype.seekMin = function seekMin(key) { + assert(key != null, 'No key passed to seek.'); + let root = this.current; let current = SENTINEL; - assert(key != null, 'No key passed to seek.'); - while (!root.isNull()) { const cmp = this.tree.compare(root.key, key); @@ -639,11 +655,11 @@ Iterator.prototype.seekMin = function seekMin(key) { */ Iterator.prototype.seekMax = function seekMax(key) { + assert(key != null, 'No key passed to seek.'); + let root = this.current; let current = SENTINEL; - assert(key != null, 'No key passed to seek.'); - while (!root.isNull()) { const cmp = this.tree.compare(root.key, key); diff --git a/lib/utils/rollingfilter.js b/lib/utils/rollingfilter.js index d79f545e..42b08b01 100644 --- a/lib/utils/rollingfilter.js +++ b/lib/utils/rollingfilter.js @@ -60,6 +60,7 @@ RollingFilter.prototype.fromRate = function fromRate(items, rate) { const limit = (items + 1) / 2 | 0; const max = limit * 3; + let size = -1 * n * max / Math.log(1.0 - Math.exp(logRate / n)); size = Math.ceil(size); @@ -161,19 +162,16 @@ RollingFilter.prototype.add = function add(val, enc) { const hash = this.hash(val, i); const bits = hash & 0x3f; const pos = (hash >>> 6) % this.items; - let pos1 = (pos & ~1) * 8; - let pos2 = (pos | 1) * 8; + const pos1 = (pos & ~1) * 8; + const pos2 = (pos | 1) * 8; const bit = bits % 8; const oct = (bits - bit) / 8; - pos1 += oct; - pos2 += oct; + this.filter[pos1 + oct] &= ~(1 << bit); + this.filter[pos1 + oct] |= (this.generation & 1) << bit; - this.filter[pos1] &= ~(1 << bit); - this.filter[pos1] |= (this.generation & 1) << bit; - - this.filter[pos2] &= ~(1 << bit); - this.filter[pos2] |= (this.generation >>> 1) << bit; + this.filter[pos2 + oct] &= ~(1 << bit); + this.filter[pos2 + oct] |= (this.generation >>> 1) << bit; } }; @@ -193,20 +191,17 @@ RollingFilter.prototype.test = function test(val, enc) { for (let i = 0; i < this.n; i++) { const hash = this.hash(val, i); - let bits = hash & 0x3f; + const bits = hash & 0x3f; const pos = (hash >>> 6) % this.items; - let pos1 = (pos & ~1) * 8; - let pos2 = (pos | 1) * 8; + const pos1 = (pos & ~1) * 8; + const pos2 = (pos | 1) * 8; const bit = bits % 8; const oct = (bits - bit) / 8; - pos1 += oct; - pos2 += oct; + const bit1 = (this.filter[pos1 + oct] >>> bit) & 1; + const bit2 = (this.filter[pos2 + oct] >>> bit) & 1; - bits = (this.filter[pos1] >>> bit) & 1; - bits |= (this.filter[pos2] >>> bit) & 1; - - if (bits === 0) + if ((bit1 | bit2) === 0) return false; } diff --git a/lib/utils/util.js b/lib/utils/util.js index ef725bff..11c45c67 100644 --- a/lib/utils/util.js +++ b/lib/utils/util.js @@ -38,12 +38,12 @@ const inspectOptions = { util.hrtime = function hrtime(time) { if (!process.hrtime) { - let now = util.ms(); + const now = util.ms(); if (time) { - time = time[0] * 1000 + time[1] / 1e6; - now -= time; - return now; + const [hi, lo] = time; + const start = hi * 1000 + lo / 1e6; + return now - start; } const ms = now % 1000; @@ -53,8 +53,8 @@ util.hrtime = function hrtime(time) { } if (time) { - const elapsed = process.hrtime(time); - return elapsed[0] * 1000 + elapsed[1] / 1e6; + const [hi, lo] = process.hrtime(time); + return hi * 1000 + lo / 1e6; } return process.hrtime(); @@ -519,7 +519,9 @@ util.indexOf = function indexOf(items, data) { for (let i = 0; i < items.length; i++) { const item = items[i]; + assert(Buffer.isBuffer(item)); + if (item.equals(data)) return i; } @@ -537,7 +539,9 @@ util.indexOf = function indexOf(items, data) { util.pad8 = function pad8(num) { assert(typeof num === 'number'); assert(num >= 0); + num = num + ''; + switch (num.length) { case 1: return '00' + num; @@ -546,6 +550,7 @@ util.pad8 = function pad8(num) { case 3: return num; } + assert(false); }; @@ -559,7 +564,9 @@ util.pad8 = function pad8(num) { util.pad32 = function pad32(num) { assert(typeof num === 'number'); assert(num >= 0); + num = num + ''; + switch (num.length) { case 1: return '000000000' + num; @@ -581,9 +588,9 @@ util.pad32 = function pad32(num) { return '0' + num; case 10: return num; - default: - assert(false); } + + assert(false); }; /** @@ -596,15 +603,17 @@ util.pad32 = function pad32(num) { util.hex8 = function hex8(num) { assert(typeof num === 'number'); assert(num >= 0); + num = num.toString(16); + switch (num.length) { case 1: return '0' + num; case 2: return num; - default: - assert(false); } + + assert(false); }; /** @@ -617,7 +626,9 @@ util.hex8 = function hex8(num) { util.hex32 = function hex32(num) { assert(typeof num === 'number'); assert(num >= 0); + num = num.toString(16); + switch (num.length) { case 1: return '0000000' + num; @@ -635,9 +646,9 @@ util.hex32 = function hex32(num) { return '0' + num; case 8: return num; - default: - assert(false); } + + assert(false); }; /** diff --git a/lib/utils/validator.js b/lib/utils/validator.js index caeee155..4407c249 100644 --- a/lib/utils/validator.js +++ b/lib/utils/validator.js @@ -439,8 +439,9 @@ Validator.prototype.bool = function bool(key, fallback) { if (value === null) return fallback; - // bitcoin core mixes semantics of truthiness amoung rpc methods - // most "verbose" parameters are bools, but getrawtransaction is 1/0 + // Bitcoin Core mixes semantics of truthiness + // amoung rpc methods most "verbose" parameters + // are bools, but getrawtransaction is 1/0. if (value === 1) return true; diff --git a/lib/wallet/account.js b/lib/wallet/account.js index b43e4439..82b54fab 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -344,9 +344,8 @@ Account.prototype.spliceKey = function spliceKey(key) { Account.prototype.addSharedKey = async function addSharedKey(key) { const result = this.pushKey(key); - const exists = await this._hasDuplicate(); - if (exists) { + if (await this.hasDuplicate()) { this.spliceKey(key); throw new Error('Cannot add a key from another account.'); } @@ -363,7 +362,7 @@ Account.prototype.addSharedKey = async function addSharedKey(key) { * @returns {Promise} */ -Account.prototype._hasDuplicate = function _hasDuplicate() { +Account.prototype.hasDuplicate = function hasDuplicate() { if (this.keys.length !== this.n - 1) return false; @@ -511,15 +510,13 @@ Account.prototype.derivePath = function derivePath(path, master) { if (path.encrypted) { data = master.decipher(data, path.hash); if (!data) - return; + return null; } - const ring = WalletKey.fromImport(this, data); - - return ring; + return WalletKey.fromImport(this, data); } case Path.types.ADDRESS: { - return; + return null; } default: { assert(false, 'Bad key type.'); @@ -538,8 +535,8 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) { assert(typeof branch === 'number'); const keys = []; - let key; + let key; if (master && master.key && !this.watchOnly) { key = master.key.deriveBIP44(this.accountIndex); key = key.derive(branch).derive(index); @@ -555,9 +552,9 @@ Account.prototype.deriveKey = function deriveKey(branch, index, master) { case Account.types.MULTISIG: keys.push(key.publicKey); - for (let shared of this.keys) { - shared = shared.derive(branch).derive(index); - keys.push(shared.publicKey); + for (const shared of this.keys) { + const key = shared.derive(branch).derive(index); + keys.push(key.publicKey); } ring.script = Script.fromMultisig(this.m, this.n, keys); @@ -800,7 +797,7 @@ Account.prototype.getAddress = function getAddress(enc) { Account.prototype.getReceive = function getReceive(enc) { if (!this.receive) - return; + return null; return this.receive.getAddress(enc); }; @@ -812,7 +809,8 @@ Account.prototype.getReceive = function getReceive(enc) { Account.prototype.getChange = function getChange(enc) { if (!this.change) - return; + return null; + return this.change.getAddress(enc); }; @@ -824,7 +822,8 @@ Account.prototype.getChange = function getChange(enc) { Account.prototype.getNested = function getNested(enc) { if (!this.nested) - return; + return null; + return this.nested.getAddress(enc); }; @@ -1004,8 +1003,8 @@ Account.isAccount = function isAccount(obj) { * Helpers */ -function cmp(key1, key2) { - return key1.compare(key2); +function cmp(a, b) { + return a.compare(b); } /* diff --git a/lib/wallet/common.js b/lib/wallet/common.js index 8527bd9b..5f45ed65 100644 --- a/lib/wallet/common.js +++ b/lib/wallet/common.js @@ -82,21 +82,21 @@ common.sortCoins = function sortCoins(coins) { common.sortDeps = function sortDeps(txs) { const map = new Map(); - const depMap = new Map(); - const depCount = new Map(); - const result = []; - const top = []; for (const tx of txs) { const hash = tx.hash('hex'); map.set(hash, tx); } - for (const [hash, tx] of map) { - let hasDeps = false; + const depMap = new Map(); + const depCount = new Map(); + const top = []; + for (const [hash, tx] of map) { depCount.set(hash, 0); + let hasDeps = false; + for (const input of tx.inputs) { const prev = input.prevout.hash; @@ -119,6 +119,8 @@ common.sortDeps = function sortDeps(txs) { top.push(tx); } + const result = []; + for (const tx of top) { const hash = tx.hash('hex'); const deps = depMap.get(hash); diff --git a/lib/wallet/http.js b/lib/wallet/http.js index 91a282ce..8cd70419 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -134,8 +134,7 @@ HTTPServer.prototype.initRouter = function initRouter() { return; } - let wallet = null; - + let wallet; try { wallet = await this.walletdb.auth(id, token); } catch (err) { @@ -315,8 +314,11 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const old = valid.str('old'); const new_ = valid.str('new'); + enforce(old || new_, 'Passphrase is required.'); + await req.wallet.setPassphrase(old, new_); + res.send(200, { success: true }); }); @@ -325,8 +327,11 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const passphrase = valid.str('passphrase'); const timeout = valid.u32('timeout'); + enforce(passphrase, 'Passphrase is required.'); + await req.wallet.unlock(passphrase, timeout); + res.send(200, { success: true }); }); @@ -392,11 +397,11 @@ HTTPServer.prototype.initRouter = function initRouter() { for (const output of outputs) { const valid = new Validator([output]); - let script = null; + let script; if (valid.has('script')) { - script = valid.buf('script'); - script = Script.fromRaw(script); + const raw = valid.buf('script'); + script = Script.fromRaw(raw); } options.outputs.push({ @@ -431,11 +436,11 @@ HTTPServer.prototype.initRouter = function initRouter() { for (const output of outputs) { const valid = new Validator([output]); - let script = null; + let script; if (valid.has('script')) { - script = valid.buf('script'); - script = Script.fromRaw(script); + const raw = valid.buf('script'); + script = Script.fromRaw(raw); } options.outputs.push({ @@ -448,6 +453,7 @@ HTTPServer.prototype.initRouter = function initRouter() { const tx = await req.wallet.createTX(options); await req.wallet.sign(tx, passphrase); + res.send(200, tx.getJSON(this.network)); }); @@ -471,8 +477,11 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const age = valid.u32('age'); + enforce(age, 'Age is required.'); + await req.wallet.zap(acct, age); + res.send(200, { success: true }); }); @@ -480,8 +489,11 @@ HTTPServer.prototype.initRouter = function initRouter() { this.del('/:id/tx/:hash', async (req, res) => { const valid = req.valid(); const hash = valid.hash('hash'); + enforce(hash, 'Hash is required.'); + await req.wallet.abandon(hash); + res.send(200, { success: true }); }); @@ -513,8 +525,11 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const key = valid.str('accountKey'); + enforce(key, 'Key is required.'); + await req.wallet.addSharedKey(acct, key); + res.send(200, { success: true }); }); @@ -523,8 +538,11 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const key = valid.str('accountKey'); + enforce(key, 'Key is required.'); + await req.wallet.removeSharedKey(acct, key); + res.send(200, { success: true }); }); @@ -568,6 +586,7 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const address = await req.wallet.createReceive(acct); + res.send(200, address.toJSON()); }); @@ -576,6 +595,7 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const address = await req.wallet.createChange(acct); + res.send(200, address.toJSON()); }); @@ -584,6 +604,7 @@ HTTPServer.prototype.initRouter = function initRouter() { const valid = req.valid(); const acct = valid.str('account'); const address = await req.wallet.createNested(acct); + res.send(200, address.toJSON()); }); @@ -787,48 +808,36 @@ HTTPServer.prototype.initSockets = function initSockets() { this.walletdb.on('tx', (id, tx, details) => { const json = details.toJSON(); - const channel = 'w:' + id; - this.to(channel, 'wallet tx', json); - this.to('!all', 'wallet tx', id, json); + this.to(`w:${id}`, 'wallet tx', json); }); this.walletdb.on('confirmed', (id, tx, details) => { const json = details.toJSON(); - const channel = 'w:' + id; - this.to(channel, 'wallet confirmed', json); - this.to('!all', 'wallet confirmed', id, json); + this.to(`w:${id}`, 'wallet confirmed', json); }); this.walletdb.on('unconfirmed', (id, tx, details) => { const json = details.toJSON(); - const channel = 'w:' + id; - this.to(channel, 'wallet unconfirmed', json); - this.to('!all', 'wallet unconfirmed', id, json); + this.to(`w:${id}`, 'wallet unconfirmed', json); }); this.walletdb.on('conflict', (id, tx, details) => { const json = details.toJSON(); - const channel = 'w:' + id; - this.to(channel, 'wallet conflict', json); - this.to('!all', 'wallet conflict', id, json); + this.to(`w:${id}`, 'wallet conflict', json); }); this.walletdb.on('balance', (id, balance) => { const json = balance.toJSON(); - const channel = 'w:' + id; - this.to(channel, 'wallet balance', json); - this.to('!all', 'wallet balance', id, json); + this.to(`w:${id}`, 'wallet balance', json); }); this.walletdb.on('address', (id, receive) => { - const channel = 'w:' + id; const json = []; for (const addr of receive) json.push(addr.toJSON()); - this.to(channel, 'wallet address', json); - this.to('!all', 'wallet address', id, json); + this.to(`w:${id}`, 'wallet address', json); }); }; @@ -840,16 +849,21 @@ HTTPServer.prototype.initSockets = function initSockets() { HTTPServer.prototype.handleSocket = function handleSocket(socket) { socket.hook('wallet auth', (args) => { - const valid = new Validator([args]); - const key = valid.str(0); - if (socket.auth) throw new Error('Already authed.'); if (!this.options.noAuth) { - const hash = hash256(key); + const valid = new Validator([args]); + const key = valid.str(0); + + if (key.length > 255) + throw new Error('Invalid API key.'); + + const data = Buffer.from(key, 'utf8'); + const hash = digest.hash256(data); + if (!ccmp(hash, this.options.apiHash)) - throw new Error('Bad key.'); + throw new Error('Invalid API key.'); } socket.auth = true; @@ -873,21 +887,19 @@ HTTPServer.prototype.handleAuth = function handleAuth(socket) { const valid = new Validator([args]); const id = valid.str(0, ''); const token = valid.buf(1); - const channel = 'w:' + id; if (!id) throw new Error('Invalid parameter.'); if (!this.options.walletAuth) { - socket.join(channel); + socket.join(`w:${id}`); return null; } if (!token) throw new Error('Invalid parameter.'); - let wallet = null; - + let wallet; try { wallet = await this.walletdb.auth(id, token); } catch (e) { @@ -900,7 +912,7 @@ HTTPServer.prototype.handleAuth = function handleAuth(socket) { this.logger.info('Successful wallet auth for %s.', id); - socket.join(channel); + socket.join(`w:${id}`); return null; }); @@ -908,12 +920,11 @@ HTTPServer.prototype.handleAuth = function handleAuth(socket) { socket.hook('wallet leave', (args) => { const valid = new Validator([args]); const id = valid.str(0, ''); - const channel = 'w:' + id; if (!id) throw new Error('Invalid parameter.'); - socket.leave(channel); + socket.leave(`w:${id}`); return null; }); @@ -934,7 +945,7 @@ function HTTPOptions(options) { this.logger = null; this.walletdb = null; this.apiKey = base58.encode(random.randomBytes(20)); - this.apiHash = hash256(this.apiKey); + this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'utf8')); this.serviceHash = this.apiHash; this.noAuth = false; this.walletAuth = false; @@ -977,7 +988,7 @@ HTTPOptions.prototype.fromOptions = function fromOptions(options) { assert(options.apiKey.length <= 200, 'API key must be under 200 bytes.'); this.apiKey = options.apiKey; - this.apiHash = hash256(this.apiKey); + this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'utf8')); } if (options.noAuth != null) { @@ -1047,16 +1058,6 @@ HTTPOptions.fromOptions = function fromOptions(options) { * Helpers */ -function hash256(data) { - if (typeof data !== 'string') - return Buffer.alloc(0); - - if (data.length > 200) - return Buffer.alloc(0); - - return digest.hash256(Buffer.from(data, 'utf8')); -} - function enforce(value, msg) { if (!value) { const err = new Error(msg); diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js index fce5cfaf..e17d4a92 100644 --- a/lib/wallet/masterkey.js +++ b/lib/wallet/masterkey.js @@ -278,7 +278,7 @@ MasterKey.prototype.derive = async function derive(passwd) { MasterKey.prototype.encipher = function encipher(data, iv) { if (!this.aesKey) - return; + return null; if (typeof iv === 'string') iv = Buffer.from(iv, 'hex'); @@ -295,7 +295,7 @@ MasterKey.prototype.encipher = function encipher(data, iv) { MasterKey.prototype.decipher = function decipher(data, iv) { if (!this.aesKey) - return; + return null; if (typeof iv === 'string') iv = Buffer.from(iv, 'hex'); @@ -395,7 +395,7 @@ MasterKey.prototype._decrypt = async function decrypt(passphrase, clean) { if (!clean) { cleanse(key); - return; + return null; } return key; @@ -446,7 +446,7 @@ MasterKey.prototype._encrypt = async function encrypt(passphrase, clean) { if (!clean) { cleanse(key); - return; + return null; } return key; diff --git a/lib/wallet/nodeclient.js b/lib/wallet/nodeclient.js index ec32d94d..08063c67 100644 --- a/lib/wallet/nodeclient.js +++ b/lib/wallet/nodeclient.js @@ -106,10 +106,10 @@ NodeClient.prototype.getEntry = async function getEntry(hash) { const entry = await this.node.chain.db.getEntry(hash); if (!entry) - return; + return null; if (!(await entry.isMainChain())) - return; + return null; return entry; }; @@ -164,10 +164,11 @@ NodeClient.prototype.resetFilter = function resetFilter() { * @returns {Promise} */ -NodeClient.prototype.estimateFee = function estimateFee(blocks) { +NodeClient.prototype.estimateFee = async function estimateFee(blocks) { if (!this.node.fees) - return Promise.resolve(this.network.feeRate); - return Promise.resolve(this.node.fees.estimateFee(blocks)); + return this.network.feeRate; + + return this.node.fees.estimateFee(blocks); }; /** diff --git a/lib/wallet/records.js b/lib/wallet/records.js index 94ca0dcd..2b78c808 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -321,7 +321,7 @@ BlockMapRecord.prototype.add = function add(hash, wid) { if (!tx) { tx = new TXMapRecord(hash); - this.txs.set(tx.hash, tx); + this.txs.set(hash, tx); } return tx.add(wid); @@ -600,7 +600,8 @@ TXRecord.prototype.unsetBlock = function unsetBlock() { TXRecord.prototype.getBlock = function getBlock() { if (this.height === -1) - return; + return null; + return new BlockMeta(this.block, this.height, this.time); }; diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index a67255a4..2ece9778 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -132,16 +132,16 @@ RPC.prototype.stop = async function stop(args, help) { }; RPC.prototype.fundRawTransaction = async function fundRawTransaction(args, help) { - const valid = new Validator([args]); - const data = valid.buf(0); - const options = valid.obj(1); - const wallet = this.wallet; - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'fundrawtransaction "hexstring" ( options )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const data = valid.buf(0); + const options = valid.obj(1); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.'); @@ -153,13 +153,15 @@ RPC.prototype.fundRawTransaction = async function fundRawTransaction(args, help) } let rate = this.feeRate; - let change = null; - + let change; if (options) { const valid = new Validator([options]); - change = valid.str('changeAddress'); - rate = valid.btc('feeRate'); + if (valid.has('feeRate')) + rate = valid.btc('feeRate'); + + if (valid.has('changeAddress')) + change = valid.str('changeAddress'); if (change) change = parseAddress(change, this.network); @@ -182,13 +184,12 @@ RPC.prototype.fundRawTransaction = async function fundRawTransaction(args, help) */ RPC.prototype.resendWalletTransactions = async function resendWalletTransactions(args, help) { - const wallet = this.wallet; - const hashes = []; - if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'resendwallettransactions'); + const wallet = this.wallet; const txs = await wallet.resend(); + const hashes = []; for (const tx of txs) hashes.push(tx.txid()); @@ -215,25 +216,25 @@ RPC.prototype.addWitnessAddress = async function addWitnessAddress(args, help) { }; RPC.prototype.backupWallet = async function backupWallet(args, help) { - const valid = new Validator([args]); - const dest = valid.str(0); - if (help || args.length !== 1 || !dest) throw new RPCError(errs.MISC_ERROR, 'backupwallet "destination"'); + const valid = new Validator([args]); + const dest = valid.str(0); + await this.wdb.backup(dest); return null; }; RPC.prototype.dumpPrivKey = async function dumpPrivKey(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'dumpprivkey "bitcoinaddress"'); + const wallet = this.wallet; const valid = new Validator([args]); const addr = valid.str(0, ''); - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'dumpprivkey "bitcoinaddress"'); - const hash = parseHash(addr, this.network); const ring = await wallet.getPrivateKey(hash); @@ -244,18 +245,18 @@ RPC.prototype.dumpPrivKey = async function dumpPrivKey(args, help) { }; RPC.prototype.dumpWallet = async function dumpWallet(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'dumpwallet "filename"'); + const wallet = this.wallet; const valid = new Validator([args]); const file = valid.str(0); - const time = util.date(); - - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'dumpwallet "filename"'); if (!file) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); const tip = await this.wdb.getTip(); + const time = util.date(); const out = [ util.fmt('# Wallet Dump created by Bcoin %s', pkg.version), @@ -302,12 +303,13 @@ RPC.prototype.dumpWallet = async function dumpWallet(args, help) { RPC.prototype.encryptWallet = async function encryptWallet(args, help) { const wallet = this.wallet; - const valid = new Validator([args]); - const passphrase = valid.str(0, ''); if (!wallet.master.encrypted && (help || args.length !== 1)) throw new RPCError(errs.MISC_ERROR, 'encryptwallet "passphrase"'); + const valid = new Validator([args]); + const passphrase = valid.str(0, ''); + if (wallet.master.encrypted) { throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Already running with an encrypted wallet.'); @@ -326,13 +328,13 @@ RPC.prototype.encryptWallet = async function encryptWallet(args, help) { }; RPC.prototype.getAccountAddress = async function getAccountAddress(args, help) { - const valid = new Validator([args]); - const wallet = this.wallet; - let name = valid.str(0, ''); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'getaccountaddress "account"'); + const wallet = this.wallet; + const valid = new Validator([args]); + let name = valid.str(0, ''); + if (!name) name = 'default'; @@ -345,13 +347,13 @@ RPC.prototype.getAccountAddress = async function getAccountAddress(args, help) { }; RPC.prototype.getAccount = async function getAccount(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'getaccount "bitcoinaddress"'); + const wallet = this.wallet; const valid = new Validator([args]); const addr = valid.str(0, ''); - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'getaccount "bitcoinaddress"'); - const hash = parseHash(addr, this.network); const path = await wallet.getPath(hash); @@ -362,14 +364,14 @@ RPC.prototype.getAccount = async function getAccount(args, help) { }; RPC.prototype.getAddressesByAccount = async function getAddressesByAccount(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'getaddressesbyaccount "account"'); + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0, ''); const addrs = []; - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'getaddressesbyaccount "account"'); - if (name === '') name = 'default'; @@ -384,17 +386,17 @@ RPC.prototype.getAddressesByAccount = async function getAddressesByAccount(args, }; RPC.prototype.getBalance = async function getBalance(args, help) { + if (help || args.length > 3) { + throw new RPCError(errs.MISC_ERROR, + 'getbalance ( "account" minconf includeWatchonly )'); + } + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0); const minconf = valid.u32(1, 0); const watchOnly = valid.bool(2, false); - if (help || args.length > 3) { - throw new RPCError(errs.MISC_ERROR, - 'getbalance ( "account" minconf includeWatchonly )'); - } - if (name === '') name = 'default'; @@ -405,8 +407,8 @@ RPC.prototype.getBalance = async function getBalance(args, help) { return 0; const balance = await wallet.getBalance(name); - let value; + let value; if (minconf > 0) value = balance.confirmed; else @@ -416,13 +418,13 @@ RPC.prototype.getBalance = async function getBalance(args, help) { }; RPC.prototype.getNewAddress = async function getNewAddress(args, help) { + if (help || args.length > 1) + throw new RPCError(errs.MISC_ERROR, 'getnewaddress ( "account" )'); + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0); - if (help || args.length > 1) - throw new RPCError(errs.MISC_ERROR, 'getnewaddress ( "account" )'); - if (name === '') name = 'default'; @@ -432,41 +434,41 @@ RPC.prototype.getNewAddress = async function getNewAddress(args, help) { }; RPC.prototype.getRawChangeAddress = async function getRawChangeAddress(args, help) { - const wallet = this.wallet; - if (help || args.length > 1) throw new RPCError(errs.MISC_ERROR, 'getrawchangeaddress'); + const wallet = this.wallet; const addr = await wallet.createChange(); return addr.getAddress('string'); }; RPC.prototype.getReceivedByAccount = async function getReceivedByAccount(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - let name = valid.str(0); - const minconf = valid.u32(0, 0); - const height = this.wdb.state.height; - const filter = new Set(); - let total = 0; - let lastConf = -1; - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'getreceivedbyaccount "account" ( minconf )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + let name = valid.str(0); + const minconf = valid.u32(1, 0); + const height = this.wdb.state.height; + if (name === '') name = 'default'; const paths = await wallet.getPaths(name); + const filter = new Set(); for (const path of paths) filter.add(path.hash); const txs = await wallet.getHistory(name); + let total = 0; + let lastConf = -1; + for (const wtx of txs) { const conf = wtx.getDepth(height); @@ -487,21 +489,22 @@ RPC.prototype.getReceivedByAccount = async function getReceivedByAccount(args, h }; RPC.prototype.getReceivedByAddress = async function getReceivedByAddress(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const addr = valid.str(0, ''); - const minconf = valid.u32(1, 0); - const height = this.wdb.state.height; - let total = 0; - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'getreceivedbyaddress "bitcoinaddress" ( minconf )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const addr = valid.str(0, ''); + const minconf = valid.u32(1, 0); + const height = this.wdb.state.height; + const hash = parseHash(addr, this.network); const txs = await wallet.getHistory(); + let total = 0; + for (const wtx of txs) { if (wtx.getDepth(height) < minconf) continue; @@ -518,14 +521,11 @@ RPC.prototype.getReceivedByAddress = async function getReceivedByAddress(args, h RPC.prototype._toWalletTX = async function _toWalletTX(wtx) { const wallet = this.wallet; const details = await wallet.toDetails(wtx); - const det = []; - let sent = 0; - let received = 0; - let receive = true; if (!details) throw new RPCError(errs.WALLET_ERROR, 'TX not found.'); + let receive = true; for (const member of details.inputs) { if (member.path) { receive = false; @@ -533,6 +533,10 @@ RPC.prototype._toWalletTX = async function _toWalletTX(wtx) { } } + const det = []; + let sent = 0; + let received = 0; + for (let i = 0; i < details.outputs.length; i++) { const member = details.outputs[i]; @@ -588,16 +592,16 @@ RPC.prototype._toWalletTX = async function _toWalletTX(wtx) { }; RPC.prototype.getTransaction = async function getTransaction(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const hash = valid.hash(0); - const watchOnly = valid.bool(1, false); - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'gettransaction "txid" ( includeWatchonly )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const hash = valid.hash(0); + const watchOnly = valid.bool(1, false); + if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter'); @@ -610,13 +614,13 @@ RPC.prototype.getTransaction = async function getTransaction(args, help) { }; RPC.prototype.abandonTransaction = async function abandonTransaction(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'abandontransaction "txid"'); + const wallet = this.wallet; const valid = new Validator([args]); const hash = valid.hash(0); - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'abandontransaction "txid"'); - if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); @@ -629,22 +633,20 @@ RPC.prototype.abandonTransaction = async function abandonTransaction(args, help) }; RPC.prototype.getUnconfirmedBalance = async function getUnconfirmedBalance(args, help) { - const wallet = this.wallet; - if (help || args.length > 0) throw new RPCError(errs.MISC_ERROR, 'getunconfirmedbalance'); + const wallet = this.wallet; const balance = await wallet.getBalance(); return Amount.btc(balance.unconfirmed, true); }; RPC.prototype.getWalletInfo = async function getWalletInfo(args, help) { - const wallet = this.wallet; - if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'getwalletinfo'); + const wallet = this.wallet; const balance = await wallet.getBalance(); return { @@ -663,16 +665,16 @@ RPC.prototype.getWalletInfo = async function getWalletInfo(args, help) { }; RPC.prototype.importPrivKey = async function importPrivKey(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const secret = valid.str(0); - const rescan = valid.bool(2, false); - if (help || args.length < 1 || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'importprivkey "bitcoinprivkey" ( "label" rescan )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const secret = valid.str(0); + const rescan = valid.bool(2, false); + const key = parseSecret(secret, this.network); await wallet.importKey(0, key); @@ -684,21 +686,20 @@ RPC.prototype.importPrivKey = async function importPrivKey(args, help) { }; RPC.prototype.importWallet = async function importWallet(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'importwallet "filename" ( rescan )'); + const wallet = this.wallet; const valid = new Validator([args]); const file = valid.str(0); const rescan = valid.bool(1, false); - const keys = []; - - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'importwallet "filename" ( rescan )'); if (fs.unsupported) throw new RPCError(errs.INTERNAL_ERROR, 'FS not available.'); const data = await fs.readFile(file, 'utf8'); - const lines = data.split(/\n+/); + const keys = []; for (let line of lines) { line = line.trim(); @@ -729,17 +730,17 @@ RPC.prototype.importWallet = async function importWallet(args, help) { }; RPC.prototype.importAddress = async function importAddress(args, help) { + if (help || args.length < 1 || args.length > 4) { + throw new RPCError(errs.MISC_ERROR, + 'importaddress "address" ( "label" rescan p2sh )'); + } + const wallet = this.wallet; const valid = new Validator([args]); let addr = valid.str(0, ''); const rescan = valid.bool(2, false); const p2sh = valid.bool(3, false); - if (help || args.length < 1 || args.length > 4) { - throw new RPCError(errs.MISC_ERROR, - 'importaddress "address" ( "label" rescan p2sh )'); - } - if (p2sh) { let script = valid.buf(0); @@ -763,16 +764,16 @@ RPC.prototype.importAddress = async function importAddress(args, help) { }; RPC.prototype.importPubkey = async function importPubkey(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const data = valid.buf(0); - const rescan = valid.bool(2, false); - if (help || args.length < 1 || args.length > 4) { throw new RPCError(errs.MISC_ERROR, 'importpubkey "pubkey" ( "label" rescan )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const data = valid.buf(0); + const rescan = valid.bool(2, false); + if (!data) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); @@ -793,18 +794,18 @@ RPC.prototype.keyPoolRefill = async function keyPoolRefill(args, help) { }; RPC.prototype.listAccounts = async function listAccounts(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const minconf = valid.u32(0, 0); - const watchOnly = valid.bool(1, false); - const map = {}; - if (help || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'listaccounts ( minconf includeWatchonly)'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const minconf = valid.u32(0, 0); + const watchOnly = valid.bool(1, false); + const accounts = await wallet.getAccounts(); + const map = {}; for (const account of accounts) { const balance = await wallet.getBalance(account); @@ -829,13 +830,12 @@ RPC.prototype.listAddressGroupings = async function listAddressGroupings(args, h }; RPC.prototype.listLockUnspent = async function listLockUnspent(args, help) { - const wallet = this.wallet; - const out = []; - if (help || args.length > 0) throw new RPCError(errs.MISC_ERROR, 'listlockunspent'); + const wallet = this.wallet; const outpoints = wallet.getLocked(); + const out = []; for (const outpoint of outpoints) { out.push({ @@ -848,30 +848,30 @@ RPC.prototype.listLockUnspent = async function listLockUnspent(args, help) { }; RPC.prototype.listReceivedByAccount = async function listReceivedByAccount(args, help) { - const valid = new Validator([args]); - const minconf = valid.u32(0, 0); - const includeEmpty = valid.bool(1, false); - const watchOnly = valid.bool(2, false); - if (help || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'listreceivedbyaccount ( minconf includeempty includeWatchonly )'); } - return await this._listReceived(minconf, includeEmpty, watchOnly, true); -}; - -RPC.prototype.listReceivedByAddress = async function listReceivedByAddress(args, help) { const valid = new Validator([args]); const minconf = valid.u32(0, 0); const includeEmpty = valid.bool(1, false); const watchOnly = valid.bool(2, false); + return await this._listReceived(minconf, includeEmpty, watchOnly, true); +}; + +RPC.prototype.listReceivedByAddress = async function listReceivedByAddress(args, help) { if (help || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'listreceivedbyaddress ( minconf includeempty includeWatchonly )'); } + const valid = new Validator([args]); + const minconf = valid.u32(0, 0); + const includeEmpty = valid.bool(1, false); + const watchOnly = valid.bool(2, false); + return await this._listReceived(minconf, includeEmpty, watchOnly, false); }; @@ -879,10 +879,8 @@ RPC.prototype._listReceived = async function _listReceived(minconf, empty, watch const wallet = this.wallet; const paths = await wallet.getPaths(); const height = this.wdb.state.height; - const map = new Map(); - let out = []; - const result = []; + const map = new Map(); for (const path of paths) { const addr = path.toAddress(); map.set(path.hash, { @@ -921,6 +919,7 @@ RPC.prototype._listReceived = async function _listReceived(minconf, empty, watch } } + let out = []; for (const entry of map.values()) out.push(entry); @@ -943,6 +942,7 @@ RPC.prototype._listReceived = async function _listReceived(minconf, empty, watch out.push(entry); } + const result = []; for (const entry of out) { if (!empty && entry.amount === 0) continue; @@ -964,9 +964,6 @@ RPC.prototype.listSinceBlock = async function listSinceBlock(args, help) { const block = valid.hash(0); const minconf = valid.u32(1, 0); const watchOnly = valid.bool(2, false); - let height = -1; - const out = []; - let highest; if (help) { throw new RPCError(errs.MISC_ERROR, @@ -974,8 +971,9 @@ RPC.prototype.listSinceBlock = async function listSinceBlock(args, help) { } if (wallet.watchOnly !== watchOnly) - return out; + return []; + let height = -1; if (block) { const entry = await this.client.getEntry(block); if (entry) @@ -987,6 +985,8 @@ RPC.prototype.listSinceBlock = async function listSinceBlock(args, help) { const txs = await wallet.getHistory(); + const out = []; + let highest; for (const wtx of txs) { if (wtx.height < height) continue; @@ -1013,14 +1013,11 @@ RPC.prototype.listSinceBlock = async function listSinceBlock(args, help) { RPC.prototype._toListTX = async function _toListTX(wtx) { const wallet = this.wallet; const details = await wallet.toDetails(wtx); - let sent = 0; - let received = 0; - let receive = true; - let sendMember, recMember, sendIndex, recIndex; if (!details) throw new RPCError(errs.WALLET_ERROR, 'TX not found.'); + let receive = true; for (const member of details.inputs) { if (member.path) { receive = false; @@ -1028,6 +1025,9 @@ RPC.prototype._toListTX = async function _toListTX(wtx) { } } + let sent = 0; + let received = 0; + let sendMember, recMember, sendIndex, recIndex; for (let i = 0; i < details.outputs.length; i++) { const member = details.outputs[i]; @@ -1046,7 +1046,6 @@ RPC.prototype._toListTX = async function _toListTX(wtx) { } let member, index; - if (receive) { member = recMember; index = recIndex; @@ -1084,22 +1083,20 @@ RPC.prototype._toListTX = async function _toListTX(wtx) { }; RPC.prototype.listTransactions = async function listTransactions(args, help) { + if (help || args.length > 4) { + throw new RPCError(errs.MISC_ERROR, + 'listtransactions ( "account" count from includeWatchonly)'); + } + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0); const count = valid.u32(1, 10); const from = valid.u32(2, 0); const watchOnly = valid.bool(3, false); - let end = from + count; - const out = []; - - if (help || args.length > 4) { - throw new RPCError(errs.MISC_ERROR, - 'listtransactions ( "account" count from includeWatchonly)'); - } if (wallet.watchOnly !== watchOnly) - return out; + return []; if (name === '') name = 'default'; @@ -1108,9 +1105,11 @@ RPC.prototype.listTransactions = async function listTransactions(args, help) { common.sortTX(txs); - end = Math.min(end, txs.length); + const end = from + count; + const to = Math.min(end, txs.length); + const out = []; - for (let i = from; i < end; i++) { + for (let i = from; i < to; i++) { const wtx = txs[i]; const json = await this._toListTX(wtx); out.push(json); @@ -1120,19 +1119,19 @@ RPC.prototype.listTransactions = async function listTransactions(args, help) { }; RPC.prototype.listUnspent = async function listUnspent(args, help) { + if (help || args.length > 3) { + throw new RPCError(errs.MISC_ERROR, + 'listunspent ( minconf maxconf ["address",...] )'); + } + const wallet = this.wallet; const valid = new Validator([args]); const minDepth = valid.u32(0, 1); const maxDepth = valid.u32(1, 9999999); const addrs = valid.array(2); const height = this.wdb.state.height; - const map = new Set(); - const out = []; - if (help || args.length > 3) { - throw new RPCError(errs.MISC_ERROR, - 'listunspent ( minconf maxconf ["address",...] )'); - } + const map = new Set(); if (addrs) { const valid = new Validator([addrs]); @@ -1151,6 +1150,8 @@ RPC.prototype.listUnspent = async function listUnspent(args, help) { common.sortCoins(coins); + const out = []; + for (const coin of coins) { const depth = coin.getDepth(height); @@ -1191,16 +1192,16 @@ RPC.prototype.listUnspent = async function listUnspent(args, help) { }; RPC.prototype.lockUnspent = async function lockUnspent(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const unlock = valid.bool(0, false); - const outputs = valid.array(1); - if (help || args.length < 1 || args.length > 2) { throw new RPCError(errs.MISC_ERROR, 'lockunspent unlock ([{"txid":"txid","vout":n},...])'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const unlock = valid.bool(0, false); + const outputs = valid.array(1); + if (args.length === 1) { if (unlock) wallet.unlockCoins(); @@ -1218,9 +1219,7 @@ RPC.prototype.lockUnspent = async function lockUnspent(args, help) { if (hash == null || index == null) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter.'); - const outpoint = new Outpoint(); - outpoint.hash = hash; - outpoint.index = index; + const outpoint = new Outpoint(hash, index); if (unlock) { wallet.unlockCoin(outpoint); @@ -1239,6 +1238,12 @@ RPC.prototype.move = async function move(args, help) { }; RPC.prototype.sendFrom = async function sendFrom(args, help) { + if (help || args.length < 3 || args.length > 6) { + throw new RPCError(errs.MISC_ERROR, + 'sendfrom "fromaccount" "tobitcoinaddress"' + + ' amount ( minconf "comment" "comment-to" )'); + } + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0); @@ -1246,17 +1251,11 @@ RPC.prototype.sendFrom = async function sendFrom(args, help) { const value = valid.btc(2); const minconf = valid.u32(3, 0); - if (help || args.length < 3 || args.length > 6) { - throw new RPCError(errs.MISC_ERROR, - 'sendfrom "fromaccount" "tobitcoinaddress"' - + ' amount ( minconf "comment" "comment-to" )'); - } + const addr = parseAddress(str, this.network); if (!addr || value == null) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); - const addr = parseAddress(str, this.network); - if (name === '') name = 'default'; @@ -1277,20 +1276,18 @@ RPC.prototype.sendFrom = async function sendFrom(args, help) { }; RPC.prototype.sendMany = async function sendMany(args, help) { + if (help || args.length < 2 || args.length > 5) { + throw new RPCError(errs.MISC_ERROR, + 'sendmany "fromaccount" {"address":amount,...}' + + ' ( minconf "comment" ["address",...] )'); + } + const wallet = this.wallet; const valid = new Validator([args]); let name = valid.str(0); const sendTo = valid.obj(1); const minconf = valid.u32(2, 1); const subtractFee = valid.bool(4, false); - const uniq = new Set(); - const outputs = []; - - if (help || args.length < 2 || args.length > 5) { - throw new RPCError(errs.MISC_ERROR, - 'sendmany "fromaccount" {"address":amount,...}' - + ' ( minconf "comment" ["address",...] )'); - } if (name === '') name = 'default'; @@ -1299,6 +1296,8 @@ RPC.prototype.sendMany = async function sendMany(args, help) { throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); const to = new Validator([sendTo]); + const uniq = new Set(); + const outputs = []; for (const key of Object.keys(sendTo)) { const value = to.btc(key); @@ -1332,18 +1331,18 @@ RPC.prototype.sendMany = async function sendMany(args, help) { }; RPC.prototype.sendToAddress = async function sendToAddress(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const str = valid.str(0); - const value = valid.btc(1); - const subtractFee = valid.bool(4, false); - if (help || args.length < 2 || args.length > 5) { throw new RPCError(errs.MISC_ERROR, 'sendtoaddress "bitcoinaddress" amount' + ' ( "comment" "comment-to" subtractfeefromamount )'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const str = valid.str(0); + const value = valid.btc(1); + const subtractFee = valid.bool(4, false); + const addr = parseAddress(str, this.network); if (!addr || value == null) @@ -1389,16 +1388,16 @@ RPC.prototype.setTXFee = async function setTXFee(args, help) { }; RPC.prototype.signMessage = async function signMessage(args, help) { - const wallet = this.wallet; - const valid = new Validator([args]); - const b58 = valid.str(0, ''); - const str = valid.str(1, ''); - if (help || args.length !== 2) { throw new RPCError(errs.MISC_ERROR, 'signmessage "bitcoinaddress" "message"'); } + const wallet = this.wallet; + const valid = new Validator([args]); + const b58 = valid.str(0, ''); + const str = valid.str(1, ''); + const addr = parseHash(b58, this.network); const ring = await wallet.getKey(addr); @@ -1433,15 +1432,16 @@ RPC.prototype.walletLock = async function walletLock(args, help) { RPC.prototype.walletPassphraseChange = async function walletPassphraseChange(args, help) { const wallet = this.wallet; - const valid = new Validator([args]); - const old = valid.str(0, ''); - const new_ = valid.str(1, ''); if (help || (wallet.master.encrypted && args.length !== 2)) { throw new RPCError(errs.MISC_ERROR, 'walletpassphrasechange' + ' "oldpassphrase" "newpassphrase"'); } + const valid = new Validator([args]); + const old = valid.str(0, ''); + const new_ = valid.str(1, ''); + if (!wallet.master.encrypted) throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.'); @@ -1479,15 +1479,15 @@ RPC.prototype.walletPassphrase = async function walletPassphrase(args, help) { }; RPC.prototype.importPrunedFunds = async function importPrunedFunds(args, help) { - const valid = new Validator([args]); - const txRaw = valid.buf(0); - const blockRaw = valid.buf(1); - if (help || args.length < 2 || args.length > 3) { throw new RPCError(errs.MISC_ERROR, 'importprunedfunds "rawtransaction" "txoutproof" ( "label" )'); } + const valid = new Validator([args]); + const txRaw = valid.buf(0); + const blockRaw = valid.buf(1); + if (!txRaw || !blockRaw) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); @@ -1519,13 +1519,13 @@ RPC.prototype.importPrunedFunds = async function importPrunedFunds(args, help) { }; RPC.prototype.removePrunedFunds = async function removePrunedFunds(args, help) { + if (help || args.length !== 1) + throw new RPCError(errs.MISC_ERROR, 'removeprunedfunds "txid"'); + const wallet = this.wallet; const valid = new Validator([args]); const hash = valid.hash(0); - if (help || args.length !== 1) - throw new RPCError(errs.MISC_ERROR, 'removeprunedfunds "txid"'); - if (!hash) throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.'); @@ -1560,12 +1560,12 @@ RPC.prototype.getMemoryInfo = async function getMemoryInfo(args, help) { }; RPC.prototype.setLogLevel = async function setLogLevel(args, help) { - const valid = new Validator([args]); - const level = valid.str(0, ''); - if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'setloglevel "level"'); + const valid = new Validator([args]); + const level = valid.str(0, ''); + this.logger.setLevel(level); return null; diff --git a/lib/wallet/server.js b/lib/wallet/server.js index ae941049..d8bd4001 100644 --- a/lib/wallet/server.js +++ b/lib/wallet/server.js @@ -58,27 +58,6 @@ server.create = function create(options) { timeout: config.num('workers-timeout') }); - workers.on('spawn', (child) => { - logger.info('Spawning worker process: %d.', child.id); - }); - - workers.on('exit', (code, child) => { - logger.warning('Worker %d exited: %s.', child.id, code); - }); - - workers.on('log', (text, child) => { - logger.debug('Worker %d says:', child.id); - logger.debug(text); - }); - - workers.on('error', (err, child) => { - if (child) { - logger.error('Worker %d error: %s', child.id, err.message); - return; - } - wdb.emit('error', err); - }); - const wdb = new WalletDB({ network: config.network, logger: logger, @@ -105,5 +84,26 @@ server.create = function create(options) { wdb.on('error', () => {}); + workers.on('spawn', (child) => { + logger.info('Spawning worker process: %d.', child.id); + }); + + workers.on('exit', (code, child) => { + logger.warning('Worker %d exited: %s.', child.id, code); + }); + + workers.on('log', (text, child) => { + logger.debug('Worker %d says:', child.id); + logger.debug(text); + }); + + workers.on('error', (err, child) => { + if (child) { + logger.error('Worker %d error: %s', child.id, err.message); + return; + } + wdb.emit('error', err); + }); + return wdb; }; diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index aa936ccd..bbbab793 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -221,8 +221,10 @@ TXDB.prototype.has = function has(key) { TXDB.prototype.range = function range(options) { if (options.gte) options.gte = this.prefix(options.gte); + if (options.lte) options.lte = this.prefix(options.lte); + return this.db.range(options); }; @@ -235,8 +237,10 @@ TXDB.prototype.range = function range(options) { TXDB.prototype.keys = function keys(options) { if (options.gte) options.gte = this.prefix(options.gte); + if (options.lte) options.lte = this.prefix(options.lte); + return this.db.keys(options); }; @@ -249,8 +253,10 @@ TXDB.prototype.keys = function keys(options) { TXDB.prototype.values = function values(options) { if (options.gte) options.gte = this.prefix(options.gte); + if (options.lte) options.lte = this.prefix(options.lte); + return this.db.values(options); }; @@ -260,13 +266,13 @@ TXDB.prototype.values = function values(options) { * @returns {Promise} - Returns {@link Path}. */ -TXDB.prototype.getPath = function getPath(output) { +TXDB.prototype.getPath = async function getPath(output) { const addr = output.getAddress(); if (!addr) - return Promise.resolve(); + return null; - return this.wallet.getPath(addr); + return await this.wallet.getPath(addr); }; /** @@ -275,13 +281,13 @@ TXDB.prototype.getPath = function getPath(output) { * @returns {Promise} - Returns Boolean. */ -TXDB.prototype.hasPath = function hasPath(output) { +TXDB.prototype.hasPath = async function hasPath(output) { const addr = output.getAddress(); if (!addr) - return Promise.resolve(false); + return false; - return this.wallet.hasPath(addr); + return await this.wallet.hasPath(addr); }; /** @@ -427,8 +433,7 @@ TXDB.prototype.resolveInput = async function resolveInput(tx, index, height, pat */ TXDB.prototype.isDoubleSpend = async function isDoubleSpend(tx) { - for (const input of tx.inputs) { - const prevout = input.prevout; + for (const {prevout} of tx.inputs) { const spent = await this.isSpent(prevout.hash, prevout.index); if (spent) return true; @@ -448,9 +453,9 @@ TXDB.prototype.isRBF = async function isRBF(tx) { if (tx.isRBF()) return true; - for (const input of tx.inputs) { - const prevout = input.prevout; - if (await this.has(layout.r(prevout.hash))) + for (const {prevout} of tx.inputs) { + const key = layout.r(prevout.hash); + if (await this.has(key)) return true; } @@ -468,7 +473,7 @@ TXDB.prototype.getSpent = async function getSpent(hash, index) { const data = await this.get(layout.s(hash, index)); if (!data) - return; + return null; return Outpoint.fromRaw(data); }; @@ -593,7 +598,7 @@ TXDB.prototype.getBlock = async function getBlock(height) { const data = await this.get(layout.b(height)); if (!data) - return; + return null; return BlockRecord.fromRaw(data); }; @@ -710,7 +715,6 @@ TXDB.prototype.add = async function add(tx, block) { this.start(); let result; - try { result = await this._add(tx, block); } catch (e) { @@ -739,12 +743,12 @@ TXDB.prototype._add = async function add(tx, block) { if (existing) { // Existing tx is already confirmed. Ignore. if (existing.height !== -1) - return; + return null; // The incoming tx won't confirm the // existing one anyway. Ignore. if (!block) - return; + return null; // Confirm transaction. return await this._confirm(existing, block); @@ -760,13 +764,13 @@ TXDB.prototype._add = async function add(tx, block) { // hash to detect "passive" // replace-by-fee. this.put(layout.r(hash), null); - return; + return null; } // Potentially remove double-spenders. // Only remove if they're not confirmed. if (!(await this.removeConflicts(tx, true))) - return; + return null; } else { // Potentially remove double-spenders. await this.removeConflicts(tx, false); @@ -825,7 +829,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { if (!block) { if (!(await this.verifyInput(tx, i, coin))) { this.clear(); - return; + return null; } } @@ -911,7 +915,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { if (!updated) { // Clear the spent list inserts. this.clear(); - return; + return null; } // Save and index the transaction record. @@ -976,7 +980,7 @@ TXDB.prototype.confirm = async function confirm(hash, block) { const wtx = await this.getTX(hash); if (!wtx) - return; + return null; if (wtx.height !== -1) throw new Error('TX is already confirmed.'); @@ -1139,7 +1143,7 @@ TXDB.prototype.remove = async function remove(hash) { const wtx = await this.getTX(hash); if (!wtx) - return; + return null; return await this.removeRecursive(wtx); }; @@ -1310,7 +1314,6 @@ TXDB.prototype.unconfirm = async function unconfirm(hash) { this.start(); let details; - try { details = await this._unconfirm(hash); } catch (e) { @@ -1334,10 +1337,10 @@ TXDB.prototype._unconfirm = async function unconfirm(hash) { const wtx = await this.getTX(hash); if (!wtx) - return; + return null; if (wtx.height === -1) - return; + return null; return await this.disconnect(wtx, wtx.getBlock()); }; @@ -1546,8 +1549,10 @@ TXDB.prototype.removeConflicts = async function removeConflicts(tx, conf) { TXDB.prototype.verifyInput = async function verifyInput(tx, index, coin) { const flags = Script.flags.MANDATORY_VERIFY_FLAGS; + if (!this.options.verify) return true; + return await tx.verifyInputAsync(index, coin, flags); }; @@ -2034,16 +2039,15 @@ TXDB.prototype.getAccountCredits = async function getAccountCredits(account) { */ TXDB.prototype.getSpentCredits = async function getSpentCredits(tx) { + if (tx.isCoinbase()) + return []; + + const hash = tx.hash('hex'); const credits = []; for (let i = 0; i < tx.inputs.length; i++) credits.push(null); - if (tx.isCoinbase()) - return credits; - - const hash = tx.hash('hex'); - await this.range({ gte: layout.d(hash, 0x00000000), lte: layout.d(hash, 0xffffffff), @@ -2108,12 +2112,11 @@ TXDB.prototype.getAccountCoins = async function getAccountCoins(account) { */ TXDB.prototype.getSpentCoins = async function getSpentCoins(tx) { - const coins = []; - if (tx.isCoinbase()) - return coins; + return []; const credits = await this.getSpentCredits(tx); + const coins = []; for (const credit of credits) { if (!credit) { @@ -2185,7 +2188,7 @@ TXDB.prototype.getState = async function getState() { const data = await this.get(layout.R); if (!data) - return; + return null; return TXDBState.fromRaw(this.wallet.wid, this.wallet.id, data); }; @@ -2200,7 +2203,7 @@ TXDB.prototype.getTX = async function getTX(hash) { const raw = await this.get(layout.t(hash)); if (!raw) - return; + return null; return TXRecord.fromRaw(raw); }; @@ -2215,7 +2218,7 @@ TXDB.prototype.getDetails = async function getDetails(hash) { const wtx = await this.getTX(hash); if (!wtx) - return; + return null; return await this.toDetails(wtx); }; @@ -2297,7 +2300,7 @@ TXDB.prototype.getCoin = async function getCoin(hash, index) { const credit = await this.getCredit(hash, index); if (!credit) - return; + return null; return credit.coin; }; @@ -2312,19 +2315,19 @@ TXDB.prototype.getCoin = async function getCoin(hash, index) { TXDB.prototype.getCredit = async function getCredit(hash, index) { const state = this.state; const key = Outpoint.toKey(hash, index); - let data = this.coinCache.get(key); + const cache = this.coinCache.get(key); - if (data) { - const credit = Credit.fromRaw(data); + if (cache) { + const credit = Credit.fromRaw(cache); credit.coin.hash = hash; credit.coin.index = index; return credit; } - data = await this.get(layout.c(hash, index)); + const data = await this.get(layout.c(hash, index)); if (!data) - return; + return null; const credit = Credit.fromRaw(data); credit.coin.hash = hash; @@ -2347,7 +2350,7 @@ TXDB.prototype.getSpentCoin = async function getSpentCoin(spent, prevout) { const data = await this.get(layout.d(spent.hash, spent.index)); if (!data) - return; + return null; const coin = Coin.fromRaw(data); coin.hash = prevout.hash; @@ -2397,13 +2400,13 @@ TXDB.prototype.updateSpentCoin = async function updateSpentCoin(tx, index, heigh * @returns {Promise} - Returns Boolean. */ -TXDB.prototype.hasCoin = function hasCoin(hash, index) { +TXDB.prototype.hasCoin = async function hasCoin(hash, index) { const key = Outpoint.toKey(hash, index); if (this.coinCache.has(key)) - return Promise.resolve(true); + return true; - return this.has(layout.c(hash, index)); + return await this.has(layout.c(hash, index)); }; /** @@ -2475,16 +2478,17 @@ TXDB.prototype.getAccountBalance = async function getAccountBalance(account) { */ TXDB.prototype.zap = async function zap(account, age) { - const hashes = []; - const now = util.now(); - assert(util.isUInt32(age)); + const now = util.now(); + const txs = await this.getRange(account, { start: 0, end: now - age }); + const hashes = []; + for (const wtx of txs) { if (wtx.height !== -1) continue; diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index a64856f9..eeee26e4 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -291,7 +291,6 @@ Wallet.prototype._addSharedKey = async function addSharedKey(acct, key) { this.start(); let result; - try { result = await account.addSharedKey(key); } catch (e) { @@ -345,7 +344,6 @@ Wallet.prototype._removeSharedKey = async function removeSharedKey(acct, key) { this.start(); let result; - try { result = await account.removeSharedKey(key); } catch (e) { @@ -611,20 +609,20 @@ Wallet.prototype.getID = function getID() { const key = this.master.key.derive(44); - let bw = new StaticWriter(37); + const bw = new StaticWriter(37); bw.writeBytes(key.publicKey); bw.writeU32(this.network.magic); const hash = digest.hash160(bw.render()); - bw = new StaticWriter(27); - bw.writeU8(0x03); - bw.writeU8(0xbe); - bw.writeU8(0x04); - bw.writeBytes(hash); - bw.writeChecksum(); + const b58 = new StaticWriter(27); + b58.writeU8(0x03); + b58.writeU8(0xbe); + b58.writeU8(0x04); + b58.writeBytes(hash); + b58.writeChecksum(); - return base58.encode(bw.render()); + return base58.encode(b58.render()); }; /** @@ -681,7 +679,6 @@ Wallet.prototype._createAccount = async function createAccount(options, passphra await this.unlock(passphrase); let key; - if (this.watchOnly && options.accountKey) { key = options.accountKey; @@ -699,7 +696,7 @@ Wallet.prototype._createAccount = async function createAccount(options, passphra key = key.toPublic(); } - options = { + const opt = { wid: this.wid, id: this.id, name: this.accountDepth === 0 ? 'default' : name, @@ -716,9 +713,8 @@ Wallet.prototype._createAccount = async function createAccount(options, passphra this.start(); let account; - try { - account = Account.fromOptions(this.db, options); + account = Account.fromOptions(this.db, opt); account.wallet = this; await account.init(); } catch (e) { @@ -802,7 +798,7 @@ Wallet.prototype.getAccount = async function getAccount(acct) { const index = await this.getAccountIndex(acct); if (index === -1) - return; + return null; const unlock = await this.readLock.lock(index); @@ -820,15 +816,15 @@ Wallet.prototype.getAccount = async function getAccount(acct) { */ Wallet.prototype._getAccount = async function getAccount(index) { - let account = this.accountCache.get(index); + const cache = this.accountCache.get(index); - if (account) - return account; + if (cache) + return cache; - account = await this.db.getAccount(this.wid, index); + const account = await this.db.getAccount(this.wid, index); if (!account) - return; + return null; account.wallet = this; account.wid = this.wid; @@ -856,12 +852,12 @@ Wallet.prototype.getAccountIndex = async function getAccountIndex(name) { if (typeof name === 'number') return name; - let index = this.indexCache.get(name); + const cache = this.indexCache.get(name); - if (index != null) - return index; + if (cache != null) + return cache; - index = await this.db.getAccountIndex(this.wid, name); + const index = await this.db.getAccountIndex(this.wid, name); if (index === -1) return -1; @@ -979,7 +975,6 @@ Wallet.prototype._createKey = async function createKey(acct, branch) { this.start(); let result; - try { result = await account.createKey(branch); } catch (e) { @@ -1060,7 +1055,7 @@ Wallet.prototype.getPath = async function getPath(address) { const path = await this.readPath(address); if (!path) - return; + return null; path.name = await this.getAccountName(path.account); @@ -1080,15 +1075,15 @@ Wallet.prototype.getPath = async function getPath(address) { Wallet.prototype.readPath = async function readPath(address) { const hash = Address.getHash(address, 'hex'); - let path = this.pathCache.get(hash); + const cache = this.pathCache.get(hash); - if (path) - return path; + if (cache) + return cache; - path = await this.db.getPath(this.wid, hash); + const path = await this.db.getPath(this.wid, hash); if (!path) - return; + return null; path.id = this.id; @@ -1147,10 +1142,11 @@ Wallet.prototype.getAccountPaths = async function getAccountPaths(acct) { const index = await this.ensureIndex(acct, true); const hashes = await this.getAccountHashes(index); const name = await this.getAccountName(acct); - const result = []; assert(name); + const result = []; + for (const hash of hashes) { const path = await this.readPath(hash); @@ -1230,9 +1226,8 @@ Wallet.prototype._importKey = async function importKey(acct, ring, passphrase) { await this.unlock(passphrase); - ring = WalletKey.fromRing(account, ring); - - const path = ring.toPath(); + const key = WalletKey.fromRing(account, ring); + const path = key.toPath(); if (this.master.encrypted) { path.data = this.master.encipher(path.data, path.hash); @@ -1370,7 +1365,6 @@ Wallet.prototype._fund = async function fund(mtx, options) { throw new Error('Cannot fund from watch-only wallet.'); let account; - if (options.account != null) { account = await this.getAccount(options.account); if (!account) @@ -1383,12 +1377,10 @@ Wallet.prototype._fund = async function fund(mtx, options) { throw new Error('Account is not initialized.'); let rate = options.rate; - if (rate == null) rate = await this.db.estimateFee(options.blocks); let coins; - if (options.smart) { coins = await this.getSmartCoins(options.account); } else { @@ -1423,7 +1415,7 @@ Wallet.prototype.getAccountByAddress = async function getAccountByAddress(addres const path = await this.getPath(hash); if (!path) - return; + return null; return await this.getAccount(path.account); }; @@ -1627,10 +1619,10 @@ Wallet.prototype._send = async function send(options, passphrase) { */ Wallet.prototype.increaseFee = async function increaseFee(hash, rate, passphrase) { - const wtx = await this.getTX(hash); - assert(util.isUInt32(rate), 'Rate must be a number.'); + const wtx = await this.getTX(hash); + if (!wtx) throw new Error('Transaction not found.'); @@ -1728,11 +1720,12 @@ Wallet.prototype.increaseFee = async function increaseFee(hash, rate, passphrase Wallet.prototype.resend = async function resend() { const wtxs = await this.getPending(); - const txs = []; if (wtxs.length > 0) this.logger.info('Rebroadcasting %d transactions.', wtxs.length); + const txs = []; + for (const wtx of wtxs) txs.push(wtx.tx); @@ -1783,12 +1776,12 @@ Wallet.prototype.getKey = async function getKey(address) { const path = await this.getPath(hash); if (!path) - return; + return null; const account = await this.getAccount(path.account); if (!account) - return; + return null; return account.derivePath(path, this.master); }; @@ -1806,19 +1799,19 @@ Wallet.prototype.getPrivateKey = async function getPrivateKey(address, passphras const path = await this.getPath(hash); if (!path) - return; + return null; const account = await this.getAccount(path.account); if (!account) - return; + return null; await this.unlock(passphrase); const key = account.derivePath(path, this.master); if (!key.privateKey) - return; + return null; return key; }; @@ -1830,14 +1823,13 @@ Wallet.prototype.getPrivateKey = async function getPrivateKey(address, passphras */ Wallet.prototype.getInputPaths = async function getInputPaths(mtx) { - const paths = []; - assert(mtx.mutable); if (!mtx.hasCoins()) throw new Error('Not all coins available.'); const hashes = mtx.getInputHashes('hex'); + const paths = []; for (const hash of hashes) { const path = await this.getPath(hash); @@ -1927,10 +1919,6 @@ Wallet.prototype._setLookahead = async function setLookahead(acct, lookahead) { Wallet.prototype.syncOutputDepth = async function syncOutputDepth(details) { const map = new Map(); - const derived = []; - - if (!details) - return derived; for (const output of details.outputs) { const path = output.path; @@ -1947,6 +1935,8 @@ Wallet.prototype.syncOutputDepth = async function syncOutputDepth(details) { map.get(path.account).push(path); } + const derived = []; + for (const [acct, paths] of map) { let receive = -1; let change = -1; @@ -1998,7 +1988,7 @@ Wallet.prototype.getRedeem = async function getRedeem(hash) { const ring = await this.getKey(hash.toString('hex')); if (!ring) - return; + return null; return ring.getRedeem(hash); }; @@ -2146,7 +2136,8 @@ Wallet.prototype._add = async function add(tx, block) { let details, derived; try { details = await this.txdb._add(tx, block); - derived = await this.syncOutputDepth(details); + if (details) + derived = await this.syncOutputDepth(details); } catch (e) { this.txdb.drop(); throw e; @@ -2154,7 +2145,7 @@ Wallet.prototype._add = async function add(tx, block) { await this.txdb.commit(); - if (derived.length > 0) { + if (derived && derived.length > 0) { this.db.emit('address', this.id, derived); this.emit('address', derived); } diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 131fa3b7..a1aff965 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -313,7 +313,6 @@ WalletDB.prototype.init = async function init() { } let tip; - if (this.client) { if (startHeight != null) { tip = await this.client.getEntry(startHeight); @@ -403,12 +402,12 @@ WalletDB.prototype.watch = async function watch() { */ WalletDB.prototype.sync = async function sync() { - let height = this.state.height; - let entry; - if (!this.client) return; + let height = this.state.height; + let entry; + while (height >= 0) { const tip = await this.getBlock(height); @@ -590,8 +589,6 @@ WalletDB.prototype.backup = function backup(path) { */ WalletDB.prototype.wipe = async function wipe() { - const batch = this.db.batch(); - this.logger.warning('Wiping WalletDB TXDB...'); this.logger.warning('I hope you know what you\'re doing.'); @@ -600,6 +597,7 @@ WalletDB.prototype.wipe = async function wipe() { lte: Buffer.from([0xff]) }); + const batch = this.db.batch(); let total = 0; for (;;) { @@ -816,22 +814,22 @@ WalletDB.prototype.unregister = function unregister(wallet) { WalletDB.prototype.getWalletID = async function getWalletID(id) { if (!id) - return; + return null; if (typeof id === 'number') return id; - let wid = this.widCache.get(id); + const cache = this.widCache.get(id); - if (wid) - return wid; + if (cache) + return cache; const data = await this.db.get(layout.l(id)); if (!data) - return; + return null; - wid = data.readUInt32LE(0, true); + const wid = data.readUInt32LE(0, true); this.widCache.set(id, wid); @@ -848,7 +846,7 @@ WalletDB.prototype.get = async function get(id) { const wid = await this.getWalletID(id); if (!wid) - return; + return null; const unlock = await this.readLock.lock(wid); @@ -867,17 +865,17 @@ WalletDB.prototype.get = async function get(id) { */ WalletDB.prototype._get = async function get(wid) { - let wallet = this.wallets.get(wid); + const cache = this.wallets.get(wid); - if (wallet) - return wallet; + if (cache) + return cache; const data = await this.db.get(layout.w(wid)); if (!data) - return; + return null; - wallet = Wallet.fromRaw(this, data); + const wallet = Wallet.fromRaw(this, data); await wallet.open(); @@ -981,7 +979,7 @@ WalletDB.prototype.auth = async function auth(wid, token) { const wallet = await this.get(wid); if (!wallet) - return; + return null; if (typeof token === 'string') { if (!util.isHex256(token)) @@ -1059,8 +1057,10 @@ WalletDB.prototype.has = async function has(id) { WalletDB.prototype.ensure = async function ensure(options) { const wallet = await this.get(options.id); + if (wallet) return wallet; + return await this.create(options); }; @@ -1076,7 +1076,7 @@ WalletDB.prototype.getAccount = async function getAccount(wid, index) { const data = await this.db.get(layout.a(wid, index)); if (!data) - return; + return null; return Account.fromRaw(this, data); }; @@ -1122,7 +1122,7 @@ WalletDB.prototype.getAccountName = async function getAccountName(wid, index) { const name = await this.db.get(layout.n(wid, index)); if (!name) - return; + return null; return name.toString('ascii'); }; @@ -1171,17 +1171,17 @@ WalletDB.prototype.hasAccount = function hasAccount(wid, index) { */ WalletDB.prototype.getPathMap = async function getPathMap(hash) { - let map = this.pathMapCache.get(hash); + const cache = this.pathMapCache.get(hash); - if (map) - return map; + if (cache) + return cache; const data = await this.db.get(layout.p(hash)); if (!data) - return; + return null; - map = PathMapRecord.fromRaw(hash, data); + const map = PathMapRecord.fromRaw(hash, data); this.pathMapCache.set(hash, map); @@ -1251,7 +1251,7 @@ WalletDB.prototype.getPath = async function getPath(wid, hash) { const data = await this.db.get(layout.P(wid, hash)); if (!data) - return; + return null; const path = Path.fromRaw(data); path.wid = wid; @@ -1336,13 +1336,13 @@ WalletDB.prototype.getAccountHashes = function getAccountHashes(wid, account) { */ WalletDB.prototype.getWalletPaths = async function getWalletPaths(wid) { - const paths = []; - const items = await this.db.range({ gte: layout.P(wid, encoding.NULL_HASH), lte: layout.P(wid, encoding.HIGH_HASH) }); + const paths = []; + for (const item of items) { const hash = layout.Pp(item.key); const path = Path.fromRaw(item.value); @@ -1536,7 +1536,7 @@ WalletDB.prototype.getWalletsByTX = async function getWalletsByTX(tx) { } if (result.size === 0) - return; + return null; return result; }; @@ -1550,7 +1550,7 @@ WalletDB.prototype.getState = async function getState() { const data = await this.db.get(layout.R); if (!data) - return; + return null; return ChainState.fromRaw(data); }; @@ -1668,7 +1668,7 @@ WalletDB.prototype.getBlockMap = async function getBlockMap(height) { const data = await this.db.get(layout.b(height)); if (!data) - return; + return null; return BlockMapRecord.fromRaw(height, data); }; @@ -1707,7 +1707,7 @@ WalletDB.prototype.getOutpointMap = async function getOutpointMap(hash, index) { const data = await this.db.get(layout.o(hash, index)); if (!data) - return; + return null; return OutpointMapRecord.fromRaw(hash, index, data); }; @@ -1750,7 +1750,7 @@ WalletDB.prototype.getBlock = async function getBlock(height) { const data = await this.db.get(layout.h(height)); if (!data) - return; + return null; const block = new BlockMeta(); block.hash = data.toString('hex'); @@ -2055,7 +2055,7 @@ WalletDB.prototype._insert = async function insert(tx, block) { assert(!tx.mutable, 'WDB: Cannot add mutable TX.'); if (!wids) - return; + return null; this.logger.info( 'Incoming transaction for %d wallets in WalletDB (%s).', @@ -2082,7 +2082,7 @@ WalletDB.prototype._insert = async function insert(tx, block) { } if (!result) - return; + return null; return wids; }; diff --git a/lib/workers/parser.js b/lib/workers/parser.js index f98fd76f..f9c9672f 100644 --- a/lib/workers/parser.js +++ b/lib/workers/parser.js @@ -43,35 +43,32 @@ Parser.prototype.feed = function feed(data) { }; Parser.prototype.read = function read(size) { - let pending, chunk, off, len; - assert(this.total >= size, 'Reading too much.'); if (size === 0) return Buffer.alloc(0); - pending = this.pending[0]; + const pending = this.pending[0]; if (pending.length > size) { - chunk = pending.slice(0, size); + const chunk = pending.slice(0, size); this.pending[0] = pending.slice(size); this.total -= chunk.length; return chunk; } if (pending.length === size) { - chunk = this.pending.shift(); + const chunk = this.pending.shift(); this.total -= chunk.length; return chunk; } - chunk = Buffer.allocUnsafe(size); - off = 0; - len = 0; + const chunk = Buffer.allocUnsafe(size); + let off = 0; while (off < chunk.length) { - pending = this.pending[0]; - len = pending.copy(chunk, off); + const pending = this.pending[0]; + const len = pending.copy(chunk, off); if (len === pending.length) this.pending.shift(); else @@ -88,7 +85,6 @@ Parser.prototype.read = function read(size) { Parser.prototype.parse = function parse(data) { let header = this.header; - let packet; if (!header) { try { @@ -107,6 +103,7 @@ Parser.prototype.parse = function parse(data) { this.waiting = 9; this.header = null; + let packet; try { packet = this.parsePacket(header, data); } catch (e) { diff --git a/migrate/chaindb2to3.js b/migrate/chaindb2to3.js index e9405923..9e830b74 100644 --- a/migrate/chaindb2to3.js +++ b/migrate/chaindb2to3.js @@ -563,6 +563,9 @@ function bpair(prefix, hash, index) { return key; } +// Make eslint happy. +reserializeEntries; + (async () => { await db.open(); diff --git a/migrate/walletdb4to5.js b/migrate/walletdb4to5.js index eb0e58f6..9a767607 100644 --- a/migrate/walletdb4to5.js +++ b/migrate/walletdb4to5.js @@ -26,7 +26,7 @@ async function updateVersion() { const data = await db.get('V'); assert(data, 'No version.'); - ver = data.readUInt32LE(0, true); + let ver = data.readUInt32LE(0, true); if (ver !== 4) throw Error(`DB is version ${ver}.`); @@ -35,7 +35,7 @@ async function updateVersion() { await db.backup(bak); - let ver = Buffer.allocUnsafe(4); + ver = Buffer.allocUnsafe(4); ver.writeUInt32LE(5, 0, true); batch.put('V', ver); } diff --git a/migrate/walletdb5to6.js b/migrate/walletdb5to6.js index 656a0675..353cf4a1 100644 --- a/migrate/walletdb5to6.js +++ b/migrate/walletdb5to6.js @@ -29,7 +29,7 @@ async function updateVersion() { const data = await db.get('V'); assert(data, 'No version.'); - ver = data.readUInt32LE(0, true); + let ver = data.readUInt32LE(0, true); if (ver !== 5) throw Error(`DB is version ${ver}.`); @@ -38,7 +38,7 @@ async function updateVersion() { await db.backup(bak); - let ver = Buffer.allocUnsafe(4); + ver = Buffer.allocUnsafe(4); ver.writeUInt32LE(6, 0, true); batch.put('V', ver); } diff --git a/test/util/memwallet.js b/test/util/memwallet.js index 2345b0f4..30e7061b 100644 --- a/test/util/memwallet.js +++ b/test/util/memwallet.js @@ -146,7 +146,7 @@ MemWallet.prototype.deriveKey = function deriveKey(branch, index) { MemWallet.prototype.getKey = function getKey(hash) { const path = this.paths.get(hash); if (!path) - return; + return null; return this.derivePath(path); };