From d0ed21406774a914c3bc8d577f2b7ae5b43c8178 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 26 Oct 2017 04:07:36 -0700 Subject: [PATCH] bcoin: remove dependence on util.js. --- bin/cli | 23 +- bin/spvnode | 3 +- browser/index.js | 5 +- browser/wsproxy.js | 14 +- lib/bip70/paymentdetails.js | 4 +- lib/bip70/paymentrequest.js | 5 +- lib/bip70/x509.js | 4 +- lib/blockchain/chain.js | 17 +- lib/blockchain/chainentry.js | 20 +- lib/blockchain/layout-browser.js | 64 ++- lib/btc/amount.js | 12 +- lib/btc/uri.js | 4 +- lib/hd/mnemonic.js | 6 +- lib/hd/private.js | 13 +- lib/hd/public.js | 13 +- lib/http/rpc.js | 97 ++-- lib/http/server.js | 7 +- lib/mempool/fees.js | 9 +- lib/mempool/mempool.js | 17 +- lib/mining/miner.js | 17 +- lib/mining/template.js | 4 +- lib/net/bip151.js | 5 +- lib/net/bip152.js | 4 +- lib/net/common.js | 12 + lib/net/hostlist.js | 27 +- lib/net/packets.js | 64 ++- lib/net/parser.js | 10 +- lib/net/peer.js | 25 +- lib/net/pool.js | 28 +- lib/net/proxysocket.js | 5 +- lib/net/socks.js | 15 +- lib/node/config.js | 6 +- lib/node/logger.js | 77 ++- lib/primitives/abstractblock.js | 16 +- lib/primitives/address.js | 13 +- lib/primitives/block.js | 2 +- lib/primitives/coin.js | 20 +- lib/primitives/headers.js | 2 +- lib/primitives/input.js | 6 +- lib/primitives/invitem.js | 15 +- lib/primitives/merkleblock.js | 8 +- lib/primitives/mtx.js | 40 +- lib/primitives/netaddress.js | 6 +- lib/primitives/outpoint.js | 31 +- lib/primitives/output.js | 9 +- lib/primitives/tx.js | 14 +- lib/primitives/txmeta.js | 16 +- lib/protocol/network.js | 4 +- lib/protocol/timedata.js | 5 +- lib/script/opcode.js | 29 +- lib/script/program.js | 3 +- lib/script/script.js | 15 +- lib/script/sigcache.js | 5 +- lib/script/witness.js | 1 - lib/utils/base58.js | 11 + lib/utils/bech32.js | 22 + lib/utils/binary.js | 85 +++ lib/utils/enforce.js | 165 ------ lib/utils/fixed.js | 216 ++++++++ lib/utils/hashwriter.js | 500 +++++++++++++++++ lib/utils/index.js | 1 + lib/utils/util.js | 920 +------------------------------ lib/utils/validator.js | 4 +- lib/wallet/account.js | 27 +- lib/wallet/client.js | 1 + lib/wallet/http.js | 13 +- lib/wallet/layout-browser.js | 90 +-- lib/wallet/masterkey.js | 8 +- lib/wallet/rpc.js | 42 +- lib/wallet/txdb.js | 2 +- lib/wallet/wallet.js | 9 +- lib/wallet/walletdb.js | 16 +- lib/workers/master.js | 6 +- lib/workers/worker.js | 4 - lib/workers/workerpool.js | 5 +- migrate/coins-old.js | 4 +- migrate/coins/coins.js | 4 +- scripts/fuzz.js | 127 ++--- scripts/gen.js | 57 +- test/bech32-test.js | 16 + test/http-test.js | 3 +- test/node-test.js | 2 +- test/protocol-test.js | 4 +- test/script-test.js | 4 +- test/tx-test.js | 3 +- test/utils-test.js | 26 +- 86 files changed, 1673 insertions(+), 1620 deletions(-) create mode 100644 lib/utils/binary.js delete mode 100644 lib/utils/enforce.js create mode 100644 lib/utils/fixed.js create mode 100644 lib/utils/hashwriter.js diff --git a/bin/cli b/bin/cli index d82fb955..289c75eb 100755 --- a/bin/cli +++ b/bin/cli @@ -3,7 +3,6 @@ 'use strict'; const Config = require('../lib/node/config'); -const util = require('../lib/utils/util'); const {NodeClient, WalletClient} = require('bclient'); const ports = { @@ -178,9 +177,9 @@ CLI.prototype.getWallet = async function getWallet() { }; CLI.prototype.getTX = async function getTX() { - const hash = this.config.str(0); + const hash = this.config.str(0, ''); - if (util.isBase58(hash) || util.isBech32(hash)) { + if (hash.length !== 64) { const txs = await this.client.getTXByAddress(hash); this.log(txs); return; @@ -197,7 +196,7 @@ CLI.prototype.getTX = async function getTX() { }; CLI.prototype.getBlock = async function getBlock() { - let hash = this.config.str(0); + let hash = this.config.str(0, ''); if (hash.length !== 64) hash = parseInt(hash, 10); @@ -213,10 +212,10 @@ CLI.prototype.getBlock = async function getBlock() { }; CLI.prototype.getCoin = async function getCoin() { - const hash = this.config.str(0); + const hash = this.config.str(0, ''); const index = this.config.uint(1); - if (util.isBase58(hash) || util.isBech32(hash)) { + if (hash.length !== 64) { const coins = await this.client.getCoinsByAddress(hash); this.log(coins); return; @@ -471,19 +470,15 @@ CLI.prototype.importKey = async function importKey() { if (!key) throw new Error('No key for import.'); - if (util.isBase58(key)) { - await this.wallet.importPrivate(account, key, passphrase); - this.log('Imported private key.'); - return; - } - - if (util.isHex(key)) { + if (key.length === 66 || key.length === 130) { await this.wallet.importPublic(account, key); this.log('Imported public key.'); return; } - throw new Error('Bad key for import.'); + await this.wallet.importPrivate(account, key, passphrase); + + this.log('Imported private key.'); }; CLI.prototype.importAddress = async function importAddress() { diff --git a/bin/spvnode b/bin/spvnode index 71fae73e..43fde24c 100755 --- a/bin/spvnode +++ b/bin/spvnode @@ -6,7 +6,6 @@ process.title = 'bcoin'; const assert = require('assert'); const SPVNode = require('../lib/node/spvnode'); -const util = require('../lib/utils/util'); const Outpoint = require('../lib/primitives/outpoint'); const node = SPVNode({ @@ -44,7 +43,7 @@ process.on('unhandledRejection', (err, promise) => { node.on('block', (block) => { assert(block.txs.length >= 1); if (block.txs.length > 1) - util.log(block.txs[1]); + console.log(block.txs[1]); }); } diff --git a/browser/index.js b/browser/index.js index 16bed188..400970ff 100644 --- a/browser/index.js +++ b/browser/index.js @@ -31,14 +31,15 @@ floating.onmouseup = function(ev) { }; function show(obj) { - floating.innerHTML = escape(util.inspectify(obj, false)); + const json = obj && obj.toJSON ? obj.toJSON() : null; + floating.innerHTML = escape(JSON.stringify(json, null, 2)); floating.style.display = 'block'; } logger = new bcoin.logger({ level: 'debug', console: true }); logger.writeConsole = function(level, module, args) { var name = bcoin.logger.levelsByVal[level]; - var msg = util.format(args, false); + var msg = this.fmt(args, false); if (++scrollback > 1000) { log.innerHTML = ''; scrollback = 1; diff --git a/browser/wsproxy.js b/browser/wsproxy.js index 80414435..926a2f97 100644 --- a/browser/wsproxy.js +++ b/browser/wsproxy.js @@ -4,7 +4,6 @@ const assert = require('assert'); const net = require('net'); const EventEmitter = require('events'); const bsock = require('bsock'); -const util = require('../lib/utils/util'); const digest = require('bcrypto/lib/digest'); const IP = require('../lib/utils/ip'); const BufferWriter = require('../lib/utils/writer'); @@ -76,7 +75,7 @@ WSProxy.prototype.handleConnect = function handleConnect(ws, port, host, nonce) return; } - if (!util.isU16(port) + if ((port & 0xffff) !== port || typeof host !== 'string' || host.length === 0) { this.log('Client gave bad arguments (%s).', state.host); @@ -86,7 +85,7 @@ WSProxy.prototype.handleConnect = function handleConnect(ws, port, host, nonce) } if (this.pow) { - if (!util.isU32(nonce)) { + if ((nonce >>> 0) !== nonce) { this.log('Client did not solve proof of work (%s).', state.host); ws.fire('tcp close'); ws.destroy(); @@ -228,7 +227,7 @@ WSProxy.prototype.attach = function attach(server) { function SocketState(server, socket) { this.pow = server.pow; this.target = server.target; - this.snonce = util.nonce(); + this.snonce = nonce(); this.socket = null; this.host = IP.normalize(socket.conn.remoteAddress); this.remoteHost = null; @@ -248,4 +247,11 @@ SocketState.prototype.connect = function connect(port, host) { return this.socket; }; +function nonce() { + const buf = Buffer.allocUnsafe(8); + buf.writeUInt32LE(Math.random() * 0x100000000, 0, true); + buf.writeUInt32LE(Math.random() * 0x100000000, 4, true); + return buf; +} + module.exports = WSProxy; diff --git a/lib/bip70/paymentdetails.js b/lib/bip70/paymentdetails.js index c33d8c42..34142b3b 100644 --- a/lib/bip70/paymentdetails.js +++ b/lib/bip70/paymentdetails.js @@ -64,12 +64,12 @@ PaymentDetails.prototype.fromOptions = function fromOptions(options) { } if (options.time != null) { - assert(util.isInt(options.time)); + assert(Number.isSafeInteger(options.time)); this.time = options.time; } if (options.expires != null) { - assert(util.isInt(options.expires)); + assert(Number.isSafeInteger(options.expires)); this.expires = options.expires; } diff --git a/lib/bip70/paymentrequest.js b/lib/bip70/paymentrequest.js index 80a9db83..b2927948 100644 --- a/lib/bip70/paymentrequest.js +++ b/lib/bip70/paymentrequest.js @@ -7,10 +7,9 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); +const PEM = require('bcrypto/lib/utils/pem'); const x509 = require('./x509'); -const PEM = require('../utils/pem'); const ProtoReader = require('../utils/protoreader'); const ProtoWriter = require('../utils/protowriter'); const PaymentDetails = require('./paymentdetails'); @@ -50,7 +49,7 @@ function PaymentRequest(options) { PaymentRequest.prototype.fromOptions = function fromOptions(options) { if (options.version != null) { - assert(util.isInt(options.version)); + assert((options.version | 0) === options.version); this.version = options.version; } diff --git a/lib/bip70/x509.js b/lib/bip70/x509.js index 6ae45925..2d1de4f6 100644 --- a/lib/bip70/x509.js +++ b/lib/bip70/x509.js @@ -7,8 +7,8 @@ 'use strict'; const assert = require('assert'); -const ASN1 = require('../utils/asn1'); -const PEM = require('../utils/pem'); +const ASN1 = require('bcrypto/lib/utils/asn1'); +const PEM = require('bcrypto/lib/utils/pem'); const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const pk = require('./pk'); diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 6a982745..f57caec9 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -1071,7 +1071,7 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f if (entry.hasUnknown(this.network)) { this.logger.warning( 'Unknown version bits in block %d: %s.', - entry.height, util.hex32(entry.version)); + entry.height, entry.version.toString(16)); } // Otherwise, everything is in order. @@ -1136,7 +1136,7 @@ Chain.prototype.saveAlternate = async function saveAlternate(entry, block, prev, if (entry.hasUnknown(this.network)) { this.logger.warning( 'Unknown version bits in block %d: %s.', - entry.height, util.hex32(entry.version)); + entry.height, entry.version.toString(16)); } await this.db.save(entry, block); @@ -1549,7 +1549,7 @@ Chain.prototype.logStatus = function logStatus(start, block, entry) { if (this.db.coinCache.capacity > 0) { this.logger.debug('Coin Cache: size=%dmb, items=%d.', - util.mb(this.db.coinCache.size), this.db.coinCache.items); + this.db.coinCache.size / (1 << 20), this.db.coinCache.items); } }; @@ -2747,12 +2747,13 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) { } if (options.maxFiles != null) { - assert(util.isU32(options.maxFiles)); + assert((options.maxFiles >>> 0) === options.maxFiles); this.maxFiles = options.maxFiles; } if (options.cacheSize != null) { - assert(util.isU64(options.cacheSize)); + assert(Number.isSafeInteger(options.cacheSize)); + assert(options.cacheSize >= 0); this.cacheSize = options.cacheSize; } @@ -2792,17 +2793,17 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) { } if (options.coinCache != null) { - assert(util.isU64(options.coinCache)); + assert((options.coinCache >>> 0) === options.coinCache); this.coinCache = options.coinCache; } if (options.entryCache != null) { - assert(util.isU32(options.entryCache)); + assert((options.entryCache >>> 0) === options.entryCache); this.entryCache = options.entryCache; } if (options.maxOrphans != null) { - assert(util.isU32(options.maxOrphans)); + assert((options.maxOrphans >>> 0) === options.maxOrphans); this.maxOrphans = options.maxOrphans; } diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index 2fcc5011..0afd9a68 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -76,13 +76,13 @@ ChainEntry.MAX_CHAINWORK = new BN(1).ushln(256); ChainEntry.prototype.fromOptions = function fromOptions(options) { assert(options, 'Block data is required.'); assert(typeof options.hash === 'string'); - assert(util.isU32(options.version)); + assert((options.version >>> 0) === options.version); assert(typeof options.prevBlock === 'string'); assert(typeof options.merkleRoot === 'string'); - assert(util.isU32(options.time)); - assert(util.isU32(options.bits)); - assert(util.isU32(options.nonce)); - assert(util.isU32(options.height)); + assert((options.time >>> 0) === options.time); + assert((options.bits >>> 0) === options.bits); + assert((options.nonce >>> 0) === options.nonce); + assert((options.height >>> 0) === options.height); assert(!options.chainwork || BN.isBN(options.chainwork)); this.hash = options.hash; @@ -298,12 +298,12 @@ ChainEntry.prototype.toJSON = function toJSON() { ChainEntry.prototype.fromJSON = function fromJSON(json) { assert(json, 'Block data is required.'); assert(typeof json.hash === 'string'); - assert(util.isU32(json.version)); + assert((json.version >>> 0) === json.version); assert(typeof json.prevBlock === 'string'); assert(typeof json.merkleRoot === 'string'); - assert(util.isU32(json.time)); - assert(util.isU32(json.bits)); - assert(util.isU32(json.nonce)); + assert((json.time >>> 0) === json.time); + assert((json.bits >>> 0) === json.bits); + assert((json.nonce >>> 0) === json.nonce); assert(typeof json.chainwork === 'string'); this.hash = util.revHex(json.hash); @@ -354,7 +354,7 @@ ChainEntry.prototype.toInv = function toInv() { ChainEntry.prototype.inspect = function inspect() { const json = this.toJSON(); - json.version = util.hex32(json.version); + json.version = json.version.toString(16); return json; }; diff --git a/lib/blockchain/layout-browser.js b/lib/blockchain/layout-browser.js index 701a627b..eaeba263 100644 --- a/lib/blockchain/layout-browser.js +++ b/lib/blockchain/layout-browser.js @@ -7,9 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); -const pad8 = util.pad8; -const pad32 = util.pad32; const layout = { binary: false, @@ -23,7 +20,7 @@ const layout = { return 'h' + hex(hash); }, H: function H(height) { - return 'H' + pad32(height); + return 'H' + hex32(height); }, n: function n(hash) { return 'n' + hex(hash); @@ -38,18 +35,18 @@ const layout = { return 't' + hex(hash); }, c: function c(hash, index) { - return 'c' + hex(hash) + pad32(index); + return 'c' + hex(hash) + hex32(index); }, u: function u(hash) { return 'u' + hex(hash); }, v: function v(bit, hash) { - return 'v' + pad8(bit) + hex(hash); + return 'v' + hex8(bit) + hex(hash); }, vv: function vv(key) { assert(typeof key === 'string'); assert(key.length === 36); - return [parseInt(key.slice(1, 4), 10), key.slice(4, 36)]; + return [parseInt(key.slice(1, 3), 16), key.slice(3, 35)]; }, T: function T(addr, hash) { addr = hex(addr); @@ -64,10 +61,10 @@ const layout = { addr = hex(addr); if (addr.length === 64) - return 'X' + addr + hex(hash) + pad32(index); + return 'X' + addr + hex(hash) + hex32(index); assert(addr.length === 40); - return 'C' + addr + hex(hash) + pad32(index); + return 'C' + addr + hex(hash) + hex32(index); }, pp: function pp(key) { assert(typeof key === 'string'); @@ -78,12 +75,12 @@ const layout = { assert(typeof key === 'string'); let hash, index; - if (key.length === 139) { + if (key.length === 137) { hash = key.slice(65, 129); - index = parseInt(key.slice(129), 10); - } else if (key.length === 115) { + index = parseInt(key.slice(129), 16); + } else if (key.length === 113) { hash = key.slice(41, 105); - index = parseInt(key.slice(105), 10); + index = parseInt(key.slice(105), 16); } else { assert(false); } @@ -112,6 +109,47 @@ function hex(hash) { return hash; } +function hex8(num) { + assert(typeof num === 'number'); + assert(num >= 0 && num <= 255); + + if (num <= 0x0f) + return '0' + num.toString(16); + + if (num <= 0xff) + return num.toString(16); + + throw new Error('Number too big.'); +} + +function hex32(num) { + assert(typeof num === 'number'); + assert(num >= 0); + + num = num.toString(16); + + switch (num.length) { + case 1: + return '0000000' + num; + case 2: + return '000000' + num; + case 3: + return '00000' + num; + case 4: + return '0000' + num; + case 5: + return '000' + num; + case 6: + return '00' + num; + case 7: + return '0' + num; + case 8: + return num; + } + + throw new Error('Number too big.'); +} + /* * Expose */ diff --git a/lib/btc/amount.js b/lib/btc/amount.js index 7c9bb45d..1d319e91 100644 --- a/lib/btc/amount.js +++ b/lib/btc/amount.js @@ -7,7 +7,7 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); +const fixed = require('../utils/fixed'); /** * Represents a bitcoin amount (satoshis internally). @@ -138,7 +138,7 @@ Amount.prototype.toString = function toString() { */ Amount.prototype.fromValue = function fromValue(value) { - assert(util.isI64(value), 'Value must be an int64.'); + assert(Number.isSafeInteger(value) && value >= 0, 'Value must be an int64.'); this.value = value; return this; }; @@ -335,8 +335,8 @@ Amount.value = function value(str) { Amount.encode = function encode(value, exp, num) { if (num) - return util.toFloat(value, exp); - return util.toFixed(value, exp); + return fixed.toFloat(value, exp); + return fixed.encode(value, exp); }; /** @@ -349,8 +349,8 @@ Amount.encode = function encode(value, exp, num) { Amount.decode = function decode(value, exp) { if (typeof value === 'number') - return util.fromFloat(value, exp); - return util.fromFixed(value, exp); + return fixed.fromFloat(value, exp); + return fixed.decode(value, exp); }; /* diff --git a/lib/btc/uri.js b/lib/btc/uri.js index fa44142d..b4e98d71 100644 --- a/lib/btc/uri.js +++ b/lib/btc/uri.js @@ -7,7 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Address = require('../primitives/address'); const Amount = require('./amount'); @@ -52,7 +51,8 @@ URI.prototype.fromOptions = function fromOptions(options) { this.address.fromOptions(options.address); if (options.amount != null) { - assert(util.isU64(options.amount), 'Amount must be a uint64.'); + assert(Number.isSafeInteger(options.amount) && options.amount >= 0, + 'Amount must be a uint64.'); this.amount = options.amount; } diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index ba8e6858..f2d8f519 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -7,7 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const cleanse = require('bcrypto/lib/cleanse'); const random = require('bcrypto/lib/random'); @@ -84,7 +83,7 @@ Mnemonic.prototype.fromOptions = function fromOptions(options) { options = { phrase: options }; if (options.bits != null) { - assert(util.isU16(options.bits)); + assert((options.bits & 0xffff) === options.bits); assert(options.bits >= common.MIN_ENTROPY); assert(options.bits <= common.MAX_ENTROPY); assert(options.bits % 32 === 0); @@ -403,7 +402,8 @@ Mnemonic.prototype.toJSON = function toJSON() { */ Mnemonic.prototype.fromJSON = function fromJSON(json) { - assert(util.isU16(json.bits)); + assert(json); + assert((json.bits & 0xffff) === json.bits); assert(typeof json.language === 'string'); assert(typeof json.entropy === 'string'); assert(typeof json.phrase === 'string'); diff --git a/lib/hd/private.js b/lib/hd/private.js index d80591f0..406b7019 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -7,7 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const cleanse = require('bcrypto/lib/cleanse'); const random = require('bcrypto/lib/random'); @@ -72,9 +71,9 @@ function HDPrivateKey(options) { HDPrivateKey.prototype.fromOptions = function fromOptions(options) { assert(options, 'No options for HD private key.'); - assert(util.isU8(options.depth)); - assert(util.isU32(options.parentFingerPrint)); - assert(util.isU32(options.childIndex)); + assert((options.depth & 0xff) === options.depth); + assert((options.parentFingerPrint >>> 0) === options.parentFingerPrint); + assert((options.childIndex >>> 0) === options.childIndex); assert(Buffer.isBuffer(options.chainCode)); assert(Buffer.isBuffer(options.privateKey)); @@ -250,9 +249,9 @@ HDPrivateKey.prototype.getID = function getID(index) { */ HDPrivateKey.prototype.deriveAccount = function deriveAccount(purpose, type, account) { - assert(util.isU32(purpose), 'Purpose must be a number.'); - assert(util.isU32(type), 'Account index must be a number.'); - assert(util.isU32(account), 'Account index must be a number.'); + assert((purpose >>> 0) === purpose, 'Purpose must be a number.'); + assert((type >>> 0) === type, 'Account index must be a number.'); + assert((account >>> 0) === account, 'Account index must be a number.'); assert(this.isMaster(), 'Cannot derive account index.'); return this .derive(purpose, true) diff --git a/lib/hd/public.js b/lib/hd/public.js index f0b1fc54..13cf43ee 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -7,7 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const cleanse = require('bcrypto/lib/cleanse'); const secp256k1 = require('bcrypto/lib/secp256k1'); @@ -60,9 +59,9 @@ function HDPublicKey(options) { HDPublicKey.prototype.fromOptions = function fromOptions(options) { assert(options, 'No options for HDPublicKey'); - assert(util.isU8(options.depth)); - assert(util.isU32(options.parentFingerPrint)); - assert(util.isU32(options.childIndex)); + assert((options.depth & 0xff) === options.depth); + assert((options.parentFingerPrint >>> 0) === options.parentFingerPrint); + assert((options.childIndex >>> 0) === options.childIndex); assert(Buffer.isBuffer(options.chainCode)); assert(Buffer.isBuffer(options.publicKey)); @@ -211,9 +210,9 @@ HDPublicKey.prototype.getID = function getID(index) { */ HDPublicKey.prototype.deriveAccount = function deriveAccount(purpose, type, account) { - assert(util.isU32(purpose)); - assert(util.isU32(type)); - assert(util.isU32(account)); + assert((purpose >>> 0) === purpose); + assert((type >>> 0) === type); + assert((account >>> 0) === account); assert(this.isAccount(account), 'Cannot derive account index.'); return this; }; diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 77afc4cc..b1045625 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -121,11 +121,11 @@ class RPC extends RPCBase { case 'RPCError': return err.code; case 'ValidationError': - return errors.TYPE_ERROR; + return errs.TYPE_ERROR; case 'EncodingError': - return errors.DESERIALIZATION_ERROR; + return errs.DESERIALIZATION_ERROR; default: - return errors.INTERNAL_ERROR; + return errs.INTERNAL_ERROR; } } @@ -307,7 +307,7 @@ class RPC extends RPCBase { version: pkg.version, subversion: this.pool.options.agent, protocolversion: this.pool.options.version, - localservices: util.hex32(this.pool.options.services), + localservices: hex32(this.pool.options.services), localrelay: !this.pool.options.noRelay, timeoffset: this.network.time.offset, networkactive: this.pool.connected, @@ -446,7 +446,7 @@ class RPC extends RPCBase { return { totalbytesrecv: recv, totalbytessent: sent, - timemillis: util.ms() + timemillis: Date.now() }; } @@ -474,13 +474,13 @@ class RPC extends RPCBase { addrlocal: !peer.local.isNull() ? peer.local.hostname : undefined, - services: util.hex32(peer.services), + services: hex32(peer.services), relaytxes: !peer.noRelay, lastsend: peer.lastSend / 1000 | 0, lastrecv: peer.lastRecv / 1000 | 0, bytessent: peer.socket.bytesWritten, bytesrecv: peer.socket.bytesRead, - conntime: peer.time !== 0 ? (util.ms() - peer.time) / 1000 | 0 : 0, + conntime: peer.time !== 0 ? (Date.now() - peer.time) / 1000 | 0 : 0, timeoffset: offset, pingtime: peer.lastPong !== -1 ? (peer.lastPong - peer.lastPing) / 1000 @@ -626,8 +626,10 @@ class RPC extends RPCBase { if (this.chain.options.spv) throw new RPCError(errs.MISC_ERROR, 'Block not available (spv mode)'); - if (this.chain.options.prune) - throw new RPCError(errs.MISC_ERROR, 'Block not available (pruned data)'); + if (this.chain.options.prune) { + throw new RPCError(errs.MISC_ERROR, + 'Block not available (pruned data)'); + } throw new RPCError(errs.MISC_ERROR, 'Can\'t read block from disk'); } @@ -663,8 +665,10 @@ class RPC extends RPCBase { if (this.chain.options.spv) throw new RPCError(errs.MISC_ERROR, 'Block not available (spv mode)'); - if (this.chain.options.prune) - throw new RPCError(errs.MISC_ERROR, 'Block not available (pruned data)'); + if (this.chain.options.prune) { + throw new RPCError(errs.MISC_ERROR, + 'Block not available (pruned data)'); + } throw new RPCError(errs.DATABASE_ERROR, 'Can\'t read block from disk'); } @@ -798,8 +802,10 @@ class RPC extends RPCBase { } async getMempoolDescendants(args, help) { - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getmempooldescendants txid (verbose)'); + 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.rhash(0); @@ -876,8 +882,10 @@ class RPC extends RPCBase { } async getTXOut(args, help) { - if (help || args.length < 2 || args.length > 3) - throw new RPCError(errs.MISC_ERROR, 'gettxout "txid" n ( includemempool )'); + 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.rhash(0); @@ -1018,8 +1026,10 @@ class RPC extends RPCBase { if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'gettxoutsetinfo'); - if (this.chain.options.spv) - throw new RPCError(errs.MISC_ERROR, 'Chainstate not available (SPV mode).'); + if (this.chain.options.spv) { + throw new RPCError(errs.MISC_ERROR, + 'Chainstate not available (SPV mode).'); + } return { height: this.chain.height, @@ -1053,8 +1063,10 @@ class RPC extends RPCBase { } async verifyChain(args, help) { - if (help || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'verifychain ( checklevel numblocks )'); + if (help || args.length > 2) { + throw new RPCError(errs.MISC_ERROR, + 'verifychain ( checklevel numblocks )'); + } const valid = new Validator(args); const level = valid.u32(0); @@ -1451,7 +1463,7 @@ class RPC extends RPCBase { height: attempt.height, previousblockhash: util.revHex(attempt.prevBlock), target: util.revHex(attempt.target.toString('hex')), - bits: util.hex32(attempt.bits), + bits: hex32(attempt.bits), noncerange: '00000000ffffffff', curtime: attempt.time, mintime: attempt.mtp + 1, @@ -1460,7 +1472,7 @@ class RPC extends RPCBase { sigoplimit: consensus.MAX_BLOCK_SIGOPS_COST / scale | 0, sizelimit: consensus.MAX_BLOCK_SIZE, weightlimit: undefined, - longpollid: this.chain.tip.rhash() + util.pad32(this.totalTX()), + longpollid: this.chain.tip.rhash() + hex32(this.totalTX()), submitold: false, coinbaseaux: { flags: attempt.coinbaseFlags.toString('hex') @@ -1817,8 +1829,10 @@ class RPC extends RPCBase { } async getRawTransaction(args, help) { - if (help || args.length < 1 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'getrawtransaction "txid" ( verbose )'); + 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.rhash(0); @@ -1985,8 +1999,10 @@ class RPC extends RPCBase { */ async createMultisig(args, help) { - if (help || args.length < 2 || args.length > 2) - throw new RPCError(errs.MISC_ERROR, 'createmultisig nrequired ["key",...]'); + 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, []); @@ -2012,8 +2028,10 @@ class RPC extends RPCBase { const script = Script.fromMultisig(m, n, keys); - if (script.getSize() > consensus.MAX_SCRIPT_PUSH) - throw new RPCError(errs.VERIFY_ERROR, 'Redeem script exceeds size limit.'); + if (script.getSize() > consensus.MAX_SCRIPT_PUSH) { + throw new RPCError(errs.VERIFY_ERROR, + 'Redeem script exceeds size limit.'); + } const addr = script.getAddress(); @@ -2239,7 +2257,7 @@ class RPC extends RPCBase { if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'getmemoryinfo'); - return util.memoryUsage(); + return this.logger.memoryUsage(); } async setLogLevel(args, help) { @@ -2259,13 +2277,13 @@ class RPC extends RPCBase { */ async handleLongpoll(lpid) { - if (lpid.length !== 74) + if (lpid.length !== 72) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid longpoll ID.'); const watched = lpid.slice(0, 64); - const lastTX = parseInt(lpid.slice(64, 74), 10); + const lastTX = parseInt(lpid.slice(64, 72), 16); - if (!util.isHex(watched) || !util.isU32(lastTX)) + if ((lastTX >>> 0) !== lastTX) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid longpoll ID.'); const hash = util.revHex(watched); @@ -2680,7 +2698,7 @@ class RPC extends RPCBase { confirmations: this.chain.height - entry.height + 1, height: entry.height, version: entry.version, - versionHex: util.hex32(entry.version), + versionHex: hex32(entry.version), merkleroot: util.revHex(entry.merkleRoot), time: entry.time, mediantime: mtp, @@ -2716,7 +2734,7 @@ class RPC extends RPCBase { weight: block.getWeight(), height: entry.height, version: entry.version, - versionHex: util.hex32(entry.version), + versionHex: hex32(entry.version), merkleroot: util.revHex(entry.merkleRoot), coinbase: block.txs[0].inputs[0].script.toJSON(), tx: txs, @@ -2825,6 +2843,19 @@ function toDifficulty(bits) { return diff; } +function hex32(num) { + assert(num >= 0); + + num = num.toString(16); + + assert(num.length <= 8); + + while (num.length < 8) + num = '0' + num; + + return num; +} + /* * Expose */ diff --git a/lib/http/server.js b/lib/http/server.js index 214d1024..ae1b20c0 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -140,7 +140,7 @@ class HTTPServer extends Server { adjusted: this.network.now(), offset: this.network.time.offset }, - memory: util.memoryUsage() + memory: this.logger.memoryUsage() }); }); @@ -715,8 +715,6 @@ class HTTPOptions { 'API key must be a string.'); assert(options.apiKey.length <= 255, 'API key must be under 256 bytes.'); - assert(util.isAscii(options.apiKey), - 'API key must be ascii.'); this.apiKey = options.apiKey; this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'ascii')); } @@ -739,7 +737,8 @@ class HTTPOptions { } if (options.port != null) { - assert(util.isU16(options.port), 'Port must be a number.'); + assert((options.port & 0xffff) === options.port, + 'Port must be a number.'); this.port = options.port; } diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index 2519f4ce..c22c739d 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -10,6 +10,7 @@ const assert = require('assert'); const util = require('../utils/util'); +const binary = require('../utils/binary'); const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const BufferReader = require('../utils/reader'); @@ -691,7 +692,7 @@ PolicyEstimator.prototype.estimateFee = function estimateFee(target, smart) { if (smart == null) smart = true; - assert(util.isU32(target), 'Target must be a number.'); + assert((target >>> 0) === target, 'Target must be a number.'); assert(target <= this.feeStats.maxConfirms, 'Too many confirmations for estimate.'); @@ -735,7 +736,7 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s if (smart == null) smart = true; - assert(util.isU32(target), 'Target must be a number.'); + assert((target >>> 0) === target, 'Target must be a number.'); assert(target <= this.priStats.maxConfirms, 'Too many confirmations for estimate.'); @@ -856,12 +857,12 @@ function DoubleMap() { } DoubleMap.prototype.insert = function insert(key, value) { - const i = util.binarySearch(this.buckets, key, compare, true); + const i = binary.search(this.buckets, key, compare, true); this.buckets.splice(i, 0, [key, value]); }; DoubleMap.prototype.search = function search(key) { - const i = util.binarySearch(this.buckets, key, compare, true); + const i = binary.search(this.buckets, key, compare, true); assert(this.buckets.length !== 0, 'Cannot search.'); return this.buckets[i][1]; }; diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 34b62667..28479c45 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -2035,7 +2035,7 @@ MempoolOptions.prototype.fromOptions = function fromOptions(options) { } if (options.limitFreeRelay != null) { - assert(util.isU32(options.limitFreeRelay)); + assert((options.limitFreeRelay >>> 0) === options.limitFreeRelay); this.limitFreeRelay = options.limitFreeRelay; } @@ -2070,27 +2070,27 @@ MempoolOptions.prototype.fromOptions = function fromOptions(options) { } if (options.maxSize != null) { - assert(util.isU64(options.maxSize)); + assert((options.maxSize >>> 0) === options.maxSize); this.maxSize = options.maxSize; } if (options.maxOrphans != null) { - assert(util.isU32(options.maxOrphans)); + assert((options.maxOrphans >>> 0) === options.maxOrphans); this.maxOrphans = options.maxOrphans; } if (options.maxAncestors != null) { - assert(util.isU32(options.maxAncestors)); + assert((options.maxAncestors >>> 0) === options.maxAncestors); this.maxAncestors = options.maxAncestors; } if (options.expiryTime != null) { - assert(util.isU32(options.expiryTime)); + assert((options.expiryTime >>> 0) === options.expiryTime); this.expiryTime = options.expiryTime; } if (options.minRelay != null) { - assert(util.isU64(options.minRelay)); + assert((options.minRelay >>> 0) === options.minRelay); this.minRelay = options.minRelay; } @@ -2111,12 +2111,13 @@ MempoolOptions.prototype.fromOptions = function fromOptions(options) { } if (options.maxFiles != null) { - assert(util.isU32(options.maxFiles)); + assert((options.maxFiles >>> 0) === options.maxFiles); this.maxFiles = options.maxFiles; } if (options.cacheSize != null) { - assert(util.isU64(options.cacheSize)); + assert(Number.isSafeInteger(options.cacheSize)); + assert(options.cacheSize >= 0); this.cacheSize = options.cacheSize; } diff --git a/lib/mining/miner.js b/lib/mining/miner.js index 1a050465..4a93c948 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Heap = require('../utils/heap'); const AsyncObject = require('../utils/asyncobject'); const Amount = require('../btc/amount'); @@ -429,7 +428,7 @@ MinerOptions.prototype.fromOptions = function fromOptions(options) { } if (options.version != null) { - assert(util.isInt(options.version)); + assert((options.version >>> 0) === options.version); this.version = options.version; } @@ -463,41 +462,41 @@ MinerOptions.prototype.fromOptions = function fromOptions(options) { } if (options.minWeight != null) { - assert(util.isU32(options.minWeight)); + assert((options.minWeight >>> 0) === options.minWeight); this.minWeight = options.minWeight; } if (options.maxWeight != null) { - assert(util.isU32(options.maxWeight)); + assert((options.maxWeight >>> 0) === options.maxWeight); assert(options.maxWeight <= consensus.MAX_BLOCK_WEIGHT, 'Max weight must be below MAX_BLOCK_WEIGHT'); this.maxWeight = options.maxWeight; } if (options.maxSigops != null) { - assert(util.isU32(options.maxSigops)); + assert((options.maxSigops >>> 0) === options.maxSigops); assert(options.maxSigops <= consensus.MAX_BLOCK_SIGOPS_COST, 'Max sigops must be below MAX_BLOCK_SIGOPS_COST'); this.maxSigops = options.maxSigops; } if (options.priorityWeight != null) { - assert(util.isU32(options.priorityWeight)); + assert((options.priorityWeight >>> 0) === options.priorityWeight); this.priorityWeight = options.priorityWeight; } if (options.priorityThreshold != null) { - assert(util.isU32(options.priorityThreshold)); + assert((options.priorityThreshold >>> 0) === options.priorityThreshold); this.priorityThreshold = options.priorityThreshold; } if (options.reservedWeight != null) { - assert(util.isU32(options.reservedWeight)); + assert((options.reservedWeight >>> 0) === options.reservedWeight); this.reservedWeight = options.reservedWeight; } if (options.reservedSigops != null) { - assert(util.isU32(options.reservedSigops)); + assert((options.reservedSigops >>> 0) === options.reservedSigops); this.reservedSigops = options.reservedSigops; } diff --git a/lib/mining/template.js b/lib/mining/template.js index e1f367c7..0a642525 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -246,7 +246,9 @@ BlockTemplate.prototype.createCoinbase = function createCoinbase(hash) { input.script.pushData(encoding.ZERO_HASH160); // Smaller nonce for good measure. - input.script.pushData(util.nonce(4)); + const nonce = Buffer.allocUnsafe(4); + nonce.writeUInt32LE(Math.random() * 0x100000000, 0, true); + input.script.pushData(nonce); // Extra nonce: incremented when // the nonce overflows. diff --git a/lib/net/bip151.js b/lib/net/bip151.js index 13d85c44..57225e34 100644 --- a/lib/net/bip151.js +++ b/lib/net/bip151.js @@ -14,6 +14,7 @@ const assert = require('assert'); const EventEmitter = require('events'); +const {format} = require('util'); const util = require('../utils/util'); const co = require('../utils/co'); const hash256 = require('bcrypto/lib/hash256'); @@ -350,7 +351,7 @@ BIP151.MAX_MESSAGE = 12 * 1000 * 1000; */ BIP151.prototype.error = function error() { - const msg = util.fmt.apply(util, arguments); + const msg = format.apply(null, arguments); this.emit('error', new Error(msg)); }; @@ -700,7 +701,7 @@ BIP151.prototype.parse = function parse(data) { // Note that 6 is the minimum size: // varint-cmdlen(1) str-cmd(1) u32-size(4) payload(0) if (size < 6 || size > BIP151.MAX_MESSAGE) { - this.error('Bad packet size: %d.', util.mb(size)); + this.error('Bad packet size: %d.', size); return; } diff --git a/lib/net/bip152.js b/lib/net/bip152.js index c3c0b0bd..ecdfb528 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -11,7 +11,6 @@ */ const assert = require('assert'); -const util = require('../utils/util'); const BufferReader = require('../utils/reader'); const StaticWriter = require('../utils/staticwriter'); const encoding = require('../utils/encoding'); @@ -22,6 +21,7 @@ const AbstractBlock = require('../primitives/abstractblock'); const TX = require('../primitives/tx'); const Headers = require('../primitives/headers'); const Block = require('../primitives/block'); +const common = require('./common'); /** * Represents a compact block (bip152): `cmpctblock` packet. @@ -494,7 +494,7 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) { this._hhash = block._hhash; if (!nonce) - nonce = util.nonce(); + nonce = common.nonce(); this.keyNonce = nonce; this.sipKey = this.getKey(); diff --git a/lib/net/common.js b/lib/net/common.js index fda0005e..0a09ba9c 100644 --- a/lib/net/common.js +++ b/lib/net/common.js @@ -159,3 +159,15 @@ exports.BAN_TIME = 24 * 60 * 60; */ exports.BAN_SCORE = 100; + +/** + * Create a nonce. + * @returns {Buffer} + */ + +exports.nonce = function nonce() { + const data = Buffer.allocUnsafe(8); + data.writeUInt32LE(Math.random() * 0x100000000, true, 0); + data.writeUInt32LE(Math.random() * 0x100000000, true, 4); + return data; +}; diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index 3f0b8d50..1029fbff 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -409,7 +409,7 @@ HostList.prototype.getHost = function getHost() { buckets = this.fresh; if (this.totalUsed > 0) { - if (this.totalFresh === 0 || util.random(0, 2) === 0) + if (this.totalFresh === 0 || random(2) === 0) buckets = this.used; } @@ -420,13 +420,13 @@ HostList.prototype.getHost = function getHost() { let factor = 1; for (;;) { - let index = util.random(0, buckets.length); + let index = random(buckets.length); const bucket = buckets[index]; if (bucket.size === 0) continue; - index = util.random(0, bucket.size); + index = random(bucket.size); let entry; if (buckets === this.used) { @@ -441,7 +441,7 @@ HostList.prototype.getHost = function getHost() { } } - const num = util.random(0, 1 << 30); + const num = random(1 << 30); if (num < factor * entry.chance(now) * (1 << 30)) return entry; @@ -541,7 +541,7 @@ HostList.prototype.add = function add(addr, src) { for (let i = 0; i < entry.refCount; i++) factor *= 2; - if (util.random(0, factor) !== 0) + if (random(factor) !== 0) return false; } else { if (this.isFull()) @@ -1374,12 +1374,13 @@ HostEntry.prototype.fromJSON = function fromJSON(json, network) { assert(json.services.length > 0); assert(json.services.length <= 32); const services = parseInt(json.services, 2); - assert(util.isU32(services)); + assert((services >>> 0) === services); this.addr.services = services; } if (json.time != null) { - assert(util.isU64(json.time)); + assert(Number.isSafeInteger(json.time)); + assert(json.time >= 0); this.addr.time = json.time; } @@ -1389,17 +1390,19 @@ HostEntry.prototype.fromJSON = function fromJSON(json, network) { } if (json.attempts != null) { - assert(util.isU64(json.attempts)); + assert((json.attempts >>> 0) === json.attempts); this.attempts = json.attempts; } if (json.lastSuccess != null) { - assert(util.isU64(json.lastSuccess)); + assert(Number.isSafeInteger(json.lastSuccess)); + assert(json.lastSuccess >= 0); this.lastSuccess = json.lastSuccess; } if (json.lastAttempt != null) { - assert(util.isU64(json.lastAttempt)); + assert(Number.isSafeInteger(json.lastAttempt)); + assert(json.lastAttempt >= 0); this.lastAttempt = json.lastAttempt; } @@ -1594,6 +1597,10 @@ function concat32(left, right) { return data; } +function random(max) { + return Math.floor(Math.random() * max); +} + /* * Expose */ diff --git a/lib/net/packets.js b/lib/net/packets.js index efb7ffbd..4c25e3bd 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -67,8 +67,8 @@ exports.types = { AUTHPROPOSE: 30, UNKNOWN: 31, // Internal - INTERNAL: 100, - DATA: 101 + INTERNAL: 32, + DATA: 33 }; /** @@ -77,7 +77,43 @@ exports.types = { * @default */ -exports.typesByVal = util.reverse(exports.types); +exports.typesByVal = [ + 'VERSION', + 'VERACK', + 'PING', + 'PONG', + 'GETADDR', + 'ADDR', + 'INV', + 'GETDATA', + 'NOTFOUND', + 'GETBLOCKS', + 'GETHEADERS', + 'HEADERS', + 'SENDHEADERS', + 'BLOCK', + 'TX', + 'REJECT', + 'MEMPOOL', + 'FILTERLOAD', + 'FILTERADD', + 'FILTERCLEAR', + 'MERKLEBLOCK', + 'FEEFILTER', + 'SENDCMPCT', + 'CMPCTBLOCK', + 'GETBLOCKTXN', + 'BLOCKTXN', + 'ENCINIT', + 'ENCACK', + 'AUTHCHALLENGE', + 'AUTHREPLY', + 'AUTHPROPOSE', + 'UNKNOWN', + // Internal + 'INTERNAL', + 'DATA' +]; /** * Base Packet @@ -1462,9 +1498,9 @@ RejectPacket.codes = { CHECKPOINT: 0x43, // Internal codes (NOT FOR USE ON NETWORK) INTERNAL: 0x100, - HIGHFEE: 0x100, - ALREADYKNOWN: 0x101, - CONFLICT: 0x102 + HIGHFEE: 0x101, + ALREADYKNOWN: 0x102, + CONFLICT: 0x103 }; /** @@ -1472,7 +1508,21 @@ RejectPacket.codes = { * @const {RevMap} */ -RejectPacket.codesByVal = util.reverse(RejectPacket.codes); +RejectPacket.codesByVal = { + 0x01: 'MALFORMED', + 0x10: 'INVALID', + 0x11: 'OBSOLETE', + 0x12: 'DUPLICATE', + 0x40: 'NONSTANDARD', + 0x41: 'DUST', + 0x42: 'INSUFFICIENTFEE', + 0x43: 'CHECKPOINT', + // Internal codes (NOT FOR USE ON NETWORK) + 0x100: 'INTERNAL', + 0x101: 'HIGHFEE', + 0x102: 'ALREADYKNOWN', + 0x103: 'CONFLICT' +}; RejectPacket.prototype.cmd = 'reject'; RejectPacket.prototype.type = exports.types.REJECT; diff --git a/lib/net/parser.js b/lib/net/parser.js index 42cdb640..00ec9700 100644 --- a/lib/net/parser.js +++ b/lib/net/parser.js @@ -11,8 +11,8 @@ const assert = require('assert'); const EventEmitter = require('events'); +const {format} = require('util'); const Network = require('../protocol/network'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const common = require('./common'); const packets = require('./packets'); @@ -49,7 +49,7 @@ Object.setPrototypeOf(Parser.prototype, EventEmitter.prototype); */ Parser.prototype.error = function error() { - const msg = util.fmt.apply(util, arguments); + const msg = format.apply(null, arguments); this.emit('error', new Error(msg)); }; @@ -100,7 +100,7 @@ Parser.prototype.parse = function parse(data) { if (checksum !== this.header.checksum) { this.waiting = 24; this.header = null; - this.error('Invalid checksum: %s.', util.hex32(checksum)); + this.error('Invalid checksum: %s.', checksum.toString(16)); return; } @@ -130,7 +130,7 @@ Parser.prototype.parseHeader = function parseHeader(data) { const magic = data.readUInt32LE(0, true); if (magic !== this.network.magic) { - this.error('Invalid magic value: %s.', util.hex32(magic)); + this.error('Invalid magic value: %s.', magic.toString(16)); return null; } @@ -149,7 +149,7 @@ Parser.prototype.parseHeader = function parseHeader(data) { if (size > common.MAX_MESSAGE) { this.waiting = 24; - this.error('Packet length too large: %dmb.', util.mb(size)); + this.error('Packet length too large: %d.', size); return null; } diff --git a/lib/net/peer.js b/lib/net/peer.js index b9f1fd27..ee3d24c2 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -9,6 +9,7 @@ const assert = require('assert'); const EventEmitter = require('events'); +const {format} = require('util'); const util = require('../utils/util'); const co = require('../utils/co'); const Parser = require('./parser'); @@ -400,7 +401,7 @@ Peer.prototype.bind = function bind(socket) { }); this.socket.on('data', (chunk) => { - this.lastRecv = util.ms(); + this.lastRecv = Date.now(); this.feedParser(chunk); }); @@ -418,7 +419,7 @@ Peer.prototype.accept = function accept(socket) { this.address = NetAddress.fromSocket(socket, this.network); this.address.services = 0; - this.time = util.ms(); + this.time = Date.now(); this.outbound = false; this.connected = true; @@ -530,7 +531,7 @@ Peer.prototype.initConnect = function initConnect() { }; this.socket.once('connect', () => { - this.time = util.ms(); + this.time = Date.now(); this.connected = true; this.emit('connect'); @@ -981,8 +982,8 @@ Peer.prototype.sendPing = function sendPing() { return; } - this.lastPing = util.ms(); - this.challenge = util.nonce(); + this.lastPing = Date.now(); + this.challenge = common.nonce(); this.send(new packets.PingPacket(this.challenge)); }; @@ -1085,7 +1086,7 @@ Peer.prototype.write = function write(data) { if (this.destroyed) throw new Error('Peer is destroyed (write).'); - this.lastSend = util.ms(); + this.lastSend = Date.now(); if (this.socket.write(data) === false) this.needsDrain(data.length); @@ -1176,7 +1177,7 @@ Peer.prototype.needsDrain = function needsDrain(size) { if (this.drainSize >= Peer.DRAIN_MAX) { this.logger.warning( 'Peer is not reading: %dmb buffered (%s).', - util.mb(this.drainSize), + this.drainSize / (1 << 20), this.hostname()); this.error('Peer stalled (drain).'); this.destroy(); @@ -1243,7 +1244,7 @@ Peer.prototype.fulfill = function fulfill(packet) { */ Peer.prototype.maybeTimeout = function maybeTimeout() { - const now = util.ms(); + const now = Date.now(); for (const [key, entry] of this.responseMap) { if (now > entry.timeout) { @@ -1403,7 +1404,7 @@ Peer.prototype.error = function error(err) { return; if (typeof err === 'string') { - const msg = util.fmt.apply(util, arguments); + const msg = format.apply(null, arguments); err = new Error(msg); } @@ -1748,7 +1749,7 @@ Peer.prototype.handlePing = async function handlePing(packet) { Peer.prototype.handlePong = async function handlePong(packet) { const nonce = packet.nonce; - const now = util.ms(); + const now = Date.now(); if (!this.challenge) { this.logger.debug('Peer sent an unsolicited pong (%s).', this.hostname()); @@ -2398,7 +2399,7 @@ PeerOptions.hasWitness = function hasWitness() { */ PeerOptions.createNonce = function createNonce(hostname) { - return util.nonce(); + return common.nonce(); }; /** @@ -2439,7 +2440,7 @@ RequestEntry.prototype.addJob = function addJob(resolve, reject) { }; RequestEntry.prototype.setTimeout = function setTimeout(timeout) { - this.timeout = util.ms() + timeout; + this.timeout = Date.now() + timeout; }; RequestEntry.prototype.reject = function reject(err) { diff --git a/lib/net/pool.js b/lib/net/pool.js index 8dd5edae..36a18b3b 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -813,7 +813,7 @@ Pool.prototype.sendSync = async function sendSync(peer) { return false; peer.syncing = true; - peer.blockTime = util.ms(); + peer.blockTime = Date.now(); let locator; try { @@ -847,7 +847,7 @@ Pool.prototype.sendLocator = function sendLocator(locator, peer) { } peer.syncing = true; - peer.blockTime = util.ms(); + peer.blockTime = Date.now(); if (this.checkpoints) { peer.sendGetHeaders(locator, this.headerTip.hash); @@ -2136,7 +2136,7 @@ Pool.prototype._handleHeaders = async function _handleHeaders(peer, packet) { // If we received a valid header // chain, consider this a "block". - peer.blockTime = util.ms(); + peer.blockTime = Date.now(); // Request the blocks we just added. if (checkpoint) { @@ -2226,7 +2226,7 @@ Pool.prototype._addBlock = async function _addBlock(peer, block, flags) { return; } - peer.blockTime = util.ms(); + peer.blockTime = Date.now(); let entry; try { @@ -2689,7 +2689,7 @@ Pool.prototype._handleMerkleBlock = async function _handleMerkleBlock(peer, pack } peer.merkleBlock = block; - peer.merkleTime = util.ms(); + peer.merkleTime = Date.now(); peer.merkleMatches = tree.matches.length; peer.merkleMap = new Set(); }; @@ -2772,7 +2772,7 @@ Pool.prototype.handleCmpctBlock = async function handleCmpctBlock(peer, packet) peer.destroy(); return; } - peer.blockMap.set(hash, util.ms()); + peer.blockMap.set(hash, Date.now()); assert(!this.blockMap.has(hash)); this.blockMap.add(hash); } @@ -2829,7 +2829,7 @@ Pool.prototype.handleCmpctBlock = async function handleCmpctBlock(peer, packet) } } - block.now = util.ms(); + block.now = Date.now(); assert(!peer.compactBlocks.has(hash)); peer.compactBlocks.set(hash, block); @@ -3348,7 +3348,7 @@ Pool.prototype.getBlock = function getBlock(peer, hashes) { if (peer.destroyed) throw new Error('Peer is destroyed (getdata).'); - let now = util.ms(); + let now = Date.now(); const items = []; for (const hash of hashes) { @@ -3392,7 +3392,7 @@ Pool.prototype.getTX = function getTX(peer, hashes) { if (peer.destroyed) throw new Error('Peer is destroyed (getdata).'); - let now = util.ms(); + let now = Date.now(); const items = []; @@ -3729,7 +3729,7 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { } if (options.port != null) { - assert(util.isU16(options.port)); + assert((options.port & 0xffff) === options.port); this.port = options.port; this.publicPort = options.port; } @@ -3740,7 +3740,7 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { } if (options.publicPort != null) { - assert(util.isU16(options.publicPort)); + assert((options.publicPort & 0xff) === options.publicPort); this.publicPort = options.publicPort; } @@ -3901,12 +3901,12 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) { this.listen = false; if (options.services != null) { - assert(util.isU32(options.services)); + assert((options.services >>> 0) === options.services); this.services = options.services; } if (options.requiredServices != null) { - assert(util.isU32(options.requiredServices)); + assert((options.requiredServices >>> 0) === options.requiredServices); this.requiredServices = options.requiredServices; } @@ -4347,7 +4347,7 @@ function NonceList() { NonceList.prototype.alloc = function alloc(hostname) { for (;;) { - const nonce = util.nonce(); + const nonce = common.nonce(); const key = nonce.toString('hex'); if (this.map.has(key)) diff --git a/lib/net/proxysocket.js b/lib/net/proxysocket.js index 927229fa..d6d603c7 100644 --- a/lib/net/proxysocket.js +++ b/lib/net/proxysocket.js @@ -9,7 +9,6 @@ const assert = require('assert'); const EventEmitter = require('events'); const bsock = require('bsock'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const BufferWriter = require('../utils/writer'); @@ -126,7 +125,7 @@ ProxySocket.prototype.connect = function connect(port, host) { const pow = bw.render(); - util.log( + console.log( 'Solving proof of work to create socket (%d, %s) -- please wait.', port, host); @@ -136,7 +135,7 @@ ProxySocket.prototype.connect = function connect(port, host) { pow.writeUInt32LE(nonce, 0, true); } while (digest.hash256(pow).compare(this.target) > 0); - util.log('Solved proof of work: %d', nonce); + console.log('Solved proof of work: %d', nonce); } this.socket.fire('tcp connect', port, host, nonce); diff --git a/lib/net/socks.js b/lib/net/socks.js index e0282fa5..dc167fff 100644 --- a/lib/net/socks.js +++ b/lib/net/socks.js @@ -13,7 +13,7 @@ const assert = require('assert'); const EventEmitter = require('events'); const net = require('net'); -const util = require('../utils/util'); +const {format} = require('util'); const IP = require('../utils/ip'); const StaticWriter = require('../utils/staticwriter'); const BufferReader = require('../utils/reader'); @@ -55,7 +55,16 @@ SOCKS.states = { RESOLVE_DONE: 7 }; -SOCKS.statesByVal = util.reverse(SOCKS.states); +SOCKS.statesByVal = [ + 'INIT', + 'CONNECT', + 'HANDSHAKE', + 'AUTH', + 'PROXY', + 'PROXY_DONE', + 'RESOLVE', + 'RESOLVE_DONE' +]; SOCKS.errors = [ '', @@ -80,7 +89,7 @@ SOCKS.prototype.error = function error(err) { return; } - const msg = util.fmt.apply(util, arguments); + const msg = format.apply(null, arguments); this.emit('error', new Error(msg)); this.destroy(); }; diff --git a/lib/node/config.js b/lib/node/config.js index 01e32f31..26fcb881 100644 --- a/lib/node/config.js +++ b/lib/node/config.js @@ -10,7 +10,7 @@ const assert = require('assert'); const Path = require('path'); const os = require('os'); const fs = require('../utils/fs'); -const util = require('../utils/util'); +const {fromFloat} = require('../utils/fixed'); const HOME = os.homedir ? os.homedir() : '/'; /** @@ -416,7 +416,7 @@ Config.prototype.fixed = function fixed(key, exp, fallback) { return fallback; try { - return util.fromFloat(value, exp || 0); + return fromFloat(value, exp || 0); } catch (e) { throw new Error(`${fmt(key)} must be a fixed number.`); } @@ -955,7 +955,7 @@ Config.prototype.parseEnv = function parseEnv(env) { assert(typeof value === 'string'); - if (!util.startsWith(key, prefix)) + if (key.indexOf(prefix) === 0) continue; key = key.substring(prefix.length); diff --git a/lib/node/logger.js b/lib/node/logger.js index 189d5fff..39666e53 100644 --- a/lib/node/logger.js +++ b/lib/node/logger.js @@ -7,11 +7,26 @@ 'use strict'; const assert = require('assert'); +const {format, inspect} = require('util'); const fs = require('../utils/fs'); const util = require('../utils/util'); const co = require('../utils/co'); const Lock = require('../utils/lock'); +/* + * Constants + */ + +const inspectOptions = { + showHidden: false, + depth: 20, + colors: false, + customInspect: true, + showProxy: false, + maxArrayLength: Infinity, + breakLength: 60 +}; + /** * Basic stdout and file logger. * @alias module:node.Logger @@ -382,6 +397,24 @@ Logger.prototype.setLevel = function setLevel(name) { this.level = level; }; +/** + * Format log. + * @param {Array} args + * @param {Boolean} colors + * @returns {String} + */ + +Logger.prototype.fmt = function fmt(args, colors) { + if (args.length > 0) { + const [obj] = args; + if (obj && typeof obj === 'object') { + inspectOptions.colors = colors; + return inspect(obj, inspectOptions); + } + } + return format(...args); +}; + /** * Output a log to the `error` log level. * @param {String|Object|Error} err @@ -545,7 +578,7 @@ Logger.prototype.writeConsole = function writeConsole(level, module, args) { : console.log(msg, args[0]); } - msg += util.format(args, false); + msg += this.fmt(args, false); if (level === Logger.levels.ERROR) { console.error(msg); @@ -570,7 +603,7 @@ Logger.prototype.writeConsole = function writeConsole(level, module, args) { if (module) msg += `(${module}) `; - msg += util.format(args, this.colors); + msg += this.fmt(args, this.colors); msg += '\n'; return level === Logger.levels.ERROR @@ -601,7 +634,7 @@ Logger.prototype.writeStream = function writeStream(level, module, args) { if (module) msg += `(${module}) `; - msg += util.format(args, false); + msg += this.fmt(args, false); msg += '\n'; this.stream.write(msg); @@ -638,13 +671,40 @@ Logger.prototype.logError = function logError(level, module, err) { } }; +/** + * Get the current memory usage. + * @returns {Object} + */ + +Logger.prototype.memoryUsage = function memoryUsage() { + if (!process.memoryUsage) { + return { + total: 0, + jsHeap: 0, + jsHeapTotal: 0, + nativeHeap: 0, + external: 0 + }; + } + + const mem = process.memoryUsage(); + + return { + total: Math.floor(mem.rss / (1 << 20)), + jsHeap: Math.floor(mem.heapUsed / (1 << 20)), + jsHeapTotal: Math.floor(mem.heapTotal / (1 << 20)), + nativeHeap: Math.floor((mem.rss - mem.heapTotal) / (1 << 20)), + external: Math.floor(mem.external / (1 << 20)) + }; +}; + /** * Log the current memory usage. * @param {String|null} module */ Logger.prototype.memory = function memory(module) { - const mem = util.memoryUsage(); + const mem = this.memoryUsage(); this.log(Logger.levels.DEBUG, module, [ 'Memory: rss=%dmb, js-heap=%d/%dmb native-heap=%dmb', @@ -842,6 +902,15 @@ LoggerContext.prototype.logError = function logError(level, err) { this.logger.logError(level, this.module, err); }; +/** + * Get the current memory usage. + * @returns {Object} + */ + +LoggerContext.prototype.memoryUsage = function memoryUsage() { + return this.logger.memoryUsage(); +}; + /** * Log the current memory usage. */ diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index 9668ddc9..e6d23e37 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -57,12 +57,12 @@ function AbstractBlock() { AbstractBlock.prototype.parseOptions = function parseOptions(options) { assert(options, 'Block data is required.'); - assert(util.isU32(options.version)); + assert((options.version >>> 0) === options.version); assert(typeof options.prevBlock === 'string'); assert(typeof options.merkleRoot === 'string'); - assert(util.isU32(options.time)); - assert(util.isU32(options.bits)); - assert(util.isU32(options.nonce)); + assert((options.time >>> 0) === options.time); + assert((options.bits >>> 0) === options.bits); + assert((options.nonce >>> 0) === options.nonce); this.version = options.version; this.prevBlock = options.prevBlock; @@ -85,12 +85,12 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) { AbstractBlock.prototype.parseJSON = function parseJSON(json) { assert(json, 'Block data is required.'); - assert(util.isU32(json.version)); + assert((json.version >>> 0) === json.version); assert(typeof json.prevBlock === 'string'); assert(typeof json.merkleRoot === 'string'); - assert(util.isU32(json.time)); - assert(util.isU32(json.bits)); - assert(util.isU32(json.nonce)); + assert((json.time >>> 0) === json.time); + assert((json.bits >>> 0) === json.bits); + assert((json.nonce >>> 0) === json.nonce); this.version = json.version; this.prevBlock = util.revHex(json.prevBlock); diff --git a/lib/primitives/address.js b/lib/primitives/address.js index 104e9745..dc373af3 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -10,7 +10,6 @@ const assert = require('assert'); const Network = require('../protocol/network'); const encoding = require('../utils/encoding'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const BufferReader = require('../utils/reader'); const StaticWriter = require('../utils/staticwriter'); @@ -55,7 +54,13 @@ Address.types = { * @const {RevMap} */ -Address.typesByVal = util.reverse(Address.types); +Address.typesByVal = [ + null, + null, + 'PUBKEYHASH', + 'SCRIPTHASH', + 'WITNESS' +]; /** * Inject properties from options object. @@ -581,8 +586,8 @@ Address.prototype.fromHash = function fromHash(hash, type, version) { version = -1; assert(Buffer.isBuffer(hash)); - assert(util.isU8(type)); - assert(util.isI8(version)); + assert((type >>> 0) === type); + assert((version | 0) === version); assert(type >= Address.types.PUBKEYHASH && type <= Address.types.WITNESS, 'Not a valid address type.'); diff --git a/lib/primitives/block.js b/lib/primitives/block.js index e225c0da..02c0e43e 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -539,7 +539,7 @@ Block.prototype.format = function format(view, height) { size: this.getSize(), virtualSize: this.getVirtualSize(), date: util.date(this.time), - version: util.hex32(this.version), + version: this.version.toString(16), prevBlock: util.revHex(this.prevBlock), merkleRoot: util.revHex(this.merkleRoot), commitmentHash: commitmentHash diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index f77bb859..0e75cc48 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -61,13 +61,15 @@ Coin.prototype.fromOptions = function fromOptions(options) { assert(options, 'Coin data is required.'); if (options.version != null) { - assert(util.isU32(options.version), 'Version must be a uint32.'); + assert((options.version >>> 0) === options.version, + 'Version must be a uint32.'); this.version = options.version; } if (options.height != null) { if (options.height !== -1) { - assert(util.isU32(options.height), 'Height must be a uint32.'); + assert((options.height >>> 0) === options.height, + 'Height must be a uint32.'); this.height = options.height; } else { this.height = -1; @@ -75,7 +77,8 @@ Coin.prototype.fromOptions = function fromOptions(options) { } if (options.value != null) { - assert(util.isU64(options.value), 'Value must be a uint64.'); + assert(Number.isSafeInteger(options.value) && options.value >= 0, + 'Value must be a uint64.'); this.value = options.value; } @@ -94,7 +97,7 @@ Coin.prototype.fromOptions = function fromOptions(options) { } if (options.index != null) { - assert(util.isU32(options.index), 'Index must be a uint32.'); + assert((options.index >>> 0) === options.index, 'Index must be a uint32.'); this.index = options.index; } @@ -262,10 +265,11 @@ Coin.prototype.getJSON = function getJSON(network, minimal) { Coin.prototype.fromJSON = function fromJSON(json) { assert(json, 'Coin data required.'); - assert(util.isU32(json.version), 'Version must be a uint32.'); - assert(json.height === -1 || util.isU32(json.height), + assert((json.version >>> 0) === json.version, 'Version must be a uint32.'); + assert(json.height === -1 || (json.height >>> 0) === json.height, 'Height must be a uint32.'); - assert(util.isU64(json.value), 'Value must be a uint64.'); + assert(Number.isSafeInteger(json.value) && json.value >= 0, + 'Value must be a uint64.'); assert(typeof json.coinbase === 'boolean', 'Coinbase must be a boolean.'); this.version = json.version; @@ -277,7 +281,7 @@ Coin.prototype.fromJSON = function fromJSON(json) { if (json.hash != null) { assert(typeof json.hash === 'string', 'Hash must be a string.'); assert(json.hash.length === 64, 'Hash must be a string.'); - assert(util.isU32(json.index), 'Index must be a uint32.'); + assert((json.index >>> 0) === json.index, 'Index must be a uint32.'); this.hash = util.revHex(json.hash); this.index = json.index; } diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index a8f3587a..36121118 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -250,7 +250,7 @@ Headers.prototype.format = function format(view, height) { hash: this.rhash(), height: height != null ? height : -1, date: util.date(this.time), - version: util.hex32(this.version), + version: this.version.toString(16), prevBlock: util.revHex(this.prevBlock), merkleRoot: util.revHex(this.merkleRoot), time: this.time, diff --git a/lib/primitives/input.js b/lib/primitives/input.js index 1a272533..73942c6c 100644 --- a/lib/primitives/input.js +++ b/lib/primitives/input.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Network = require('../protocol/network'); const Script = require('../script/script'); const Witness = require('../script/witness'); @@ -55,7 +54,8 @@ Input.prototype.fromOptions = function fromOptions(options) { this.script.fromOptions(options.script); if (options.sequence != null) { - assert(util.isU32(options.sequence), 'Sequence must be a uint32.'); + assert((options.sequence >>> 0) === options.sequence, + 'Sequence must be a uint32.'); this.sequence = options.sequence; } @@ -333,7 +333,7 @@ Input.prototype.getJSON = function getJSON(network, coin) { Input.prototype.fromJSON = function fromJSON(json) { assert(json, 'Input data is required.'); - assert(util.isU32(json.sequence), 'Sequence must be a uint32.'); + assert((json.sequence >>> 0) === json.sequence, 'Sequence must be a uint32.'); this.prevout.fromJSON(json.prevout); this.script.fromJSON(json.script); this.witness.fromJSON(json.witness); diff --git a/lib/primitives/invitem.js b/lib/primitives/invitem.js index 74d9a066..3d744c27 100644 --- a/lib/primitives/invitem.js +++ b/lib/primitives/invitem.js @@ -40,10 +40,10 @@ InvItem.types = { TX: 1, BLOCK: 2, FILTERED_BLOCK: 3, + CMPCT_BLOCK: 4, WITNESS_TX: 1 | (1 << 30), WITNESS_BLOCK: 2 | (1 << 30), - WITNESS_FILTERED_BLOCK: 3 | (1 << 30), - CMPCT_BLOCK: 4 + WITNESS_FILTERED_BLOCK: 3 | (1 << 30) }; /** @@ -51,7 +51,16 @@ InvItem.types = { * @const {RevMap} */ -InvItem.typesByVal = util.reverse(InvItem.types); +InvItem.typesByVal = { + 0: 'ERROR', + 1: 'TX', + 2: 'BLOCK', + 3: 'FILTERED_BLOCK', + 4: 'CMPCT_BLOCK', + [1 | (1 << 30)]: 'WITNESS_TX', + [2 | (1 << 30)]: 'WITNESS_BLOCK', + [3 | (1 << 30)]: 'WITNESS_FILTERED_BLOCK' +}; /** * Witness bit for inv types. diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 8e4d7175..87785aea 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -57,7 +57,7 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) { assert(options, 'MerkleBlock data is required.'); assert(Array.isArray(options.hashes)); assert(Buffer.isBuffer(options.flags)); - assert(util.isU32(options.totalTX)); + assert((options.totalTX >>> 0) === options.totalTX); if (options.hashes) { for (let hash of options.hashes) { @@ -74,7 +74,7 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) { } if (options.totalTX != null) { - assert(util.isU32(options.totalTX)); + assert((options.totalTX >>> 0) === options.totalTX); this.totalTX = options.totalTX; } @@ -302,7 +302,7 @@ MerkleBlock.prototype.format = function format(view, height) { hash: this.rhash(), height: height != null ? height : -1, date: util.date(this.time), - version: util.hex32(this.version), + version: this.version.toString(16), prevBlock: util.revHex(this.prevBlock), merkleRoot: util.revHex(this.merkleRoot), time: this.time, @@ -468,7 +468,7 @@ MerkleBlock.prototype.fromJSON = function fromJSON(json) { assert(json, 'MerkleBlock data is required.'); assert(Array.isArray(json.hashes)); assert(typeof json.flags === 'string'); - assert(util.isU32(json.totalTX)); + assert((json.totalTX >>> 0) === json.totalTX); this.parseJSON(json); diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index 9176a82a..a3330125 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Script = require('../script/script'); const TX = require('./tx'); const Input = require('./input'); @@ -66,7 +65,8 @@ Object.setPrototypeOf(MTX.prototype, TX.prototype); MTX.prototype.fromOptions = function fromOptions(options) { if (options.version != null) { - assert(util.isU32(options.version), 'Version must a be uint32.'); + assert((options.version >>> 0) === options.version, + 'Version must a be uint32.'); this.version = options.version; } @@ -83,13 +83,14 @@ MTX.prototype.fromOptions = function fromOptions(options) { } if (options.locktime != null) { - assert(util.isU32(options.locktime), 'Locktime must be a uint32.'); + assert((options.locktime >>> 0) === options.locktime, + 'Locktime must be a uint32.'); this.locktime = options.locktime; } if (options.changeIndex != null) { if (options.changeIndex !== -1) { - assert(util.isU32(options.changeIndex), + assert((options.changeIndex >>> 0) === options.changeIndex, 'Change index must be a uint32.'); this.changeIndex = options.changeIndex; } else { @@ -221,12 +222,10 @@ MTX.prototype.addTX = function addTX(tx, index, height) { MTX.prototype.addOutput = function addOutput(script, value) { let output; - if (value != null) { - assert(util.isU64(value), 'Value must be a uint64.'); + if (value != null) output = Output.fromScript(script, value); - } else { + else output = Output.fromOptions(script); - } this.outputs.push(output); @@ -1378,8 +1377,8 @@ MTX.prototype.sortMembers = function sortMembers() { MTX.prototype.avoidFeeSniping = function avoidFeeSniping(height) { assert(typeof height === 'number', 'Must pass in height.'); - if (util.random(0, 10) === 0) { - height -= util.random(0, 100); + if ((Math.random() * 10 | 0) === 0) { + height -= Math.random() * 100 | 0; if (height < 0) height = 0; @@ -1394,7 +1393,7 @@ MTX.prototype.avoidFeeSniping = function avoidFeeSniping(height) { */ MTX.prototype.setLocktime = function setLocktime(locktime) { - assert(util.isU32(locktime), 'Locktime must be a uint32.'); + assert((locktime >>> 0) === locktime, 'Locktime must be a uint32.'); assert(this.inputs.length > 0, 'Cannot set sequence with no inputs.'); for (const input of this.inputs) { @@ -1416,7 +1415,7 @@ MTX.prototype.setSequence = function setSequence(index, locktime, seconds) { const input = this.inputs[index]; assert(input, 'Input does not exist.'); - assert(util.isU32(locktime), 'Locktime must be a uint32.'); + assert((locktime >>> 0) === locktime, 'Locktime must be a uint32.'); this.version = 2; @@ -1619,7 +1618,7 @@ CoinSelector.prototype.fromOptions = function fromOptions(options) { if (options.subtractFee != null) { if (typeof options.subtractFee === 'number') { - assert(util.isInt(options.subtractFee)); + assert(Number.isSafeInteger(options.subtractFee)); assert(options.subtractFee >= -1); this.subtractIndex = options.subtractFee; this.subtractFee = this.subtractIndex !== -1; @@ -1630,43 +1629,44 @@ CoinSelector.prototype.fromOptions = function fromOptions(options) { } if (options.subtractIndex != null) { - assert(util.isInt(options.subtractIndex)); + assert(Number.isSafeInteger(options.subtractIndex)); assert(options.subtractIndex >= -1); this.subtractIndex = options.subtractIndex; this.subtractFee = this.subtractIndex !== -1; } if (options.height != null) { - assert(util.isInt(options.height)); + assert(Number.isSafeInteger(options.height)); assert(options.height >= -1); this.height = options.height; } if (options.confirmations != null) { - assert(util.isInt(options.confirmations)); + assert(Number.isSafeInteger(options.confirmations)); assert(options.confirmations >= -1); this.depth = options.confirmations; } if (options.depth != null) { - assert(util.isInt(options.depth)); + assert(Number.isSafeInteger(options.depth)); assert(options.depth >= -1); this.depth = options.depth; } if (options.hardFee != null) { - assert(util.isInt(options.hardFee)); + assert(Number.isSafeInteger(options.hardFee)); assert(options.hardFee >= -1); this.hardFee = options.hardFee; } if (options.rate != null) { - assert(util.isU64(options.rate)); + assert(Number.isSafeInteger(options.rate)); + assert(options.rate >= 0); this.rate = options.rate; } if (options.maxFee != null) { - assert(util.isInt(options.maxFee)); + assert(Number.isSafeInteger(options.maxFee)); assert(options.maxFee >= -1); this.maxFee = options.maxFee; } diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index 497b3c6d..182d26e0 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -439,9 +439,9 @@ NetAddress.prototype.toJSON = function toJSON() { */ NetAddress.prototype.fromJSON = function fromJSON(json) { - assert(util.isU16(json.port)); - assert(util.isU32(json.services)); - assert(util.isU32(json.time)); + assert((json.port & 0xffff) === json.port); + assert((json.services >>> 0) === json.services); + assert((json.time >>> 0) === json.time); this.raw = IP.toBuffer(json.host); this.host = json.host; this.port = json.port; diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index 10292dd5..1e10f121 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -31,7 +31,7 @@ function Outpoint(hash, index) { if (hash != null) { assert(typeof hash === 'string', 'Hash must be a string.'); - assert(util.isU32(index), 'Index must be a uint32.'); + assert((index >>> 0) === index, 'Index must be a uint32.'); this.hash = hash; this.index = index; } @@ -46,7 +46,7 @@ function Outpoint(hash, index) { Outpoint.prototype.fromOptions = function fromOptions(options) { assert(options, 'Outpoint data is required.'); assert(typeof options.hash === 'string', 'Hash must be a string.'); - assert(util.isU32(options.index), 'Index must be a uint32.'); + assert((options.index >>> 0) === options.index, 'Index must be a uint32.'); this.hash = options.hash; this.index = options.index; return this; @@ -95,7 +95,7 @@ Outpoint.prototype.equals = function equals(prevout) { Outpoint.prototype.compare = function compare(prevout) { assert(Outpoint.isOutpoint(prevout)); - const cmp = util.strcmp(this.txid(), prevout.txid()); + const cmp = strcmp(this.txid(), prevout.txid()); if (cmp !== 0) return cmp; @@ -245,7 +245,7 @@ Outpoint.fromRaw = function fromRaw(data) { Outpoint.prototype.fromJSON = function fromJSON(json) { assert(json, 'Outpoint data is required.'); assert(typeof json.hash === 'string', 'Hash must be a string.'); - assert(util.isU32(json.index), 'Index must be a uint32.'); + assert((json.index >>> 0) === json.index, 'Index must be a uint32.'); this.hash = util.revHex(json.hash); this.index = json.index; return this; @@ -337,6 +337,29 @@ Outpoint.isOutpoint = function isOutpoint(obj) { return obj instanceof Outpoint; }; +/* + * Helpers + */ + +function strcmp(a, b) { + const len = Math.min(a.length, b.length); + + for (let i = 0; i < len; i++) { + if (a[i] < b[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + + if (a.length < b.length) + return -1; + + if (a.length > b.length) + return 1; + + return 0; +} + /* * Expose */ diff --git a/lib/primitives/output.js b/lib/primitives/output.js index 4d21f035..96581555 100644 --- a/lib/primitives/output.js +++ b/lib/primitives/output.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Amount = require('../btc/amount'); const Network = require('../protocol/network'); const Address = require('../primitives/address'); @@ -48,7 +47,8 @@ Output.prototype.fromOptions = function fromOptions(options) { assert(options, 'Output data is required.'); if (options.value) { - assert(util.isU64(options.value), 'Value must be a uint64.'); + assert(Number.isSafeInteger(options.value) && options.value >= 0, + 'Value must be a uint64.'); this.value = options.value; } @@ -87,7 +87,7 @@ Output.prototype.fromScript = function fromScript(script, value) { script = Script.fromAddress(script); assert(script instanceof Script, 'Script must be a Script.'); - assert(util.isU64(value), 'Value must be a uint64.'); + assert(Number.isSafeInteger(value) && value >= 0, 'Value must be a uint64.'); this.script = script; this.value = value; @@ -278,7 +278,8 @@ Output.prototype.isDust = function isDust(rate) { Output.prototype.fromJSON = function fromJSON(json) { assert(json, 'Output data is required.'); - assert(util.isU64(json.value), 'Value must be a uint64.'); + assert(Number.isSafeInteger(json.value) && json.value >= 0, + 'Value must be a uint64.'); this.value = json.value; this.script.fromJSON(json.script); return this; diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 8732cbe3..a248dd97 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -8,10 +8,10 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); -const encoding = require('../utils/encoding'); const digest = require('bcrypto/lib/digest'); const secp256k1 = require('bcrypto/lib/secp256k1'); +const util = require('../utils/util'); +const encoding = require('../utils/encoding'); const Amount = require('../btc/amount'); const Network = require('../protocol/network'); const Script = require('../script/script'); @@ -80,7 +80,8 @@ TX.prototype.fromOptions = function fromOptions(options) { assert(options, 'TX data is required.'); if (options.version != null) { - assert(util.isU32(options.version), 'Version must be a uint32.'); + assert((options.version >>> 0) === options.version, + 'Version must be a uint32.'); this.version = options.version; } @@ -97,7 +98,8 @@ TX.prototype.fromOptions = function fromOptions(options) { } if (options.locktime != null) { - assert(util.isU32(options.locktime), 'Locktime must be a uint32.'); + assert((options.locktime >>> 0) === options.locktime, + 'Locktime must be a uint32.'); this.locktime = options.locktime; } @@ -2194,10 +2196,10 @@ TX.prototype.getJSON = function getJSON(network, view, entry, index) { TX.prototype.fromJSON = function fromJSON(json) { assert(json, 'TX data is required.'); - assert(util.isU32(json.version), 'Version must be a uint32.'); + assert((json.version >>> 0) === json.version, 'Version must be a uint32.'); assert(Array.isArray(json.inputs), 'Inputs must be an array.'); assert(Array.isArray(json.outputs), 'Outputs must be an array.'); - assert(util.isU32(json.locktime), 'Locktime must be a uint32.'); + assert((json.locktime >>> 0) === json.locktime, 'Locktime must be a uint32.'); this.version = json.version; diff --git a/lib/primitives/txmeta.js b/lib/primitives/txmeta.js index 10ccc086..b8cfea2e 100644 --- a/lib/primitives/txmeta.js +++ b/lib/primitives/txmeta.js @@ -47,12 +47,12 @@ TXMeta.prototype.fromOptions = function fromOptions(options) { } if (options.mtime != null) { - assert(util.isU32(options.mtime)); + assert((options.mtime >>> 0) === options.mtime); this.mtime = options.mtime; } if (options.height != null) { - assert(util.isInt(options.height)); + assert(Number.isSafeInteger(options.height)); this.height = options.height; } @@ -62,12 +62,12 @@ TXMeta.prototype.fromOptions = function fromOptions(options) { } if (options.time != null) { - assert(util.isU32(options.time)); + assert((options.time >>> 0) === options.time); this.time = options.time; } if (options.index != null) { - assert(util.isInt(options.index)); + assert(Number.isSafeInteger(options.index)); this.index = options.index; } @@ -174,11 +174,11 @@ TXMeta.prototype.getJSON = function getJSON(network, view, chainHeight) { TXMeta.prototype.fromJSON = function fromJSON(json) { this.tx.fromJSON(json); - assert(util.isU32(json.mtime)); - assert(util.isInt(json.height)); + assert((json.mtime >>> 0) === json.mtime); + assert(Number.isSafeInteger(json.height)); assert(!json.block || typeof json.block === 'string'); - assert(util.isU32(json.time)); - assert(util.isInt(json.index)); + assert((json.time >>> 0) === json.time); + assert(Number.isSafeInteger(json.index)); this.mtime = json.mtime; this.height = json.height; diff --git a/lib/protocol/network.js b/lib/protocol/network.js index 33c97e5a..63aa546a 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -8,7 +8,7 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); +const binary = require('../utils/binary'); const networks = require('./networks'); const consensus = require('./consensus'); const TimeData = require('./timedata'); @@ -115,7 +115,7 @@ Network.prototype._init = function _init() { */ Network.prototype.byBit = function byBit(bit) { - const index = util.binarySearch(this.deploys, bit, cmpBit); + const index = binary.search(this.deploys, bit, cmpBit); if (index === -1) return null; diff --git a/lib/protocol/timedata.js b/lib/protocol/timedata.js index 310c090a..e3572408 100644 --- a/lib/protocol/timedata.js +++ b/lib/protocol/timedata.js @@ -9,6 +9,7 @@ const EventEmitter = require('events'); const util = require('../utils/util'); +const binary = require('../utils/binary'); /** * An object which handles "adjusted time". This may not @@ -59,7 +60,7 @@ TimeData.prototype.add = function add(id, time) { this.known.set(id, sample); - util.binaryInsert(this.samples, sample, compare); + binary.insert(this.samples, sample, compare); this.emit('sample', sample, this.samples.length); @@ -126,7 +127,7 @@ TimeData.prototype.local = function local(time) { */ TimeData.prototype.ms = function ms() { - return util.ms() + this.offset * 1000; + return Date.now() + this.offset * 1000; }; /* diff --git a/lib/script/opcode.js b/lib/script/opcode.js index a46366e1..fbe693fc 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -9,7 +9,6 @@ const assert = require('assert'); const ScriptNum = require('./scriptnum'); -const util = require('../utils/util'); const common = require('./common'); const BufferReader = require('../utils/reader'); const StaticWriter = require('../utils/staticwriter'); @@ -268,7 +267,7 @@ Opcode.prototype.toSymbol = function toSymbol() { const symbol = common.opcodesByVal[this.value]; if (!symbol) - return `0x${util.hex8(this.value)}`; + return `0x${hex8(this.value)}`; return symbol; }; @@ -366,7 +365,7 @@ Opcode.prototype.toFormat = function toFormat() { // Direct push if (!symbol) { - const size = util.hex8(this.value); + const size = hex8(this.value); return `0x${size} 0x${data}`; } @@ -385,7 +384,7 @@ Opcode.prototype.toFormat = function toFormat() { return symbol; // Unknown opcodes - const value = util.hex8(this.value); + const value = hex8(this.value); return `0x${value}`; }; @@ -492,7 +491,7 @@ Opcode.fromString = function fromString(str, enc) { */ Opcode.fromSmall = function fromSmall(num) { - assert(util.isU8(num) && num >= 0 && num <= 16); + assert((num & 0xff) === num && num >= 0 && num <= 16); return Opcode.fromOp(num === 0 ? 0 : num + 0x50); }; @@ -514,7 +513,7 @@ Opcode.fromNum = function fromNum(num) { */ Opcode.fromInt = function fromInt(num) { - assert(util.isInt(num)); + assert(Number.isSafeInteger(num)); if (num === 0) return Opcode.fromOp(opcodes.OP_0); @@ -551,10 +550,10 @@ Opcode.fromSymbol = function fromSymbol(name) { assert(typeof name === 'string'); assert(name.length > 0); - if (!util.isUpperCase(name)) + if (name.charCodeAt(0) & 32) name = name.toUpperCase(); - if (!util.startsWith(name, 'OP_')) + if (!/^OP_/.test(name)) name = `OP_${name}`; const op = common.opcodes[name]; @@ -562,12 +561,12 @@ Opcode.fromSymbol = function fromSymbol(name) { if (op != null) return Opcode.fromOp(op); - assert(util.startsWith(name, 'OP_0X'), 'Unknown opcode.'); + assert(/^OP_0X/.test(name), 'Unknown opcode.'); assert(name.length === 7, 'Unknown opcode.'); const value = parseInt(name.substring(5), 16); - assert(util.isU8(value), 'Unknown opcode.'); + assert((value & 0xff) === value, 'Unknown opcode.'); return Opcode.fromOp(value); }; @@ -668,6 +667,16 @@ Opcode.isOpcode = function isOpcode(obj) { return obj instanceof Opcode; }; +/* + * Helpers + */ + +function hex8(num) { + if (num <= 0x0f) + return '0' + num.toString(16); + return num.toString(16); +} + /* * Fill Cache */ diff --git a/lib/script/program.js b/lib/script/program.js index fedee361..6ad000c5 100644 --- a/lib/script/program.js +++ b/lib/script/program.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const common = require('./common'); const scriptTypes = common.types; @@ -29,7 +28,7 @@ function Program(version, data) { if (!(this instanceof Program)) return new Program(version, data); - assert(util.isU8(version)); + assert((version & 0xff) === version); assert(version >= 0 && version <= 16); assert(Buffer.isBuffer(data)); assert(data.length >= 2 && data.length <= 40); diff --git a/lib/script/script.js b/lib/script/script.js index 6f64ddba..118b35f3 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -8,11 +8,11 @@ 'use strict'; const assert = require('assert'); -const consensus = require('../protocol/consensus'); -const policy = require('../protocol/policy'); -const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); const merkle = require('bcrypto/lib/merkle'); +const secp256k1 = require('bcrypto/lib/secp256k1'); +const consensus = require('../protocol/consensus'); +const policy = require('../protocol/policy'); const BufferWriter = require('../utils/writer'); const BufferReader = require('../utils/reader'); const StaticWriter = require('../utils/staticwriter'); @@ -23,7 +23,6 @@ const ScriptError = require('./scripterror'); const ScriptNum = require('./scriptnum'); const common = require('./common'); const encoding = require('../utils/encoding'); -const secp256k1 = require('bcrypto/lib/secp256k1'); const Address = require('../primitives/address'); const opcodes = common.opcodes; const scriptTypes = common.types; @@ -1513,7 +1512,7 @@ Script.fromPubkeyhash = function fromPubkeyhash(hash) { */ Script.prototype.fromMultisig = function fromMultisig(m, n, keys) { - assert(util.isU8(m) && util.isU8(n)); + assert((m & 0xff) === m && (n & 0xff) === n); assert(Array.isArray(keys)); assert(keys.length === n, '`n` keys are required for multisig.'); assert(m >= 1 && m <= n); @@ -1614,7 +1613,7 @@ Script.fromNulldata = function fromNulldata(flags) { */ Script.prototype.fromProgram = function fromProgram(version, data) { - assert(util.isU8(version) && version >= 0 && version <= 16); + assert((version & 0xff) === version && version >= 0 && version <= 16); assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40); this.raw = Buffer.allocUnsafe(2 + data.length); @@ -3082,10 +3081,10 @@ Script.prototype.fromString = function fromString(code) { for (const item of items) { let symbol = item; - if (!util.isUpperCase(symbol)) + if (symbol.charCodeAt(0) & 32) symbol = symbol.toUpperCase(); - if (!util.startsWith(symbol, 'OP_')) + if (!/^OP_/.test(symbol)) symbol = `OP_${symbol}`; const value = opcodes[symbol]; diff --git a/lib/script/sigcache.js b/lib/script/sigcache.js index c44c2f32..2269e596 100644 --- a/lib/script/sigcache.js +++ b/lib/script/sigcache.js @@ -7,7 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const secp256k1 = require('bcrypto/lib/secp256k1'); /** @@ -27,7 +26,7 @@ function SigCache(size) { if (size == null) size = 10000; - assert(util.isU32(size)); + assert((size >>> 0) === size); this.size = size; this.keys = []; @@ -40,7 +39,7 @@ function SigCache(size) { */ SigCache.prototype.resize = function resize(size) { - assert(util.isU32(size)); + assert((size >>> 0) === size); this.size = size; this.keys.length = 0; diff --git a/lib/script/witness.js b/lib/script/witness.js index 51adab1b..9909718d 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); const Script = require('./script'); const common = require('./common'); const encoding = require('../utils/encoding'); diff --git a/lib/utils/base58.js b/lib/utils/base58.js index f04afe75..5d38c34f 100644 --- a/lib/utils/base58.js +++ b/lib/utils/base58.js @@ -150,3 +150,14 @@ exports.decode = function decode(str) { if (native) exports.decode = native.fromBase58; + +/** + * Test whether a string is base58 (note that you + * may get a false positive on a hex string). + * @param {String?} str + * @returns {Boolean} + */ + +exports.isBase58 = function isBase58(str) { + return typeof str === 'string' && /^[1-9A-Za-z]+$/.test(str); +}; diff --git a/lib/utils/bech32.js b/lib/utils/bech32.js index bc171425..0ae8ad3a 100644 --- a/lib/utils/bech32.js +++ b/lib/utils/bech32.js @@ -312,6 +312,27 @@ function decode(str) { if (native) decode = native.fromBech32; +/** + * Test whether a string is bech32 (note that + * this doesn't guarantee address is bech32). + * @param {String?} str + * @returns {Boolean} + */ + +function isBech32(str) { + if (typeof str !== 'string') + return false; + + if (str.toUpperCase() !== str && str.toLowerCase() !== str) + return false; + + if (str.length < 8 || str.length > 90) + return false; + + // it's unlikely any network will have hrp other than a-z symbols. + return /^[a-zA-Z]{1,3}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+$/i.test(str); +} + /** * AddrResult * @constructor @@ -339,3 +360,4 @@ exports.serialize = serialize; exports.convert = convert; exports.encode = encode; exports.decode = decode; +exports.isBech32 = isBech32; diff --git a/lib/utils/binary.js b/lib/utils/binary.js new file mode 100644 index 00000000..04a8d9d6 --- /dev/null +++ b/lib/utils/binary.js @@ -0,0 +1,85 @@ +/*! + * binary.js - binary search utils for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +/** + * Perform a binary search on a sorted array. + * @param {Array} items + * @param {Object} key + * @param {Function} compare + * @param {Boolean?} insert + * @returns {Number} Index. + */ + +exports.search = function search(items, key, compare, insert) { + let start = 0; + let end = items.length - 1; + + while (start <= end) { + const pos = (start + end) >>> 1; + const cmp = compare(items[pos], key); + + if (cmp === 0) + return pos; + + if (cmp < 0) + start = pos + 1; + else + end = pos - 1; + } + + if (!insert) + return -1; + + return start; +}; + +/** + * Perform a binary insert on a sorted array. + * @param {Array} items + * @param {Object} item + * @param {Function} compare + * @returns {Number} index + */ + +exports.insert = function insert(items, item, compare, uniq) { + const i = exports.search(items, item, compare, true); + + if (uniq && i < items.length) { + if (compare(items[i], item) === 0) + return -1; + } + + if (i === 0) + items.unshift(item); + else if (i === items.length) + items.push(item); + else + items.splice(i, 0, item); + + return i; +}; + +/** + * Perform a binary removal on a sorted array. + * @param {Array} items + * @param {Object} item + * @param {Function} compare + * @returns {Boolean} + */ + +exports.remove = function remove(items, item, compare) { + const i = exports.search(items, item, compare, false); + + if (i === -1) + return false; + + items.splice(i, 1); + + return true; +}; diff --git a/lib/utils/enforce.js b/lib/utils/enforce.js deleted file mode 100644 index 34b363bc..00000000 --- a/lib/utils/enforce.js +++ /dev/null @@ -1,165 +0,0 @@ -/*! - * enforce.js - type enforcement for bcoin - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -const util = require('./util'); - -function enforce(value, name, type, func) { - if (!value) { - if (!func) - func = enforce; - - if (name && !type) - throwError(name, func); - - if (!name) - name = 'value'; - - throwError(`'${name}' must be a(n) ${type}.`, func); - } -} - -function throwError(msg, func) { - const error = new TypeError(msg); - if (Error.captureStackTrace && func) - Error.captureStackTrace(error, func); - throw error; -} - -enforce.none = function none(value, name) { - enforce(value == null, name, 'object', none); -}; - -enforce.nul = function nul(value, name) { - enforce(value === null, name, 'object', nul); -}; - -enforce.undef = function undef(value, name) { - enforce(value === undefined, name, 'object', undef); -}; - -enforce.str = function str(value, name) { - enforce(typeof value === 'string', name, 'string', str); -}; - -enforce.bool = function bool(value, name) { - enforce(typeof value === 'boolean', name, 'boolean', bool); -}; - -enforce.num = function num(value, name) { - enforce(util.isNumber(value), name, 'number', num); -}; - -enforce.obj = function obj(v, name) { - enforce(v && typeof v === 'object' && !Array.isArray(v), name, 'object', obj); -}; - -enforce.array = function array(value, name) { - enforce(Array.isArray(value), name, 'object', array); -}; - -enforce.func = function func(value, name) { - enforce(typeof value === 'function', name, 'function', func); -}; - -enforce.error = function error(value, name) { - enforce(value instanceof Error, name, 'object', error); -}; - -enforce.regexp = function regexp(value, name) { - enforce(value && typeof value.exec === 'function' , name, 'object', regexp); -}; - -enforce.buf = function buf(value, name) { - enforce(Buffer.isBuffer(value), name, 'buffer', buf); -}; - -enforce.len = function len(value, length, name) { - if ((typeof value !== 'string' && !value) || value.length !== length) { - if (!name) - name = 'value'; - throwError(`'${name}' must have a length of ${length}.`, len); - } -}; - -enforce.instance = function instance(obj, parent, name) { - if (!(obj instanceof parent)) { - if (!name) - name = 'value'; - throwError(`'${name}' must be an instance of ${parent.name}.`, instance); - } -}; - -enforce.uint = function uint(value, name) { - enforce(util.isUInt(value), name, 'uint', uint); -}; - -enforce.int = function int(value, name) { - enforce(util.isInt(value), name, 'int', int); -}; - -enforce.u8 = function u8(value, name) { - enforce(util.isU8(value), name, 'uint8', u8); -}; - -enforce.u16 = function u16(value, name) { - enforce(util.isU16(value), name, 'uint16', u16); -}; - -enforce.u32 = function u32(value, name) { - enforce(util.isU32(value), name, 'uint32', u32); -}; - -enforce.u64 = function u64(value, name) { - enforce(util.isU64(value), name, 'uint64', u64); -}; - -enforce.i8 = function i8(value, name) { - enforce(util.isI8(value), name, 'int8', i8); -}; - -enforce.i16 = function i16(value, name) { - enforce(util.isI16(value), name, 'int16', i16); -}; - -enforce.i32 = function i32(value, name) { - enforce(util.isI32(value), name, 'int32', i32); -}; - -enforce.i64 = function i64(value, name) { - enforce(util.isI64(value), name, 'int64', i64); -}; - -enforce.ufloat = function ufloat(value, name) { - enforce(util.isUfloat(value), name, 'positive float', ufloat); -}; - -enforce.float = function float(value, name) { - enforce(util.isFloat(value), name, 'float', float); -}; - -enforce.ascii = function ascii(value, name) { - enforce(util.isAscii(value), name, 'ascii string', ascii); -}; - -enforce.hex = function hex(value, name) { - enforce(util.isHex(value), name, 'hex string', hex); -}; - -enforce.hex160 = function hex160(value, name) { - enforce(util.isHex160(value), name, '160 bit hex string', hex160); -}; - -enforce.hex256 = function hex256(value, name) { - enforce(util.isHex256(value), name, '256 bit hex string', hex256); -}; - -enforce.base58 = function base58(value, name) { - enforce(util.isBase58(value), name, 'base58 string', base58); -}; - -module.exports = enforce; diff --git a/lib/utils/fixed.js b/lib/utils/fixed.js new file mode 100644 index 00000000..00e75c4e --- /dev/null +++ b/lib/utils/fixed.js @@ -0,0 +1,216 @@ +/*! + * fixed.js - fixed number parsing + * Copyright (c) 2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +const assert = require('assert'); + +/** + * Convert int to fixed number string and reduce by a + * power of ten (uses no floating point arithmetic). + * @param {Number} num + * @param {Number} exp - Number of decimal places. + * @returns {String} Fixed number string. + */ + +exports.encode = function encode(num, exp) { + assert(Number.isSafeInteger(num), 'Invalid integer value.'); + + let sign = ''; + + if (num < 0) { + num = -num; + sign = '-'; + } + + const mult = pow10(exp); + + let lo = num % mult; + let hi = (num - lo) / mult; + + lo = lo.toString(10); + hi = hi.toString(10); + + while (lo.length < exp) + lo = '0' + lo; + + lo = lo.replace(/0+$/, ''); + + assert(lo.length <= exp, 'Invalid integer value.'); + + if (lo.length === 0) + lo = '0'; + + if (exp === 0) + return `${sign}${hi}`; + + return `${sign}${hi}.${lo}`; +}; + +/** + * Parse a fixed number string and multiply by a + * power of ten (uses no floating point arithmetic). + * @param {String} str + * @param {Number} exp - Number of decimal places. + * @returns {Number} Integer. + */ + +exports.decode = function decode(str, exp) { + assert(typeof str === 'string'); + assert(str.length <= 32, 'Fixed number string too large.'); + + let sign = 1; + + if (str.length > 0 && str[0] === '-') { + str = str.substring(1); + sign = -1; + } + + let hi = str; + let lo = '0'; + + const index = str.indexOf('.'); + + if (index !== -1) { + hi = str.substring(0, index); + lo = str.substring(index + 1); + } + + hi = hi.replace(/^0+/, ''); + lo = lo.replace(/0+$/, ''); + + assert(hi.length <= 16 - exp, + 'Fixed number string exceeds 2^53-1.'); + + assert(lo.length <= exp, + 'Too many decimal places in fixed number string.'); + + if (hi.length === 0) + hi = '0'; + + while (lo.length < exp) + lo += '0'; + + if (lo.length === 0) + lo = '0'; + + assert(/^\d+$/.test(hi) && /^\d+$/.test(lo), + 'Non-numeric characters in fixed number string.'); + + hi = parseInt(hi, 10); + lo = parseInt(lo, 10); + + const mult = pow10(exp); + const maxLo = modSafe(mult); + const maxHi = divSafe(mult); + + assert(hi < maxHi || (hi === maxHi && lo <= maxLo), + 'Fixed number string exceeds 2^53-1.'); + + return sign * (hi * mult + lo); +}; + +/** + * Convert int to float and reduce by a power + * of ten (uses no floating point arithmetic). + * @param {Number} num + * @param {Number} exp - Number of decimal places. + * @returns {Number} Double float. + */ + +exports.toFloat = function toFloat(num, exp) { + return parseFloat(exports.encode(num, exp)); +}; + +/** + * Parse a double float number and multiply by a + * power of ten (uses no floating point arithmetic). + * @param {Number} num + * @param {Number} exp - Number of decimal places. + * @returns {Number} Integer. + */ + +exports.fromFloat = function fromFloat(num, exp) { + assert(typeof num === 'number' && isFinite(num)); + assert(Number.isSafeInteger(exp)); + return exports.decode(num.toFixed(exp), exp); +}; + +/* + * Helpers + */ + +function pow10(exp) { + switch (exp) { + case 0: + return 1; + case 1: + return 10; + case 2: + return 100; + case 3: + return 1000; + case 4: + return 10000; + case 5: + return 100000; + case 6: + return 1000000; + case 7: + return 10000000; + case 8: + return 100000000; + } + throw new Error('Exponent is too large.'); +} + +function modSafe(mod) { + switch (mod) { + case 1: + return 0; + case 10: + return 1; + case 100: + return 91; + case 1000: + return 991; + case 10000: + return 991; + case 100000: + return 40991; + case 1000000: + return 740991; + case 10000000: + return 4740991; + case 100000000: + return 54740991; + } + throw new Error('Exponent is too large.'); +} + +function divSafe(div) { + switch (div) { + case 1: + return 9007199254740991; + case 10: + return 900719925474099; + case 100: + return 90071992547409; + case 1000: + return 9007199254740; + case 10000: + return 900719925474; + case 100000: + return 90071992547; + case 1000000: + return 9007199254; + case 10000000: + return 900719925; + case 100000000: + return 90071992; + } + throw new Error('Exponent is too large.'); +} diff --git a/lib/utils/hashwriter.js b/lib/utils/hashwriter.js new file mode 100644 index 00000000..bf1dbec0 --- /dev/null +++ b/lib/utils/hashwriter.js @@ -0,0 +1,500 @@ +/*! + * staticwriter.js - buffer writer for bcoin + * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +const assert = require('assert'); +const encoding = require('./encoding'); + +const POOL0 = Buffer.allocUnsafe(0); +const POOL8 = Buffer.allocUnsafe(1); +const POOL16 = Buffer.allocUnsafe(2); +const POOL24 = Buffer.allocUnsafe(3); +const POOL32 = Buffer.allocUnsafe(4); +const POOL40 = Buffer.allocUnsafe(5); +const POOL48 = Buffer.allocUnsafe(6); +const POOL56 = Buffer.allocUnsafe(7); +const POOL64 = Buffer.allocUnsafe(8); +const POOL72 = Buffer.allocUnsafe(9); +const POOL256 = Buffer.allocUnsafe(32); + +const poolBySize = [ + POOL0, + POOL8, + POOL16, + POOL24, + POOL32, + POOL40, + POOL48, + POOL56, + POOL64, + POOL72 +]; + +/** + * Statically allocated buffer writer. + * @alias module:utils.HashWriter + * @constructor + * @param {Number} size + */ + +function HashWriter(Hash, ...args) { + if (!(this instanceof HashWriter)) + return new HashWriter(Hash, ...args); + + this.ctx = new Hash().init(...args); +} + +/** + * Allocate and render the final buffer. + * @returns {Buffer} Rendered buffer. + */ + +HashWriter.prototype.init = function init(...args) { + return this.ctx.init(...args); +}; + +/** + * Allocate and render the final buffer. + * @returns {Buffer} Rendered buffer. + */ + +HashWriter.prototype.update = function update(data) { + return this.ctx.update(data); +}; + +/** + * Allocate and render the final buffer. + * @returns {Buffer} Rendered buffer. + */ + +HashWriter.prototype.final = function final() { + return this.ctx.final(); +}; + +/** + * Get size of data written so far. + * @returns {Number} + */ + +HashWriter.prototype.getSize = function getSize() { + assert(false); +}; + +/** + * Seek to relative offset. + * @param {Number} offset + */ + +HashWriter.prototype.seek = function seek(offset) { + assert(false); +}; + +/** + * Destroy the buffer writer. + */ + +HashWriter.prototype.destroy = function destroy() { + assert(false); +}; + +/** + * Write uint8. + * @param {Number} value + */ + +HashWriter.prototype.writeU8 = function writeU8(value) { + POOL8.writeUInt8(value, 0, true); + this.ctx.update(POOL8); +}; + +/** + * Write uint16le. + * @param {Number} value + */ + +HashWriter.prototype.writeU16 = function writeU16(value) { + POOL16.writeUInt16LE(value, 0, true); + this.ctx.update(POOL16); +}; + +/** + * Write uint16be. + * @param {Number} value + */ + +HashWriter.prototype.writeU16BE = function writeU16BE(value) { + POOL16.writeUInt16BE(value, 0, true); + this.ctx.update(POOL16); +}; + +/** + * Write uint32le. + * @param {Number} value + */ + +HashWriter.prototype.writeU32 = function writeU32(value) { + POOL32.writeUInt32LE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write uint32be. + * @param {Number} value + */ + +HashWriter.prototype.writeU32BE = function writeU32BE(value) { + POOL32.writeUInt32BE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write uint64le. + * @param {Number} value + */ + +HashWriter.prototype.writeU64 = function writeU64(value) { + encoding.writeU64(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write uint64be. + * @param {Number} value + */ + +HashWriter.prototype.writeU64BE = function writeU64BE(value) { + encoding.writeU64BE(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write uint64le. + * @param {U64} value + */ + +HashWriter.prototype.writeU64N = function writeU64N(value) { + encoding.writeU64N(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write uint64be. + * @param {U64} value + */ + +HashWriter.prototype.writeU64BEN = function writeU64BEN(value) { + encoding.writeU64BEN(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write int8. + * @param {Number} value + */ + +HashWriter.prototype.writeI8 = function writeI8(value) { + POOL8.writeInt8(value, 0, true); + this.ctx.update(POOL8); +}; + +/** + * Write int16le. + * @param {Number} value + */ + +HashWriter.prototype.writeI16 = function writeI16(value) { + POOL16.writeInt16LE(value, 0, true); + this.ctx.update(POOL16); +}; + +/** + * Write int16be. + * @param {Number} value + */ + +HashWriter.prototype.writeI16BE = function writeI16BE(value) { + POOL16.writeInt16BE(value, 0, true); + this.ctx.update(POOL16); +}; + +/** + * Write int32le. + * @param {Number} value + */ + +HashWriter.prototype.writeI32 = function writeI32(value) { + POOL32.writeInt32LE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write int32be. + * @param {Number} value + */ + +HashWriter.prototype.writeI32BE = function writeI32BE(value) { + POOL32.writeInt32BE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write int64le. + * @param {Number} value + */ + +HashWriter.prototype.writeI64 = function writeI64(value) { + encoding.writeI64(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write int64be. + * @param {Number} value + */ + +HashWriter.prototype.writeI64BE = function writeI64BE(value) { + encoding.writeI64BE(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write int64le. + * @param {I64} value + */ + +HashWriter.prototype.writeI64N = function writeI64N(value) { + encoding.writeI64N(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write int64be. + * @param {I64} value + */ + +HashWriter.prototype.writeI64BEN = function writeI64BEN(value) { + encoding.writeI64BEN(POOL64, value, 0); + this.ctx.update(POOL64); +}; + +/** + * Write float le. + * @param {Number} value + */ + +HashWriter.prototype.writeFloat = function writeFloat(value) { + POOL32.writeFloatLE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write float be. + * @param {Number} value + */ + +HashWriter.prototype.writeFloatBE = function writeFloatBE(value) { + POOL32.writeFloatBE(value, 0, true); + this.ctx.update(POOL32); +}; + +/** + * Write double le. + * @param {Number} value + */ + +HashWriter.prototype.writeDouble = function writeDouble(value) { + POOL64.writeDoubleLE(value, 0, true); + this.ctx.update(POOL64); +}; + +/** + * Write double be. + * @param {Number} value + */ + +HashWriter.prototype.writeDoubleBE = function writeDoubleBE(value) { + POOL64.writeDoubleBE(value, 0, true); + this.ctx.update(POOL64); +}; + +/** + * Write a varint. + * @param {Number} value + */ + +HashWriter.prototype.writeVarint = function writeVarint(value) { + const size = encoding.sizeVarint(value); + const pool = poolBySize[size]; + encoding.writeVarint(pool, value, 0); + this.ctx.update(pool); +}; + +/** + * Write a varint. + * @param {U64} value + */ + +HashWriter.prototype.writeVarintN = function writeVarintN(value) { + const size = encoding.sizeVarintN(value); + const pool = poolBySize[size]; + encoding.writeVarintN(pool, value, 0); + this.ctx.update(pool); +}; + +/** + * Write a varint (type 2). + * @param {Number} value + */ + +HashWriter.prototype.writeVarint2 = function writeVarint2(value) { + const size = encoding.sizeVarint2(value); + const pool = poolBySize[size]; + encoding.writeVarint2(pool, value, 0); + this.ctx.update(pool); +}; + +/** + * Write a varint (type 2). + * @param {U64} value + */ + +HashWriter.prototype.writeVarint2N = function writeVarint2N(value) { + const size = encoding.sizeVarint2N(value); + const pool = poolBySize[size]; + encoding.writeVarint2N(pool, value, 0); + this.ctx.update(pool); +}; + +/** + * Write bytes. + * @param {Buffer} value + */ + +HashWriter.prototype.writeBytes = function writeBytes(value) { + this.ctx.update(value); +}; + +/** + * Write bytes with a varint length before them. + * @param {Buffer} value + */ + +HashWriter.prototype.writeVarBytes = function writeVarBytes(value) { + this.writeVarint(value.length); + this.writeBytes(value); +}; + +/** + * Copy bytes. + * @param {Buffer} value + * @param {Number} start + * @param {Number} end + */ + +HashWriter.prototype.copy = function copy(value, start, end) { + this.ctx.update(value.slice(start, end)); +}; + +/** + * Write string to buffer. + * @param {String} value + * @param {String?} enc - Any buffer-supported encoding. + */ + +HashWriter.prototype.writeString = function writeString(value, enc) { + if (value.length === 0) + return; + + if (typeof value === 'string') + value = Buffer.from(value, enc); + + this.ctx.update(value); +}; + +/** + * Write a 32 byte hash. + * @param {Hash} value + */ + +HashWriter.prototype.writeHash = function writeHash(value) { + if (typeof value !== 'string') { + assert(value.length === 32); + this.writeBytes(value); + return; + } + assert(value.length === 64); + POOL256.write(value, 0, 'hex'); + this.ctx.update(POOL256); +}; + +/** + * Write a string with a varint length before it. + * @param {String} + * @param {String?} enc - Any buffer-supported encoding. + */ + +HashWriter.prototype.writeVarString = function writeVarString(value, enc) { + if (value.length === 0) { + this.writeVarint(0); + return; + } + + const size = Buffer.byteLength(value, enc); + + this.writeVarint(size); + this.ctx.update(value, enc); +}; + +/** + * Write a null-terminated string. + * @param {String|Buffer} + * @param {String?} enc - Any buffer-supported encoding. + */ + +HashWriter.prototype.writeNullString = function writeNullString(value, enc) { + this.writeString(value, enc); + this.writeU8(0); +}; + +/** + * Calculate and write a checksum for the data written so far. + */ + +HashWriter.prototype.writeChecksum = function writeChecksum() { + assert(false); +}; + +/** + * Fill N bytes with value. + * @param {Number} value + * @param {Number} size + */ + +HashWriter.prototype.fill = function fill(value, size) { + assert(size >= 0); + + if (size === 0) + return; + + if (size <= 32) { + const data = POOL256.slice(0, size); + data.fill(value); + this.ctx.update(data); + return; + } + + const data = Buffer.allocUnsafe(size); + data.fill(value); + + this.ctx.update(data); +}; + +/* + * Expose + */ + +module.exports = HashWriter; diff --git a/lib/utils/index.js b/lib/utils/index.js index b246bb5e..2d7277df 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -19,6 +19,7 @@ exports.Bloom = require('./bloom'); exports.co = require('./co'); exports.encoding = require('./encoding'); exports.enforce = require('./enforce'); +exports.fixed = require('./fixed'); exports.fs = require('./fs'); exports.GCSFilter = require('./gcs'); exports.Heap = require('./heap'); diff --git a/lib/utils/util.js b/lib/utils/util.js index be2fe72e..ed9c1364 100644 --- a/lib/utils/util.js +++ b/lib/utils/util.js @@ -8,7 +8,6 @@ 'use strict'; const assert = require('assert'); -const nodeUtil = require('util'); /** * @exports utils/util @@ -16,367 +15,6 @@ const nodeUtil = require('util'); const util = exports; -/* - * Constants - */ - -const inspectOptions = { - showHidden: false, - depth: 20, - colors: false, - customInspect: true, - showProxy: false, - maxArrayLength: Infinity, - breakLength: 60 -}; - -/** - * Test whether a number is Number, - * finite, and below MAX_SAFE_INTEGER. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isNumber = function isNumber(value) { - return typeof value === 'number' - && isFinite(value) - && value >= -Number.MAX_SAFE_INTEGER - && value <= Number.MAX_SAFE_INTEGER; -}; - -/** - * Test whether an object is an int. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isInt = function isInt(value) { - return Number.isSafeInteger(value); -}; - -/** - * Test whether an object is a uint. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isUint = function isUint(value) { - return util.isInt(value) && value >= 0; -}; - -/** - * Test whether a number is a float. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isFloat = function isFloat(value) { - return typeof value === 'number' && isFinite(value); -}; - -/** - * Test whether a number is a positive float. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isUfloat = function isUfloat(value) { - return util.isFloat(value) && value >= 0; -}; - -/** - * Test whether an object is an int8. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isI8 = function isI8(value) { - return (value | 0) === value && value >= -0x80 && value <= 0x7f; -}; - -/** - * Test whether an object is an int16. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isI16 = function isI16(value) { - return (value | 0) === value && value >= -0x8000 && value <= 0x7fff; -}; - -/** - * Test whether an object is an int32. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isI32 = function isI32(value) { - return (value | 0) === value; -}; - -/** - * Test whether an object is a int53. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isI64 = function isI64(value) { - return util.isInt(value); -}; - -/** - * Test whether an object is a uint8. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isU8 = function isU8(value) { - return (value & 0xff) === value; -}; - -/** - * Test whether an object is a uint16. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isU16 = function isU16(value) { - return (value & 0xffff) === value; -}; - -/** - * Test whether an object is a uint32. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isU32 = function isU32(value) { - return (value >>> 0) === value; -}; - -/** - * Test whether an object is a uint53. - * @param {Number?} value - * @returns {Boolean} - */ - -util.isU64 = function isU64(value) { - return util.isUint(value); -}; - -/** - * Test whether a string is a plain - * ascii string (no control characters). - * @param {String} str - * @returns {Boolean} - */ - -util.isAscii = function isAscii(str) { - return typeof str === 'string' && /^[\t\n\r -~]*$/.test(str); -}; - -/** - * Test whether a string is base58 (note that you - * may get a false positive on a hex string). - * @param {String?} str - * @returns {Boolean} - */ - -util.isBase58 = function isBase58(str) { - return typeof str === 'string' && /^[1-9A-Za-z]+$/.test(str); -}; - -/** - * Test whether a string is bech32 (note that - * this doesn't guarantee address is bech32). - * @param {String?} str - * @returns {Boolean} - */ - -util.isBech32 = function isBech32(str) { - if (typeof str !== 'string') - return false; - - if (str.toUpperCase() !== str && str.toLowerCase() !== str) - return false; - - if (str.length < 8 || str.length > 90) - return false; - - // it's unlikely any network will have hrp other than a-z symbols. - return /^[a-z]{2}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+$/i.test(str); -}; - -/** - * Test whether a string is hex (length must be even). - * Note that this _could_ await a false positive on - * base58 strings. - * @param {String?} str - * @returns {Boolean} - */ - -util.isHex = function isHex(str) { - if (typeof str !== 'string') - return false; - return str.length % 2 === 0 && /^[0-9A-Fa-f]+$/.test(str); -}; - -/** - * Test whether an object is a 160 bit hash (hex string). - * @param {String?} hash - * @returns {Boolean} - */ - -util.isHex160 = function isHex160(hash) { - if (typeof hash !== 'string') - return false; - return hash.length === 40 && util.isHex(hash); -}; - -/** - * Test whether an object is a 256 bit hash (hex string). - * @param {String?} hash - * @returns {Boolean} - */ - -util.isHex256 = function isHex256(hash) { - if (typeof hash !== 'string') - return false; - return hash.length === 64 && util.isHex(hash); -}; - -/** - * Test whether the result of a positive - * addition would be below MAX_SAFE_INTEGER. - * @param {Number} value - * @returns {Boolean} - */ - -util.isSafeAddition = function isSafeAddition(a, b) { - // We only work on positive numbers. - assert(a >= 0); - assert(b >= 0); - - // Fast case. - if (a <= 0xfffffffffffff && b <= 0xfffffffffffff) - return true; - - // Do a 64 bit addition and check the top 11 bits. - let ahi = (a * (1 / 0x100000000)) | 0; - const alo = a | 0; - - let bhi = (b * (1 / 0x100000000)) | 0; - const blo = b | 0; - - // Credit to @indutny for this method. - const lo = (alo + blo) | 0; - - const s = lo >> 31; - const as = alo >> 31; - const bs = blo >> 31; - - const c = ((as & bs) | (~s & (as ^ bs))) & 1; - - let hi = (((ahi + bhi) | 0) + c) | 0; - - hi >>>= 0; - ahi >>>= 0; - bhi >>>= 0; - - // Overflow? - if (hi < ahi || hi < bhi) - return false; - - return (hi & 0xffe00000) === 0; -}; - -/** - * util.inspect() with 20 levels of depth. - * @param {Object|String} obj - * @param {Boolean?} color - * @return {String} - */ - -util.inspectify = function inspectify(obj, color) { - if (typeof obj === 'string') - return obj; - - inspectOptions.colors = color !== false; - - return nodeUtil.inspect(obj, inspectOptions); -}; - -/** - * Format a string. - * @function - * @param {...String} args - * @returns {String} - */ - -util.fmt = nodeUtil.format; - -/** - * Format a string. - * @param {Array} args - * @param {Boolean?} color - * @return {String} - */ - -util.format = function format(args, color) { - if (args.length > 0 && args[0] && typeof args[0] === 'object') { - if (color == null) - color = Boolean(process.stdout && process.stdout.isTTY); - return util.inspectify(args[0], color); - } - return util.fmt(...args); -}; - -/** - * Write a message to stdout (console in browser). - * @param {Object|String} obj - * @param {...String} args - */ - -util.log = function log(...args) { - if (!process.stdout) { - let msg; - if (args.length > 0) { - msg = typeof args[0] !== 'object' - ? util.fmt(...args) - : args[0]; - } - console.log(msg); - return; - } - - const msg = util.format(args); - - process.stdout.write(msg + '\n'); -}; - -/** - * Write a message to stderr (console in browser). - * @param {Object|String} obj - * @param {...String} args - */ - -util.error = function error(...args) { - if (!process.stderr) { - let msg; - if (args.length > 0) { - msg = typeof args[0] !== 'object' - ? util.fmt(...args) - : args[0]; - } - console.error(msg); - return; - } - - const msg = util.format(args); - - process.stderr.write(msg + '\n'); -}; - /** * Return hrtime (shim for browser). * @param {Array} time @@ -385,7 +23,7 @@ util.error = function error(...args) { util.hrtime = function hrtime(time) { if (!process.hrtime) { - const now = util.ms(); + const now = Date.now(); if (time) { const [hi, lo] = time; @@ -418,16 +56,7 @@ util.hrtime = function hrtime(time) { */ util.now = function now() { - return Math.floor(util.ms() / 1000); -}; - -/** - * Get current time in unix time (milliseconds). - * @returns {Number} - */ - -util.ms = function ms() { - return Date.now(); + return Math.floor(Date.now() / 1000); }; /** @@ -456,208 +85,6 @@ util.time = function time(date) { return new Date(date) / 1000 | 0; }; -/** - * Get random range. - * @param {Number} min - * @param {Number} max - * @returns {Number} - */ - -util.random = function random(min, max) { - return Math.floor(Math.random() * (max - min)) + min; -}; - -/** - * Create a 32 or 64 bit nonce. - * @param {Number} size - * @returns {Buffer} - */ - -util.nonce = function nonce(size) { - let n, data; - - if (!size) - size = 8; - - switch (size) { - case 8: - data = Buffer.allocUnsafe(8); - n = util.random(0, 0x100000000); - data.writeUInt32LE(n, 0, true); - n = util.random(0, 0x100000000); - data.writeUInt32LE(n, 4, true); - break; - case 4: - data = Buffer.allocUnsafe(4); - n = util.random(0, 0x100000000); - data.writeUInt32LE(n, 0, true); - break; - default: - assert(false, 'Bad nonce size.'); - break; - } - - return data; -}; - -/** - * String comparator (memcmp + length comparison). - * @param {Buffer} a - * @param {Buffer} b - * @returns {Number} -1, 1, or 0. - */ - -util.strcmp = function strcmp(a, b) { - const len = Math.min(a.length, b.length); - - for (let i = 0; i < len; i++) { - if (a[i] < b[i]) - return -1; - if (a[i] > b[i]) - return 1; - } - - if (a.length < b.length) - return -1; - - if (a.length > b.length) - return 1; - - return 0; -}; - -/** - * Convert bytes to mb. - * @param {Number} size - * @returns {Number} mb - */ - -util.mb = function mb(size) { - return Math.floor(size / 1024 / 1024); -}; - -/** - * Convert a number to a padded uint8 - * string (3 digits in decimal). - * @param {Number} num - * @returns {String} Padded number. - */ - -util.pad8 = function pad8(num) { - assert(typeof num === 'number'); - assert(num >= 0); - - num = num.toString(10); - - switch (num.length) { - case 1: - return '00' + num; - case 2: - return '0' + num; - case 3: - return num; - } - - throw new Error('Number too big.'); -}; - -/** - * Convert a number to a padded uint32 - * string (10 digits in decimal). - * @param {Number} num - * @returns {String} Padded number. - */ - -util.pad32 = function pad32(num) { - assert(typeof num === 'number'); - assert(num >= 0); - - num = num.toString(10); - - switch (num.length) { - case 1: - return '000000000' + num; - case 2: - return '00000000' + num; - case 3: - return '0000000' + num; - case 4: - return '000000' + num; - case 5: - return '00000' + num; - case 6: - return '0000' + num; - case 7: - return '000' + num; - case 8: - return '00' + num; - case 9: - return '0' + num; - case 10: - return num; - } - - throw new Error('Number too big.'); -}; - -/** - * Convert a number to a padded uint8 - * string (2 digits in hex). - * @param {Number} num - * @returns {String} Padded number. - */ - -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; - } - - throw new Error('Number too big.'); -}; - -/** - * Convert a number to a padded uint32 - * string (8 digits in hex). - * @param {Number} num - * @returns {String} Padded number. - */ - -util.hex32 = function hex32(num) { - assert(typeof num === 'number'); - assert(num >= 0); - - num = num.toString(16); - - switch (num.length) { - case 1: - return '0000000' + num; - case 2: - return '000000' + num; - case 3: - return '00000' + num; - case 4: - return '0000' + num; - case 5: - return '000' + num; - case 6: - return '00' + num; - case 7: - return '0' + num; - case 8: - return num; - } - - throw new Error('Number too big.'); -}; - /** * Reverse a hex-string (used because of * bitcoind's affinity for uint256le). @@ -677,346 +104,3 @@ util.revHex = function revHex(data) { return out; }; - -/** - * Perform a binary search on a sorted array. - * @param {Array} items - * @param {Object} key - * @param {Function} compare - * @param {Boolean?} insert - * @returns {Number} Index. - */ - -util.binarySearch = function binarySearch(items, key, compare, insert) { - let start = 0; - let end = items.length - 1; - - while (start <= end) { - const pos = (start + end) >>> 1; - const cmp = compare(items[pos], key); - - if (cmp === 0) - return pos; - - if (cmp < 0) - start = pos + 1; - else - end = pos - 1; - } - - if (!insert) - return -1; - - return start; -}; - -/** - * Perform a binary insert on a sorted array. - * @param {Array} items - * @param {Object} item - * @param {Function} compare - * @returns {Number} index - */ - -util.binaryInsert = function binaryInsert(items, item, compare, uniq) { - const i = util.binarySearch(items, item, compare, true); - - if (uniq && i < items.length) { - if (compare(items[i], item) === 0) - return -1; - } - - if (i === 0) - items.unshift(item); - else if (i === items.length) - items.push(item); - else - items.splice(i, 0, item); - - return i; -}; - -/** - * Perform a binary removal on a sorted array. - * @param {Array} items - * @param {Object} item - * @param {Function} compare - * @returns {Boolean} - */ - -util.binaryRemove = function binaryRemove(items, item, compare) { - const i = util.binarySearch(items, item, compare, false); - - if (i === -1) - return false; - - items.splice(i, 1); - - return true; -}; - -/** - * Quick test to see if a string is uppercase. - * @param {String} str - * @returns {Boolean} - */ - -util.isUpperCase = function isUpperCase(str) { - assert(typeof str === 'string'); - - if (str.length === 0) - return false; - - return (str.charCodeAt(0) & 32) === 0; -}; - -/** - * Test to see if a string starts with a prefix. - * @param {String} str - * @param {String} prefix - * @returns {Boolean} - */ - -util.startsWith = function startsWith(str, prefix) { - assert(typeof str === 'string'); - - if (!str.startsWith) - return str.indexOf(prefix) === 0; - - return str.startsWith(prefix); -}; - -/** - * Get memory usage info. - * @returns {Object} - */ - -util.memoryUsage = function memoryUsage() { - if (!process.memoryUsage) { - return { - total: 0, - jsHeap: 0, - jsHeapTotal: 0, - nativeHeap: 0, - external: 0 - }; - } - - const mem = process.memoryUsage(); - - return { - total: util.mb(mem.rss), - jsHeap: util.mb(mem.heapUsed), - jsHeapTotal: util.mb(mem.heapTotal), - nativeHeap: util.mb(mem.rss - mem.heapTotal), - external: util.mb(mem.external) - }; -}; - -/** - * Convert int to fixed number string and reduce by a - * power of ten (uses no floating point arithmetic). - * @param {Number} num - * @param {Number} exp - Number of decimal places. - * @returns {String} Fixed number string. - */ - -util.toFixed = function toFixed(num, exp) { - assert(typeof num === 'number'); - assert(Number.isSafeInteger(num), 'Invalid integer value.'); - - let sign = ''; - - if (num < 0) { - num = -num; - sign = '-'; - } - - const mult = pow10(exp); - - let lo = num % mult; - let hi = (num - lo) / mult; - - lo = lo.toString(10); - hi = hi.toString(10); - - while (lo.length < exp) - lo = '0' + lo; - - lo = lo.replace(/0+$/, ''); - - assert(lo.length <= exp, 'Invalid integer value.'); - - if (lo.length === 0) - lo = '0'; - - if (exp === 0) - return `${sign}${hi}`; - - return `${sign}${hi}.${lo}`; -}; - -/** - * Parse a fixed number string and multiply by a - * power of ten (uses no floating point arithmetic). - * @param {String} str - * @param {Number} exp - Number of decimal places. - * @returns {Number} Integer. - */ - -util.fromFixed = function fromFixed(str, exp) { - assert(typeof str === 'string'); - assert(str.length <= 32, 'Fixed number string too large.'); - - let sign = 1; - - if (str.length > 0 && str[0] === '-') { - str = str.substring(1); - sign = -1; - } - - let hi = str; - let lo = '0'; - - const index = str.indexOf('.'); - - if (index !== -1) { - hi = str.substring(0, index); - lo = str.substring(index + 1); - } - - hi = hi.replace(/^0+/, ''); - lo = lo.replace(/0+$/, ''); - - assert(hi.length <= 16 - exp, - 'Fixed number string exceeds 2^53-1.'); - - assert(lo.length <= exp, - 'Too many decimal places in fixed number string.'); - - if (hi.length === 0) - hi = '0'; - - while (lo.length < exp) - lo += '0'; - - if (lo.length === 0) - lo = '0'; - - assert(/^\d+$/.test(hi) && /^\d+$/.test(lo), - 'Non-numeric characters in fixed number string.'); - - hi = parseInt(hi, 10); - lo = parseInt(lo, 10); - - const mult = pow10(exp); - const maxLo = modSafe(mult); - const maxHi = divSafe(mult); - - assert(hi < maxHi || (hi === maxHi && lo <= maxLo), - 'Fixed number string exceeds 2^53-1.'); - - return sign * (hi * mult + lo); -}; - -/** - * Convert int to float and reduce by a power - * of ten (uses no floating point arithmetic). - * @param {Number} num - * @param {Number} exp - Number of decimal places. - * @returns {Number} Double float. - */ - -util.toFloat = function toFloat(num, exp) { - return Number(util.toFixed(num, exp)); -}; - -/** - * Parse a double float number and multiply by a - * power of ten (uses no floating point arithmetic). - * @param {Number} num - * @param {Number} exp - Number of decimal places. - * @returns {Number} Integer. - */ - -util.fromFloat = function fromFloat(num, exp) { - assert(typeof num === 'number' && isFinite(num)); - assert(Number.isSafeInteger(exp)); - return util.fromFixed(num.toFixed(exp), exp); -}; - -/* - * Helpers - */ - -function pow10(exp) { - switch (exp) { - case 0: - return 1; - case 1: - return 10; - case 2: - return 100; - case 3: - return 1000; - case 4: - return 10000; - case 5: - return 100000; - case 6: - return 1000000; - case 7: - return 10000000; - case 8: - return 100000000; - } - throw new Error('Exponent is too large.'); -} - -function modSafe(mod) { - switch (mod) { - case 1: - return 0; - case 10: - return 1; - case 100: - return 91; - case 1000: - return 991; - case 10000: - return 991; - case 100000: - return 40991; - case 1000000: - return 740991; - case 10000000: - return 4740991; - case 100000000: - return 54740991; - } - throw new Error('Exponent is too large.'); -} - -function divSafe(div) { - switch (div) { - case 1: - return 9007199254740991; - case 10: - return 900719925474099; - case 100: - return 90071992547409; - case 1000: - return 9007199254740; - case 10000: - return 900719925474; - case 100000: - return 90071992547; - case 1000000: - return 9007199254; - case 10000000: - return 900719925; - case 100000000: - return 90071992; - } - throw new Error('Exponent is too large.'); -} diff --git a/lib/utils/validator.js b/lib/utils/validator.js index a982d339..114ce7a4 100644 --- a/lib/utils/validator.js +++ b/lib/utils/validator.js @@ -7,7 +7,7 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); +const {fromFloat} = require('../utils/fixed'); /** * Validator @@ -286,7 +286,7 @@ Validator.prototype.fixed = function fixed(key, exp, fallback) { return fallback; try { - return util.fromFloat(value, exp || 0); + return fromFloat(value, exp || 0); } catch (e) { throw new ValidationError(key, 'fixed number'); } diff --git a/lib/wallet/account.js b/lib/wallet/account.js index b5dbb07b..163b4480 100644 --- a/lib/wallet/account.js +++ b/lib/wallet/account.js @@ -6,8 +6,8 @@ 'use strict'; -const util = require('../utils/util'); const assert = require('assert'); +const binary = require('../utils/binary'); const BufferReader = require('../utils/reader'); const StaticWriter = require('../utils/staticwriter'); const encoding = require('../utils/encoding'); @@ -99,10 +99,11 @@ Account.typesByVal = { Account.prototype.fromOptions = function fromOptions(options) { assert(options, 'Options are required.'); - assert(util.isU32(options.wid)); + assert((options.wid >>> 0) === options.wid); assert(common.isName(options.id), 'Bad Wallet ID.'); assert(HD.isHD(options.accountKey), 'Account key is required.'); - assert(util.isU32(options.accountIndex), 'Account index is required.'); + assert((options.accountIndex >>> 0) === options.accountIndex, + 'Account index is required.'); this.wid = options.wid; this.id = options.id; @@ -139,37 +140,37 @@ Account.prototype.fromOptions = function fromOptions(options) { } if (options.m != null) { - assert(util.isU8(options.m)); + assert((options.m & 0xff) === options.m); this.m = options.m; } if (options.n != null) { - assert(util.isU8(options.n)); + assert((options.n & 0xff) === options.n); this.n = options.n; } if (options.accountIndex != null) { - assert(util.isU32(options.accountIndex)); + assert((options.accountIndex >>> 0) === options.accountIndex); this.accountIndex = options.accountIndex; } if (options.receiveDepth != null) { - assert(util.isU32(options.receiveDepth)); + assert((options.receiveDepth >>> 0) === options.receiveDepth); this.receiveDepth = options.receiveDepth; } if (options.changeDepth != null) { - assert(util.isU32(options.changeDepth)); + assert((options.changeDepth >>> 0) === options.changeDepth); this.changeDepth = options.changeDepth; } if (options.nestedDepth != null) { - assert(util.isU32(options.nestedDepth)); + assert((options.nestedDepth >>> 0) === options.nestedDepth); this.nestedDepth = options.nestedDepth; } if (options.lookahead != null) { - assert(util.isU32(options.lookahead)); + assert((options.lookahead >>> 0) === options.lookahead); assert(options.lookahead >= 0); assert(options.lookahead <= Account.MAX_LOOKAHEAD); this.lookahead = options.lookahead; @@ -262,13 +263,13 @@ Account.prototype.pushKey = function pushKey(key) { if (key.equals(this.accountKey)) throw new Error('Cannot add own key.'); - const index = util.binaryInsert(this.keys, key, cmp, true); + const index = binary.insert(this.keys, key, cmp, true); if (index === -1) return false; if (this.keys.length > this.n - 1) { - util.binaryRemove(this.keys, key, cmp); + binary.remove(this.keys, key, cmp); throw new Error('Cannot add more keys.'); } @@ -299,7 +300,7 @@ Account.prototype.spliceKey = function spliceKey(key) { if (this.keys.length === this.n - 1) throw new Error('Cannot remove key.'); - return util.binaryRemove(this.keys, key, cmp); + return binary.remove(this.keys, key, cmp); }; /** diff --git a/lib/wallet/client.js b/lib/wallet/client.js index 6e92c3bc..83fdaf13 100644 --- a/lib/wallet/client.js +++ b/lib/wallet/client.js @@ -7,6 +7,7 @@ 'use strict'; +const assert = require('assert'); const {NodeClient} = require('bclient'); const TX = require('../primitives/tx'); const digest = require('bcrypto/lib/digest'); diff --git a/lib/wallet/http.js b/lib/wallet/http.js index b23ef630..43f8c5d3 100644 --- a/lib/wallet/http.js +++ b/lib/wallet/http.js @@ -10,7 +10,6 @@ const assert = require('assert'); const path = require('path'); const {Server} = require('bweb'); -const util = require('../utils/util'); const base58 = require('../utils/base58'); const MTX = require('../primitives/mtx'); const Outpoint = require('../primitives/outpoint'); @@ -132,12 +131,17 @@ class HTTPServer extends Server { return; } + if (!token) { + res.json(403); + return; + } + let wallet; try { wallet = await this.wdb.auth(id, token); } catch (err) { this.logger.info('Auth failure for %s: %s.', id, err.message); - res.error(403, err); + res.json(403); return; } @@ -1007,8 +1011,6 @@ class HTTPOptions { 'API key must be a string.'); assert(options.apiKey.length <= 255, 'API key must be under 255 bytes.'); - assert(util.isAscii(options.apiKey), - 'API key must be ASCII.'); this.apiKey = options.apiKey; this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'ascii')); } @@ -1036,7 +1038,8 @@ class HTTPOptions { } if (options.port != null) { - assert(util.isU16(options.port), 'Port must be a number.'); + assert((options.port & 0xffff) === options.port, + 'Port must be a number.'); this.port = options.port; } diff --git a/lib/wallet/layout-browser.js b/lib/wallet/layout-browser.js index 4615ef0e..b932df39 100644 --- a/lib/wallet/layout-browser.js +++ b/lib/wallet/layout-browser.js @@ -7,8 +7,6 @@ 'use strict'; const assert = require('assert'); -const util = require('../utils/util'); -const pad32 = util.pad32; const layouts = exports; layouts.walletdb = { @@ -23,7 +21,7 @@ layouts.walletdb = { }, P: function P(wid, hash) { assert(typeof hash === 'string'); - return 'p' + pad32(wid) + hash; + return 'p' + hex32(wid) + hash; }, Pp: function Pp(key) { assert(typeof key === 'string'); @@ -31,18 +29,18 @@ layouts.walletdb = { }, r: function r(wid, index, hash) { assert(typeof hash === 'string'); - return 'r' + pad32(wid) + pad32(index) + hash; + return 'r' + hex32(wid) + hex32(index) + hash; }, rr: function rr(key) { assert(typeof key === 'string'); return key.slice(21); }, w: function w(wid) { - return 'w' + pad32(wid); + return 'w' + hex32(wid); }, ww: function ww(key) { assert(typeof key === 'string'); - return parseInt(key.slice(1), 10); + return parseInt(key.slice(1), 16); }, l: function l(id) { assert(typeof id === 'string'); @@ -53,36 +51,36 @@ layouts.walletdb = { return key.slice(1); }, a: function a(wid, index) { - return 'a' + pad32(wid) + pad32(index); + return 'a' + hex32(wid) + hex32(index); }, i: function i(wid, name) { assert(typeof name === 'string'); - return 'i' + pad32(wid) + name; + return 'i' + hex32(wid) + name; }, ii: function ii(key) { assert(typeof key === 'string'); - return [parseInt(key.slice(1, 11), 10), key.slice(11)]; + return [parseInt(key.slice(1, 9), 16), key.slice(9)]; }, n: function n(wid, index) { - return 'n' + pad32(wid) + pad32(index); + return 'n' + hex32(wid) + hex32(index); }, R: 'R', h: function h(height) { - return 'h' + pad32(height); + return 'h' + hex32(height); }, b: function b(height) { - return 'b' + pad32(height); + return 'b' + hex32(height); }, bb: function bb(key) { assert(typeof key === 'string'); - return parseInt(key.slice(1), 10); + return parseInt(key.slice(1), 16); }, o: function o(hash, index) { assert(typeof hash === 'string'); - return 'o' + hash + pad32(index); + return 'o' + hash + hex32(index); }, oo: function oo(key) { - return [key.slice(1, 65), parseInt(key.slice(65), 10)]; + return [key.slice(1, 65), parseInt(key.slice(65), 16)]; }, T: function T(hash) { assert(typeof hash === 'string'); @@ -96,55 +94,55 @@ layouts.walletdb = { layouts.txdb = { binary: false, prefix: function prefix(wid) { - return 't' + pad32(wid); + return 't' + hex32(wid); }, R: 'R', r: function r(acct) { assert(typeof acct === 'number'); - return 'r' + pad32(acct); + return 'r' + hex32(acct); }, rr: function rr(key) { assert(typeof key === 'string'); - return parseInt(key.slice(1), 10); + return parseInt(key.slice(1), 16); }, hi: function hi(ch, hash, index) { assert(typeof hash === 'string'); - return ch + hash + pad32(index); + return ch + hash + hex32(index); }, hii: function hii(key) { assert(typeof key === 'string'); - return [key.slice(1, 65), parseInt(key.slice(65), 10)]; + return [key.slice(1, 65), parseInt(key.slice(65), 16)]; }, ih: function ih(ch, index, hash) { assert(typeof hash === 'string'); - return ch + pad32(index) + hash; + return ch + hex32(index) + hash; }, ihh: function ihh(key) { assert(typeof key === 'string'); - return [parseInt(key.slice(1, 11), 10), key.slice(11)]; + return [parseInt(key.slice(1, 9), 16), key.slice(9)]; }, iih: function iih(ch, index, num, hash) { assert(typeof hash === 'string'); - return ch + pad32(index) + pad32(num) + hash; + return ch + hex32(index) + hex32(num) + hash; }, iihh: function iihh(key) { assert(typeof key === 'string'); return [ - parseInt(key.slice(1, 11), 10), - parseInt(key.slice(11, 21), 10), - key.slice(21) + parseInt(key.slice(1, 9), 16), + parseInt(key.slice(9, 17), 16), + key.slice(17) ]; }, ihi: function ihi(ch, index, hash, num) { assert(typeof hash === 'string'); - return ch + pad32(index) + hash + pad32(num); + return ch + hex32(index) + hash + hex32(num); }, ihii: function ihii(key) { assert(typeof key === 'string'); return [ - parseInt(key.slice(1, 11), 10), - key.slice(11, 75), - parseInt(key.slice(75), 10) + parseInt(key.slice(1, 9), 16), + key.slice(9, 73), + parseInt(key.slice(73), 16) ]; }, ha: function ha(ch, hash) { @@ -234,10 +232,38 @@ layouts.txdb = { return this.ihii(key); }, b: function b(height) { - return 'b' + pad32(height); + return 'b' + hex32(height); }, bb: function bb(key) { assert(typeof key === 'string'); - return parseInt(key.slice(1), 10); + return parseInt(key.slice(1), 16); } }; + +function hex32(num) { + assert(typeof num === 'number'); + assert(num >= 0); + + num = num.toString(16); + + switch (num.length) { + case 1: + return '0000000' + num; + case 2: + return '000000' + num; + case 3: + return '00000' + num; + case 4: + return '0000' + num; + case 5: + return '000' + num; + case 6: + return '00' + num; + case 7: + return '0' + num; + case 8: + return num; + } + + throw new Error('Number too big.'); +} diff --git a/lib/wallet/masterkey.js b/lib/wallet/masterkey.js index 942ea686..654c99b8 100644 --- a/lib/wallet/masterkey.js +++ b/lib/wallet/masterkey.js @@ -134,22 +134,22 @@ MasterKey.prototype.fromOptions = function fromOptions(options) { } if (options.rounds != null) { - assert(util.isU32(options.rounds)); + assert((options.rounds >>> 0) === options.rounds); this.N = options.rounds; } if (options.N != null) { - assert(util.isU32(options.N)); + assert((options.N >>> 0) === options.N); this.N = options.N; } if (options.r != null) { - assert(util.isU32(options.r)); + assert((options.r >>> 0) === options.r); this.r = options.r; } if (options.p != null) { - assert(util.isU32(options.p)); + assert((options.p >>> 0) === options.p); this.p = options.p; } diff --git a/lib/wallet/rpc.js b/lib/wallet/rpc.js index b606ce59..8249d7ea 100644 --- a/lib/wallet/rpc.js +++ b/lib/wallet/rpc.js @@ -8,6 +8,7 @@ const assert = require('assert'); const bweb = require('bweb'); +const {format} = require('util'); const fs = require('../utils/fs'); const util = require('../utils/util'); const digest = require('bcrypto/lib/digest'); @@ -101,13 +102,13 @@ class RPC extends RPCBase { case 'RPCError': return err.code; case 'ValidationError': - return errors.TYPE_ERROR; + return errs.TYPE_ERROR; case 'EncodingError': - return errors.DESERIALIZATION_ERROR; + return errs.DESERIALIZATION_ERROR; case 'FundingError': - return errors.WALLET_INSUFFICIENT_FUNDS; + return errs.WALLET_INSUFFICIENT_FUNDS; default: - return errors.INTERNAL_ERROR; + return errs.INTERNAL_ERROR; } } @@ -312,11 +313,11 @@ class RPC extends RPCBase { const time = util.date(); const out = [ - util.fmt('# Wallet Dump created by Bcoin %s', pkg.version), - util.fmt('# * Created on %s', time), - util.fmt('# * Best block at time of backup was %d (%s).', + format('# Wallet Dump created by Bcoin %s', pkg.version), + format('# * Created on %s', time), + format('# * Best block at time of backup was %d (%s).', tip.height, util.revHex(tip.hash)), - util.fmt('# * File: %s', file), + format('# * File: %s', file), '' ]; @@ -335,7 +336,7 @@ class RPC extends RPCBase { if (ring.branch === 1) fmt = '%s %s change=1 addr=%s'; - const str = util.fmt(fmt, ring.toSecret(this.network), time, addr); + const str = format(fmt, ring.toSecret(this.network), time, addr); out.push(str); } @@ -1470,8 +1471,11 @@ class RPC extends RPCBase { if (help || (wallet.master.encrypted && args.length !== 0)) throw new RPCError(errs.MISC_ERROR, 'walletlock'); - if (!wallet.master.encrypted) - throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.'); + if (!wallet.master.encrypted) { + throw new RPCError( + errs.WALLET_WRONG_ENC_STATE, + 'Wallet is not encrypted.'); + } await wallet.lock(); @@ -1490,8 +1494,11 @@ class RPC extends RPCBase { const old = valid.str(0, ''); const passphrase = valid.str(1, ''); - if (!wallet.master.encrypted) - throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.'); + if (!wallet.master.encrypted) { + throw new RPCError( + errs.WALLET_WRONG_ENC_STATE, + 'Wallet is not encrypted.'); + } if (old.length < 1 || passphrase.length < 1) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter'); @@ -1512,8 +1519,11 @@ class RPC extends RPCBase { 'walletpassphrase "passphrase" timeout'); } - if (!wallet.master.encrypted) - throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.'); + if (!wallet.master.encrypted) { + throw new RPCError( + errs.WALLET_WRONG_ENC_STATE, + 'Wallet is not encrypted.'); + } if (passphrase.length < 1) throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter'); @@ -1604,7 +1614,7 @@ class RPC extends RPCBase { if (help || args.length !== 0) throw new RPCError(errs.MISC_ERROR, 'getmemoryinfo'); - return util.memoryUsage(); + return this.logger.memoryUsage(); } async setLogLevel(args, help) { diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 5dd5a4e2..e85216b4 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -2064,7 +2064,7 @@ TXDB.prototype.getAccountBalance = async function getAccountBalance(acct) { */ TXDB.prototype.zap = async function zap(acct, age) { - assert(util.isU32(age)); + assert((age >>> 0) === age); const now = util.now(); diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index fe6df76d..46a08f3c 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -10,7 +10,6 @@ const assert = require('assert'); const EventEmitter = require('events'); const Network = require('../protocol/network'); -const util = require('../utils/util'); const encoding = require('../utils/encoding'); const Lock = require('../utils/lock'); const digest = require('bcrypto/lib/digest'); @@ -115,7 +114,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) { this.master.fromKey(key, mnemonic, this.network); if (options.wid != null) { - assert(util.isU32(options.wid)); + assert((options.wid >>> 0) === options.wid); this.wid = options.wid; } @@ -135,7 +134,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) { } if (options.accountDepth != null) { - assert(util.isU32(options.accountDepth)); + assert((options.accountDepth >>> 0) === options.accountDepth); this.accountDepth = options.accountDepth; } @@ -146,7 +145,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) { } if (options.tokenDepth != null) { - assert(util.isU32(options.tokenDepth)); + assert((options.tokenDepth >>> 0) === options.tokenDepth); this.tokenDepth = options.tokenDepth; } @@ -1388,7 +1387,7 @@ Wallet.prototype._send = async function _send(options, passphrase) { */ Wallet.prototype.increaseFee = async function increaseFee(hash, rate, passphrase) { - assert(util.isU32(rate), 'Rate must be a number.'); + assert((rate >>> 0) === rate, 'Rate must be a number.'); const wtx = await this.getTX(hash); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 248ad472..5b67dbfb 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -430,7 +430,7 @@ WalletDB.prototype.scan = async function scan(height) { if (height == null) height = this.state.startHeight; - assert(util.isU32(height), 'WDB: Must pass in a height.'); + assert((height >>> 0) === height, 'WDB: Must pass in a height.'); await this.rollback(height); @@ -851,7 +851,7 @@ WalletDB.prototype.renameAccount = function renameAccount(b, account, name) { /** * Get a wallet with token auth first. * @param {WalletID} wid - * @param {String|Buffer} token + * @param {Buffer} token * @returns {Promise} - Returns {@link Wallet}. */ @@ -861,12 +861,6 @@ WalletDB.prototype.auth = async function auth(wid, token) { if (!wallet) return null; - if (typeof token === 'string') { - if (!util.isHex256(token)) - throw new Error('WDB: Authentication error.'); - token = Buffer.from(token, 'hex'); - } - // Compare in constant time: if (!ccmp(token, wallet.token)) throw new Error('WDB: Authentication error.'); @@ -2082,7 +2076,7 @@ WalletOptions.prototype.fromOptions = function fromOptions(options) { } if (options.feeRate != null) { - assert(util.isU64(options.feeRate)); + assert((options.feeRate >>> 0) === options.feeRate); this.feeRate = options.feeRate; } @@ -2103,12 +2097,12 @@ WalletOptions.prototype.fromOptions = function fromOptions(options) { } if (options.maxFiles != null) { - assert(util.isU32(options.maxFiles)); + assert((options.maxFiles >>> 0) === options.maxFiles); this.maxFiles = options.maxFiles; } if (options.cacheSize != null) { - assert(util.isU64(options.cacheSize)); + assert(Number.isSafeInteger(options.cacheSize) && options.cacheSize >= 0); this.cacheSize = options.cacheSize; } diff --git a/lib/workers/master.js b/lib/workers/master.js index 9cc109f6..229f2c00 100644 --- a/lib/workers/master.js +++ b/lib/workers/master.js @@ -9,7 +9,7 @@ const assert = require('assert'); const EventEmitter = require('events'); -const util = require('../utils/util'); +const {format} = require('util'); const Network = require('../protocol/network'); const jobs = require('./jobs'); const Parser = require('./parser'); @@ -132,8 +132,8 @@ Master.prototype.destroy = function destroy() { * @param {...String} args */ -Master.prototype.log = function log(...items) { - const text = util.format(items, this.color); +Master.prototype.log = function log() { + const text = format.apply(null, arguments); this.send(new packets.LogPacket(text)); }; diff --git a/lib/workers/worker.js b/lib/workers/worker.js index 197a7b1c..92fdc33f 100644 --- a/lib/workers/worker.js +++ b/lib/workers/worker.js @@ -8,10 +8,6 @@ 'use strict'; const Master = require('./master'); -const util = require('../utils/util'); const server = new Master(); -util.log = server.log.bind(server); -util.error = util.log; - server.listen(); diff --git a/lib/workers/workerpool.js b/lib/workers/workerpool.js index 05ce36d8..77655172 100644 --- a/lib/workers/workerpool.js +++ b/lib/workers/workerpool.js @@ -12,7 +12,6 @@ const assert = require('assert'); const EventEmitter = require('events'); const os = require('os'); -const util = require('../utils/util'); const co = require('../utils/co'); const Network = require('../protocol/network'); const Child = require('./child'); @@ -68,13 +67,13 @@ WorkerPool.prototype.set = function set(options) { } if (options.size != null) { - assert(util.isU32(options.size)); + assert((options.size >>> 0) === options.size); assert(options.size > 0); this.size = options.size; } if (options.timeout != null) { - assert(util.isInt(options.timeout)); + assert(Number.isSafeInteger(options.timeout)); assert(options.timeout >= -1); this.timeout = options.timeout; } diff --git a/migrate/coins-old.js b/migrate/coins-old.js index 4b814862..49ea879e 100644 --- a/migrate/coins-old.js +++ b/migrate/coins-old.js @@ -52,7 +52,7 @@ function Coins(options) { Coins.prototype.fromOptions = function fromOptions(options) { if (options.version != null) { - assert(util.isU32(options.version)); + assert((options.version >>> 0) === options.version); this.version = options.version; } @@ -62,7 +62,7 @@ Coins.prototype.fromOptions = function fromOptions(options) { } if (options.height != null) { - assert(util.isInt(options.height)); + assert(Number.isSafeInteger(options.height)); this.height = options.height; } diff --git a/migrate/coins/coins.js b/migrate/coins/coins.js index 9aaf1c9b..5e8b6c86 100644 --- a/migrate/coins/coins.js +++ b/migrate/coins/coins.js @@ -54,7 +54,7 @@ function Coins(options) { Coins.prototype.fromOptions = function fromOptions(options) { if (options.version != null) { - assert(util.isU32(options.version)); + assert((options.version >>> 0) === options.version); this.version = options.version; } @@ -64,7 +64,7 @@ Coins.prototype.fromOptions = function fromOptions(options) { } if (options.height != null) { - assert(util.isInt(options.height)); + assert(Number.isSafeInteger(options.height)); this.height = options.height; } diff --git a/scripts/fuzz.js b/scripts/fuzz.js index facfa9a8..37e5aff4 100644 --- a/scripts/fuzz.js +++ b/scripts/fuzz.js @@ -1,6 +1,5 @@ 'use strict'; -const util = require('../lib/utils/util'); const Script = require('../lib/script/script'); const Stack = require('../lib/script/stack'); const Witness = require('../lib/script/witness'); @@ -21,7 +20,7 @@ try { } if (consensus) - util.log('Running against bitcoinconsensus...'); + console.log('Running against bitcoinconsensus...'); const MANDATORY = flags.MANDATORY_VERIFY_FLAGS | flags.VERIFY_WITNESS; const STANDARD = flags.STANDARD_VERIFY_FLAGS; @@ -39,13 +38,13 @@ function assertConsensus(tx, output, flags, code) { const err = verifyConsensus(tx, 0, output, 0, flags); if (err !== code) { - util.log('bitcoinconsensus mismatch!'); - util.log(`${err} (bitcoin core) !== ${code} (bcoin)`); - util.log(tx); - util.log(output); - util.log(flags); - util.log('TX: %s', tx.toRaw().toString('hex')); - util.log('Output Script: %s', output.toRaw().toString('hex')); + console.log('bitcoinconsensus mismatch!'); + console.log(`${err} (bitcoin core) !== ${code} (bcoin)`); + console.log(tx); + console.log(output); + console.log(flags); + console.log('TX: %s', tx.toRaw().toString('hex')); + console.log('Output Script: %s', output.toRaw().toString('hex')); } } @@ -59,7 +58,7 @@ function randomKey() { const x = secp256k1.generatePrivateKey(); const y = secp256k1.generatePrivateKey(); - if (util.random(0, 2) === 0) { + if (rand(0, 2) === 0) { const p = Buffer.from([2 | (y[y.length - 1] & 1)]); return Buffer.concat([p, x]); } @@ -70,28 +69,28 @@ function randomKey() { function randomOutpoint() { const hash = random.randomBytes(32).toString('hex'); - return new Outpoint(hash, util.random(0, 0xffffffff)); + return new Outpoint(hash, rand(0, 0xffffffff)); } function randomInput() { const input = Input.fromOutpoint(randomOutpoint()); - if (util.random(0, 5) === 0) - input.sequence = util.random(0, 0xffffffff); + if (rand(0, 5) === 0) + input.sequence = rand(0, 0xffffffff); return input; } function randomOutput() { - return Output.fromScript(randomScript(), util.random(0, 1e8)); + return Output.fromScript(randomScript(), rand(0, 1e8)); } function randomTX() { const tx = new TX(); - const inputs = util.random(1, 5); - const outputs = util.random(0, 5); + const inputs = rand(1, 5); + const outputs = rand(0, 5); - tx.version = util.random(0, 0xffffffff); + tx.version = rand(0, 0xffffffff); for (let i = 0; i < inputs; i++) tx.inputs.push(randomInput()); @@ -99,8 +98,8 @@ function randomTX() { for (let i = 0; i < outputs; i++) tx.outputs.push(randomOutput()); - if (util.random(0, 5) === 0) - tx.locktime = util.random(0, 0xffffffff); + if (rand(0, 5) === 0) + tx.locktime = rand(0, 0xffffffff); tx.refresh(); @@ -108,11 +107,11 @@ function randomTX() { } function randomWitness(redeem) { - const size = util.random(1, 100); + const size = rand(1, 100); const witness = new Witness(); for (let i = 0; i < size; i++) { - const len = util.random(0, 100); + const len = rand(0, 100); witness.push(random.randomBytes(len)); } @@ -125,11 +124,11 @@ function randomWitness(redeem) { } function randomInputScript(redeem) { - const size = util.random(1, 100); + const size = rand(1, 100); const script = new Script(); for (let i = 0; i < size; i++) { - const len = util.random(0, 100); + const len = rand(0, 100); script.pushData(random.randomBytes(len)); } @@ -140,7 +139,7 @@ function randomInputScript(redeem) { } function randomOutputScript() { - const size = util.random(1, 10000); + const size = rand(1, 10000); return Script.fromRaw(random.randomBytes(size)); } @@ -165,7 +164,7 @@ function isPushOnly(script) { } function randomPubkey() { - const len = util.random(0, 2) === 0 ? 33 : 65; + const len = rand(0, 2) === 0 ? 33 : 65; return Script.fromPubkey(random.randomBytes(len)); } @@ -174,12 +173,12 @@ function randomPubkeyhash() { } function randomMultisig() { - const n = util.random(1, 16); - const m = util.random(1, n); + const n = rand(1, 16); + const m = rand(1, n); const keys = []; for (let i = 0; i < n; i++) { - const len = util.random(0, 2) === 0 ? 33 : 65; + const len = rand(0, 2) === 0 ? 33 : 65; keys.push(random.randomBytes(len)); } @@ -199,13 +198,13 @@ function randomWitnessScripthash() { } function randomProgram() { - const version = util.random(0, 16); - const size = util.random(2, 41); + const version = rand(0, 16); + const size = rand(2, 41); return Script.fromProgram(version, random.randomBytes(size)); } function randomRedeem() { - switch (util.random(0, 5)) { + switch (rand(0, 5)) { case 0: return randomPubkey(); case 1: @@ -221,7 +220,7 @@ function randomRedeem() { } function randomScript() { - switch (util.random(0, 7)) { + switch (rand(0, 7)) { case 0: return randomPubkey(); case 1: @@ -299,7 +298,7 @@ function randomWitnessNestedContext() { } function randomContext() { - switch (util.random(0, 6)) { + switch (rand(0, 6)) { case 0: return randomPubkeyContext(); case 1: @@ -322,7 +321,7 @@ function fuzzSimple(flags) { for (;;) { if (++total % 1000 === 0) - util.log('Fuzzed %d scripts.', total); + console.log('Fuzzed %d scripts.', total); if (total % 500 === 0) tx = randomTX(); @@ -357,16 +356,16 @@ function fuzzSimple(flags) { if (isPushOnly(output)) continue; - util.log('Produced valid scripts:'); + console.log('Produced valid scripts:'); - util.log('Input:'); - util.log(input); + console.log('Input:'); + console.log(input); - util.log('Output:'); - util.log(output); + console.log('Output:'); + console.log(output); - util.log('Stack:'); - util.log(stack); + console.log('Stack:'); + console.log(stack); break; } @@ -378,7 +377,7 @@ function fuzzVerify(flags) { for (;;) { if (++total % 1000 === 0) - util.log('Fuzzed %d scripts.', total); + console.log('Fuzzed %d scripts.', total); if (total % 500 === 0) tx = randomTX(); @@ -415,16 +414,16 @@ function fuzzVerify(flags) { if (isPushOnly(output)) continue; - util.log('Produced valid scripts:'); + console.log('Produced valid scripts:'); - util.log('Input:'); - util.log(input); + console.log('Input:'); + console.log(input); - util.log('Witness:'); - util.log(witness); + console.log('Witness:'); + console.log(witness); - util.log('Output:'); - util.log(output); + console.log('Output:'); + console.log(output); break; } @@ -436,7 +435,7 @@ function fuzzLess(flags) { for (;;) { if (++total % 1000 === 0) - util.log('Fuzzed %d scripts.', total); + console.log('Fuzzed %d scripts.', total); if (total % 500 === 0) tx = randomTX(); @@ -469,20 +468,20 @@ function fuzzLess(flags) { assertConsensus(tx, ctx.output, flags, 'OK'); - util.log('Produced valid scripts:'); + console.log('Produced valid scripts:'); - util.log('Input:'); - util.log(ctx.input); + console.log('Input:'); + console.log(ctx.input); - util.log('Witness:'); - util.log(ctx.witness); + console.log('Witness:'); + console.log(ctx.witness); - util.log('Output:'); - util.log(ctx.output); + console.log('Output:'); + console.log(ctx.output); if (ctx.redeem) { - util.log('Redeem:'); - util.log(ctx.redeem); + console.log('Redeem:'); + console.log(ctx.redeem); } break; @@ -505,13 +504,17 @@ function main() { fuzzLess(flags); break; default: - util.log('Please select a mode:'); - util.log('simple, verify, less'); - util.log('Optional `--standard` flag.'); + console.log('Please select a mode:'); + console.log('simple, verify, less'); + console.log('Optional `--standard` flag.'); break; } } +function rand(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + randomKey; randomSignature; diff --git a/scripts/gen.js b/scripts/gen.js index e3fe3d3d..a247b57f 100644 --- a/scripts/gen.js +++ b/scripts/gen.js @@ -1,6 +1,5 @@ 'use strict'; -const util = require('../lib/utils/util'); const consensus = require('../lib/protocol/consensus'); const encoding = require('../lib/utils/encoding'); const TX = require('../lib/primitives/tx'); @@ -106,31 +105,31 @@ const btcd = createGenesisBlock({ nonce: 2 }); -util.log(main); -util.log(''); -util.log(testnet); -util.log(''); -util.log(regtest); -util.log(''); -util.log(segnet3); -util.log(''); -util.log(segnet4); -util.log(''); -util.log(''); -util.log('main hash: %s', main.rhash()); -util.log('main raw: %s', main.toRaw().toString('hex')); -util.log(''); -util.log('testnet hash: %s', testnet.rhash()); -util.log('testnet raw: %s', testnet.toRaw().toString('hex')); -util.log(''); -util.log('regtest hash: %s', regtest.rhash()); -util.log('regtest raw: %s', regtest.toRaw().toString('hex')); -util.log(''); -util.log('segnet3 hash: %s', segnet3.rhash()); -util.log('segnet3 raw: %s', segnet3.toRaw().toString('hex')); -util.log(''); -util.log('segnet4 hash: %s', segnet4.rhash()); -util.log('segnet4 raw: %s', segnet4.toRaw().toString('hex')); -util.log(''); -util.log('btcd simnet hash: %s', btcd.rhash()); -util.log('btcd simnet raw: %s', btcd.toRaw().toString('hex')); +console.log(main); +console.log(''); +console.log(testnet); +console.log(''); +console.log(regtest); +console.log(''); +console.log(segnet3); +console.log(''); +console.log(segnet4); +console.log(''); +console.log(''); +console.log('main hash: %s', main.rhash()); +console.log('main raw: %s', main.toRaw().toString('hex')); +console.log(''); +console.log('testnet hash: %s', testnet.rhash()); +console.log('testnet raw: %s', testnet.toRaw().toString('hex')); +console.log(''); +console.log('regtest hash: %s', regtest.rhash()); +console.log('regtest raw: %s', regtest.toRaw().toString('hex')); +console.log(''); +console.log('segnet3 hash: %s', segnet3.rhash()); +console.log('segnet3 raw: %s', segnet3.toRaw().toString('hex')); +console.log(''); +console.log('segnet4 hash: %s', segnet4.rhash()); +console.log('segnet4 raw: %s', segnet4.toRaw().toString('hex')); +console.log(''); +console.log('btcd simnet hash: %s', btcd.rhash()); +console.log('btcd simnet raw: %s', btcd.toRaw().toString('hex')); diff --git a/test/bech32-test.js b/test/bech32-test.js index fd65d6e2..0d44f3d2 100644 --- a/test/bech32-test.js +++ b/test/bech32-test.js @@ -104,6 +104,16 @@ const invalidAddresses = [ 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv' ]; +const validBech32Tests = [ + 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4', + 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', + 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw50' + + '8d6qejxtdg4y5r3zarvary0c5xw7k7grplx', + 'BC1SW50QA3JX3S', + 'bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj', + 'tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy' +]; + function fromAddress(hrp, addr) { const dec = bech32.decode(addr); @@ -216,4 +226,10 @@ describe('Bech32', function() { assert.throws(() => Address.fromBech32(addr, 'testnet')); }); } + + it('should validate bech32 addresses based only on string data', () => { + for (const bech32addr of validBech32Tests) { + assert.strictEqual(bech32.isBech32(bech32addr), true); + } + }); }); diff --git a/test/http-test.js b/test/http-test.js index c5a29760..02c9004e 100644 --- a/test/http-test.js +++ b/test/http-test.js @@ -11,7 +11,6 @@ const Address = require('../lib/primitives/address'); const Script = require('../lib/script/script'); const Outpoint = require('../lib/primitives/outpoint'); const MTX = require('../lib/primitives/mtx'); -const HTTP = require('../lib/http'); const FullNode = require('../lib/node/fullnode'); const pkg = require('../lib/pkg'); const Network = require('../lib/protocol/network'); @@ -221,7 +220,7 @@ describe('HTTP', function() { sizelimit: 1000000, longpollid: '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206' - + '0000000000', + + '00000000', submitold: false, coinbaseaux: { flags: '6d696e65642062792062636f696e' }, coinbasevalue: 5000000000, diff --git a/test/node-test.js b/test/node-test.js index b1d37d7b..923c0ae2 100644 --- a/test/node-test.js +++ b/test/node-test.js @@ -498,7 +498,7 @@ describe('Node', function() { sigoplimit: 80000, sizelimit: 4000000, weightlimit: 4000000, - longpollid: node.chain.tip.rhash() + '0000000000', + longpollid: node.chain.tip.rhash() + '00000000', submitold: false, coinbaseaux: { flags: '6d696e65642062792062636f696e' }, coinbasevalue: 1250000000, diff --git a/test/protocol-test.js b/test/protocol-test.js index 4f408211..221431fd 100644 --- a/test/protocol-test.js +++ b/test/protocol-test.js @@ -51,7 +51,7 @@ describe('Protocol', function() { time: network.now(), remote: new NetAddress(), local: new NetAddress(), - nonce: util.nonce(), + nonce: Buffer.allocUnsafe(8), agent: agent, height: 0, noRelay: false @@ -70,7 +70,7 @@ describe('Protocol', function() { time: network.now(), remote: new NetAddress(), local: new NetAddress(), - nonce: util.nonce(), + nonce: Buffer.allocUnsafe(8), agent: agent, height: 10, noRelay: true diff --git a/test/script-test.js b/test/script-test.js index a50ff9b4..43c89b67 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -9,8 +9,8 @@ const Witness = require('../lib/script/witness'); const Stack = require('../lib/script/stack'); const Opcode = require('../lib/script/opcode'); const TX = require('../lib/primitives/tx'); -const util = require('../lib/utils/util'); const encoding = require('../lib/utils/encoding'); +const {fromFloat} = require('../lib/utils/fixed'); const scripts = require('./data/script-tests.json'); @@ -39,7 +39,7 @@ function parseScriptTest(data) { let value = 0; if (witArr.length > 0) - value = util.fromFloat(witArr.pop(), 8); + value = fromFloat(witArr.pop(), 8); const witness = Witness.fromString(witArr); const input = Script.fromString(inpHex); diff --git a/test/tx-test.js b/test/tx-test.js index 12d85b0c..ca005a65 100644 --- a/test/tx-test.js +++ b/test/tx-test.js @@ -3,6 +3,7 @@ 'use strict'; +const {inspect} = require('util'); const assert = require('./util/assert'); const util = require('../lib/utils/util'); const encoding = require('../lib/utils/encoding'); @@ -90,7 +91,7 @@ function parseTXTest(data) { flags: flags, view: view, comments: coin - ? util.inspectify(coin.script, false) + ? inspect(coin.script) : 'coinbase', data: data }; diff --git a/test/utils-test.js b/test/utils-test.js index 8f964c3e..70f94ce7 100644 --- a/test/utils-test.js +++ b/test/utils-test.js @@ -9,7 +9,7 @@ const base58 = require('../lib/utils/base58'); const encoding = require('../lib/utils/encoding'); const Amount = require('../lib/btc/amount'); const Validator = require('../lib/utils/validator'); -const util = require('../lib/utils/util'); +const fixed = require('../lib/utils/fixed'); const base58Tests = [ ['', ''], @@ -32,16 +32,6 @@ const base58Tests = [ ['00000000000000000000', '1111111111'] ]; -const validBech32Tests = [ - 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4', - 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', - 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw50' - + '8d6qejxtdg4y5r3zarvary0c5xw7k7grplx', - 'BC1SW50QA3JX3S', - 'bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj', - 'tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy' -]; - const unsigned = [ new U64('ffeeffee', 16), new U64('001fffeeffeeffee', 16), @@ -122,10 +112,10 @@ describe('Utils', function() { assert.strictEqual(parseFloat('0.15645647') * 1e8, 15645646.999999998); assert.strictEqual(15645647 / 1e8, 0.15645647); - assert.strictEqual(util.fromFixed('0.15645647', 8), 15645647); - assert.strictEqual(util.toFixed(15645647, 8), '0.15645647'); - assert.strictEqual(util.fromFloat(0.15645647, 8), 15645647); - assert.strictEqual(util.toFloat(15645647, 8), 0.15645647); + assert.strictEqual(fixed.decode('0.15645647', 8), 15645647); + assert.strictEqual(fixed.encode(15645647, 8), '0.15645647'); + assert.strictEqual(fixed.fromFloat(0.15645647, 8), 15645647); + assert.strictEqual(fixed.toFloat(15645647, 8), 0.15645647); }); it('should write/read new varints', () => { @@ -254,10 +244,4 @@ describe('Utils', function() { assert.strictEqual(validator.bool('shouldBeTrue'), true); assert.strictEqual(validator.bool('shouldBeFalse'), false); }); - - it('should validate bech32 addresses based only on string data', () => { - for (const bech32addr of validBech32Tests) { - assert.strictEqual(util.isBech32(bech32addr), true); - } - }); });