/*!
* server.js - http server 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';
const assert = require('bsert');
const path = require('path');
const {Server} = require('bweb');
const Validator = require('bval');
const base58 = require('bcrypto/lib/encoding/base58');
const MTX = require('../primitives/mtx');
const Outpoint = require('../primitives/outpoint');
const Script = require('../script/script');
const sha256 = require('bcrypto/lib/sha256');
const random = require('bcrypto/lib/random');
const {safeEqual} = require('bcrypto/lib/safe');
const Network = require('../protocol/network');
const Address = require('../primitives/address');
const KeyRing = require('../primitives/keyring');
const Mnemonic = require('../hd/mnemonic');
const HDPrivateKey = require('../hd/private');
const HDPublicKey = require('../hd/public');
const common = require('./common');
const pkg = require('../pkg');
/**
* HTTP
* @alias module:wallet.HTTP
*/
class HTTP extends Server {
/**
* Create an http server.
* @constructor
* @param {Object} options
*/
constructor(options) {
super(new HTTPOptions(options));
this.network = this.options.network;
this.logger = this.options.logger.context('wallet-http');
this.wdb = this.options.node.wdb;
this.rpc = this.options.node.rpc;
this.init();
}
/**
* Initialize http server.
* @private
*/
init() {
this.on('request', (req, res) => {
if (req.method === 'POST' && req.pathname === '/')
return;
this.logger.debug('Request for method=%s path=%s (%s).',
req.method, req.pathname, req.socket.remoteAddress);
});
this.on('listening', (address) => {
this.logger.info('Wallet HTTP server listening on %s (port=%d).',
address.address, address.port);
});
this.initRouter();
this.initSockets();
}
/**
* Initialize routes.
* @private
*/
initRouter() {
if (this.options.cors)
this.use(this.cors());
if (!this.options.noAuth) {
this.use(this.basicAuth({
hash: sha256.digest,
password: this.options.apiKey,
realm: 'wallet'
}));
}
this.use(this.bodyParser({
type: 'json'
}));
this.use(async (req, res) => {
if (!this.options.walletAuth) {
req.admin = true;
return;
}
const valid = Validator.fromRequest(req);
const token = valid.buf('token');
if (token && safeEqual(token, this.options.adminToken)) {
req.admin = true;
return;
}
if (req.method === 'POST' && req.path.length === 0) {
res.json(403);
return;
}
});
this.use(this.jsonRPC());
this.use(this.router());
this.error((err, req, res) => {
const code = err.statusCode || 500;
res.json(code, {
error: {
type: err.type,
code: err.code,
message: err.message
}
});
});
this.hook(async (req, res) => {
if (req.path.length < 2)
return;
if (req.path[0] !== 'wallet')
return;
if (req.method === 'PUT' && req.path.length === 2)
return;
const valid = Validator.fromRequest(req);
const id = valid.str('id');
const token = valid.buf('token');
if (!id) {
res.json(403);
return;
}
if (req.admin || !this.options.walletAuth) {
const wallet = await this.wdb.get(id);
if (!wallet) {
res.json(404);
return;
}
req.wallet = wallet;
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.json(403);
return;
}
if (!wallet) {
res.json(404);
return;
}
req.wallet = wallet;
this.logger.info('Successful auth for %s.', id);
});
// Info
this.get('/', async (req, res) => {
res.json(200, {
version: pkg.version,
network: this.network.type
});
});
// Rescan
this.post('/rescan', async (req, res) => {
if (!req.admin) {
res.json(403);
return;
}
const valid = Validator.fromRequest(req);
const height = valid.u32('height');
res.json(200, { success: true });
await this.wdb.rescan(height);
});
// Resend
this.post('/resend', async (req, res) => {
if (!req.admin) {
res.json(403);
return;
}
await this.wdb.resend();
res.json(200, { success: true });
});
// Backup WalletDB
this.post('/backup', async (req, res) => {
if (!req.admin) {
res.json(403);
return;
}
const valid = Validator.fromRequest(req);
const path = valid.str('path');
enforce(path, 'Path is required.');
await this.wdb.backup(path);
res.json(200, { success: true });
});
// List wallets
this.get('/wallet', async (req, res) => {
if (!req.admin) {
res.json(403);
return;
}
const wallets = await this.wdb.getWallets();
res.json(200, wallets);
});
// Get wallet
this.get('/wallet/:id', async (req, res) => {
const balance = await req.wallet.getBalance();
res.json(200, req.wallet.toJSON(false, balance));
});
// Get wallet master key
this.get('/wallet/:id/master', (req, res) => {
if (!req.admin) {
res.json(403);
return;
}
res.json(200, req.wallet.master.toJSON(this.network, true));
});
// Create wallet
this.put('/wallet/:id', async (req, res) => {
const valid = Validator.fromRequest(req);
let master = valid.str('master');
let mnemonic = valid.str('mnemonic');
let accountKey = valid.str('accountKey');
if (master)
master = HDPrivateKey.fromBase58(master, this.network);
if (mnemonic)
mnemonic = Mnemonic.fromPhrase(mnemonic);
if (accountKey)
accountKey = HDPublicKey.fromBase58(accountKey, this.network);
const wallet = await this.wdb.create({
id: valid.str('id'),
type: valid.str('type'),
m: valid.u32('m'),
n: valid.u32('n'),
passphrase: valid.str('passphrase'),
master: master,
mnemonic: mnemonic,
witness: valid.bool('witness'),
accountKey: accountKey,
watchOnly: valid.bool('watchOnly')
});
const balance = await wallet.getBalance();
res.json(200, wallet.toJSON(false, balance));
});
// List accounts
this.get('/wallet/:id/account', async (req, res) => {
const accounts = await req.wallet.getAccounts();
res.json(200, accounts);
});
// Get account
this.get('/wallet/:id/account/:account', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const account = await req.wallet.getAccount(acct);
if (!account) {
res.json(404);
return;
}
const balance = await req.wallet.getBalance(account.accountIndex);
res.json(200, account.toJSON(balance));
});
// Create account
this.put('/wallet/:id/account/:account', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
let accountKey = valid.get('accountKey');
if (accountKey)
accountKey = HDPublicKey.fromBase58(accountKey, this.network);
const options = {
name: valid.str('account'),
witness: valid.bool('witness'),
type: valid.str('type'),
m: valid.u32('m'),
n: valid.u32('n'),
accountKey: accountKey,
lookahead: valid.u32('lookahead')
};
const account = await req.wallet.createAccount(options, passphrase);
const balance = await req.wallet.getBalance(account.accountIndex);
res.json(200, account.toJSON(balance));
});
// Change passphrase
this.post('/wallet/:id/passphrase', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const old = valid.str('old');
enforce(passphrase, 'Passphrase is required.');
await req.wallet.setPassphrase(passphrase, old);
res.json(200, { success: true });
});
// Unlock wallet
this.post('/wallet/:id/unlock', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const timeout = valid.u32('timeout');
enforce(passphrase, 'Passphrase is required.');
await req.wallet.unlock(passphrase, timeout);
res.json(200, { success: true });
});
// Lock wallet
this.post('/wallet/:id/lock', async (req, res) => {
await req.wallet.lock();
res.json(200, { success: true });
});
// Import key
this.post('/wallet/:id/import', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const passphrase = valid.str('passphrase');
const pub = valid.buf('publicKey');
const priv = valid.str('privateKey');
const address = valid.str('address');
if (pub) {
const key = KeyRing.fromPublic(pub);
await req.wallet.importKey(acct, key);
res.json(200, { success: true });
return;
}
if (priv) {
const key = KeyRing.fromSecret(priv, this.network);
await req.wallet.importKey(acct, key, passphrase);
res.json(200, { success: true });
return;
}
if (address) {
const addr = Address.fromString(address, this.network);
await req.wallet.importAddress(acct, addr);
res.json(200, { success: true });
return;
}
enforce(false, 'Key or address is required.');
});
// Generate new token
this.post('/wallet/:id/retoken', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const token = await req.wallet.retoken(passphrase);
res.json(200, {
token: token.toString('hex')
});
});
// Send TX
this.post('/wallet/:id/send', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const outputs = valid.array('outputs', []);
const options = {
rate: valid.u64('rate'),
blocks: valid.u32('blocks'),
maxFee: valid.u64('maxFee'),
selection: valid.str('selection'),
smart: valid.bool('smart'),
account: valid.str('account'),
sort: valid.bool('sort'),
subtractFee: valid.bool('subtractFee'),
subtractIndex: valid.i32('subtractIndex'),
depth: valid.u32(['confirmations', 'depth']),
outputs: []
};
for (const output of outputs) {
const valid = new Validator(output);
let addr = valid.str('address');
let script = valid.buf('script');
if (addr)
addr = Address.fromString(addr, this.network);
if (script)
script = Script.fromRaw(script);
options.outputs.push({
address: addr,
script: script,
value: valid.u64('value')
});
}
const tx = await req.wallet.send(options, passphrase);
const details = await req.wallet.getDetails(tx.hash());
res.json(200, details.toJSON(this.network, this.wdb.height));
});
// Create TX
this.post('/wallet/:id/create', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const outputs = valid.array('outputs', []);
const sign = valid.bool('sign', true);
const options = {
rate: valid.u64('rate'),
blocks: valid.u32('blocks'),
maxFee: valid.u64('maxFee'),
selection: valid.str('selection'),
smart: valid.bool('smart'),
account: valid.str('account'),
sort: valid.bool('sort'),
subtractFee: valid.bool('subtractFee'),
subtractIndex: valid.i32('subtractIndex'),
depth: valid.u32(['confirmations', 'depth']),
template: valid.bool('template', sign),
outputs: []
};
for (const output of outputs) {
const valid = new Validator(output);
let addr = valid.str('address');
let script = valid.buf('script');
if (addr)
addr = Address.fromString(addr, this.network);
if (script)
script = Script.fromRaw(script);
options.outputs.push({
address: addr,
script: script,
value: valid.u64('value')
});
}
const tx = await req.wallet.createTX(options);
if (sign)
await req.wallet.sign(tx, passphrase);
res.json(200, tx.getJSON(this.network));
});
// Sign TX
this.post('/wallet/:id/sign', async (req, res) => {
const valid = Validator.fromRequest(req);
const passphrase = valid.str('passphrase');
const raw = valid.buf('tx');
enforce(raw, 'TX is required.');
const tx = MTX.fromRaw(raw);
tx.view = await req.wallet.getCoinView(tx);
await req.wallet.sign(tx, passphrase);
res.json(200, tx.getJSON(this.network));
});
// Zap Wallet TXs
this.post('/wallet/:id/zap', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const age = valid.u32('age');
enforce(age, 'Age is required.');
await req.wallet.zap(acct, age);
res.json(200, { success: true });
});
// Abandon Wallet TX
this.del('/wallet/:id/tx/:hash', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.brhash('hash');
enforce(hash, 'Hash is required.');
await req.wallet.abandon(hash);
res.json(200, { success: true });
});
// List blocks
this.get('/wallet/:id/block', async (req, res) => {
const heights = await req.wallet.getBlocks();
res.json(200, heights);
});
// Get Block Record
this.get('/wallet/:id/block/:height', async (req, res) => {
const valid = Validator.fromRequest(req);
const height = valid.u32('height');
enforce(height != null, 'Height is required.');
const block = await req.wallet.getBlock(height);
if (!block) {
res.json(404);
return;
}
res.json(200, block.toJSON());
});
// Add key
this.put('/wallet/:id/shared-key', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const b58 = valid.str('accountKey');
enforce(b58, 'Key is required.');
const key = HDPublicKey.fromBase58(b58, this.network);
const added = await req.wallet.addSharedKey(acct, key);
res.json(200, {
success: true,
addedKey: added
});
});
// Remove key
this.del('/wallet/:id/shared-key', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const b58 = valid.str('accountKey');
enforce(b58, 'Key is required.');
const key = HDPublicKey.fromBase58(b58, this.network);
const removed = await req.wallet.removeSharedKey(acct, key);
res.json(200, {
success: true,
removedKey: removed
});
});
// Get key by address
this.get('/wallet/:id/key/:address', async (req, res) => {
const valid = Validator.fromRequest(req);
const b58 = valid.str('address');
enforce(b58, 'Address is required.');
const addr = Address.fromString(b58, this.network);
const key = await req.wallet.getKey(addr);
if (!key) {
res.json(404);
return;
}
res.json(200, key.toJSON(this.network));
});
// Get private key
this.get('/wallet/:id/wif/:address', async (req, res) => {
const valid = Validator.fromRequest(req);
const address = valid.str('address');
const passphrase = valid.str('passphrase');
enforce(address, 'Address is required.');
const addr = Address.fromString(address, this.network);
const key = await req.wallet.getPrivateKey(addr, passphrase);
if (!key) {
res.json(404);
return;
}
res.json(200, { privateKey: key.toSecret(this.network) });
});
// Create address
this.post('/wallet/:id/address', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const addr = await req.wallet.createReceive(acct);
res.json(200, addr.toJSON(this.network));
});
// Create change address
this.post('/wallet/:id/change', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const addr = await req.wallet.createChange(acct);
res.json(200, addr.toJSON(this.network));
});
// Create nested address
this.post('/wallet/:id/nested', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const addr = await req.wallet.createNested(acct);
res.json(200, addr.toJSON(this.network));
});
// Wallet Balance
this.get('/wallet/:id/balance', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const balance = await req.wallet.getBalance(acct);
if (!balance) {
res.json(404);
return;
}
res.json(200, balance.toJSON());
});
// Wallet UTXOs
this.get('/wallet/:id/coin', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const coins = await req.wallet.getCoins(acct);
const result = [];
common.sortCoins(coins);
for (const coin of coins)
result.push(coin.getJSON(this.network));
res.json(200, result);
});
// Locked coins
this.get('/wallet/:id/locked', async (req, res) => {
const locked = req.wallet.getLocked();
const result = [];
for (const outpoint of locked)
result.push(outpoint.toJSON());
res.json(200, result);
});
// Lock coin
this.put('/wallet/:id/locked/:hash/:index', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.brhash('hash');
const index = valid.u32('index');
enforce(hash, 'Hash is required.');
enforce(index != null, 'Index is required.');
const outpoint = new Outpoint(hash, index);
req.wallet.lockCoin(outpoint);
res.json(200, { success: true });
});
// Unlock coin
this.del('/wallet/:id/locked/:hash/:index', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.brhash('hash');
const index = valid.u32('index');
enforce(hash, 'Hash is required.');
enforce(index != null, 'Index is required.');
const outpoint = new Outpoint(hash, index);
req.wallet.unlockCoin(outpoint);
res.json(200, { success: true });
});
// Wallet Coin
this.get('/wallet/:id/coin/:hash/:index', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.brhash('hash');
const index = valid.u32('index');
enforce(hash, 'Hash is required.');
enforce(index != null, 'Index is required.');
const coin = await req.wallet.getCoin(hash, index);
if (!coin) {
res.json(404);
return;
}
res.json(200, coin.getJSON(this.network));
});
// Wallet TXs
this.get('/wallet/:id/tx/history', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const txs = await req.wallet.getHistory(acct);
common.sortTX(txs);
const details = await req.wallet.toDetails(txs);
const result = [];
for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));
res.json(200, result);
});
// Wallet Pending TXs
this.get('/wallet/:id/tx/unconfirmed', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const txs = await req.wallet.getPending(acct);
common.sortTX(txs);
const details = await req.wallet.toDetails(txs);
const result = [];
for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));
res.json(200, result);
});
// Wallet TXs within time range
this.get('/wallet/:id/tx/range', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const options = {
start: valid.u32('start'),
end: valid.u32('end'),
limit: valid.u32('limit'),
reverse: valid.bool('reverse')
};
const txs = await req.wallet.getRange(acct, options);
const details = await req.wallet.toDetails(txs);
const result = [];
for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));
res.json(200, result);
});
// Last Wallet TXs
this.get('/wallet/:id/tx/last', async (req, res) => {
const valid = Validator.fromRequest(req);
const acct = valid.str('account');
const limit = valid.u32('limit');
const txs = await req.wallet.getLast(acct, limit);
const details = await req.wallet.toDetails(txs);
const result = [];
for (const item of details)
result.push(item.toJSON(this.network, this.wdb.height));
res.json(200, result);
});
// Wallet TX
this.get('/wallet/:id/tx/:hash', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.brhash('hash');
enforce(hash, 'Hash is required.');
const tx = await req.wallet.getTX(hash);
if (!tx) {
res.json(404);
return;
}
const details = await req.wallet.toDetails(tx);
res.json(200, details.toJSON(this.network, this.wdb.height));
});
// Resend
this.post('/wallet/:id/resend', async (req, res) => {
await req.wallet.resend();
res.json(200, { success: true });
});
}
/**
* Initialize websockets.
* @private
*/
initSockets() {
const handleTX = (event, wallet, tx, details) => {
const name = `w:${wallet.id}`;
if (!this.channel(name) && !this.channel('w:*'))
return;
const json = details.toJSON(this.network, this.wdb.liveHeight());
if (this.channel(name))
this.to(name, event, wallet.id, json);
if (this.channel('w:*'))
this.to('w:*', event, wallet.id, json);
};
this.wdb.on('tx', (wallet, tx, details) => {
handleTX('tx', wallet, tx, details);
});
this.wdb.on('confirmed', (wallet, tx, details) => {
handleTX('confirmed', wallet, tx, details);
});
this.wdb.on('unconfirmed', (wallet, tx, details) => {
handleTX('unconfirmed', wallet, tx, details);
});
this.wdb.on('conflict', (wallet, tx, details) => {
handleTX('conflict', wallet, tx, details);
});
this.wdb.on('balance', (wallet, balance) => {
const name = `w:${wallet.id}`;
if (!this.channel(name) && !this.channel('w:*'))
return;
const json = balance.toJSON();
if (this.channel(name))
this.to(name, 'balance', wallet.id, json);
if (this.channel('w:*'))
this.to('w:*', 'balance', wallet.id, json);
});
this.wdb.on('address', (wallet, receive) => {
const name = `w:${wallet.id}`;
if (!this.channel(name) && !this.channel('w:*'))
return;
const json = [];
for (const addr of receive)
json.push(addr.toJSON(this.network));
if (this.channel(name))
this.to(name, 'address', wallet.id, json);
if (this.channel('w:*'))
this.to('w:*', 'address', wallet.id, json);
});
}
/**
* Handle new websocket.
* @private
* @param {WebSocket} socket
*/
handleSocket(socket) {
socket.hook('auth', (...args) => {
if (socket.channel('auth'))
throw new Error('Already authed.');
if (!this.options.noAuth) {
const valid = new Validator(args);
const key = valid.str(0, '');
if (key.length > 255)
throw new Error('Invalid API key.');
const data = Buffer.from(key, 'utf8');
const hash = sha256.digest(data);
if (!safeEqual(hash, this.options.apiHash))
throw new Error('Invalid API key.');
}
socket.join('auth');
this.logger.info('Successful auth from %s.', socket.host);
this.handleAuth(socket);
return null;
});
}
/**
* Handle new auth'd websocket.
* @private
* @param {WebSocket} socket
*/
handleAuth(socket) {
socket.hook('join', async (...args) => {
const valid = new Validator(args);
const id = valid.str(0, '');
const token = valid.buf(1);
if (!id)
throw new Error('Invalid parameter.');
if (!this.options.walletAuth) {
socket.join('admin');
} else if (token) {
if (safeEqual(token, this.options.adminToken))
socket.join('admin');
}
if (socket.channel('admin') || !this.options.walletAuth) {
socket.join(`w:${id}`);
return null;
}
if (id === '*')
throw new Error('Bad token.');
if (!token)
throw new Error('Invalid parameter.');
let wallet;
try {
wallet = await this.wdb.auth(id, token);
} catch (e) {
this.logger.info('Wallet auth failure for %s: %s.', id, e.message);
throw new Error('Bad token.');
}
if (!wallet)
throw new Error('Wallet does not exist.');
this.logger.info('Successful wallet auth for %s.', id);
socket.join(`w:${id}`);
return null;
});
socket.hook('leave', (...args) => {
const valid = new Validator(args);
const id = valid.str(0, '');
if (!id)
throw new Error('Invalid parameter.');
socket.leave(`w:${id}`);
return null;
});
}
}
class HTTPOptions {
/**
* HTTPOptions
* @alias module:http.HTTPOptions
* @constructor
* @param {Object} options
*/
constructor(options) {
this.network = Network.primary;
this.logger = null;
this.node = null;
this.apiKey = base58.encode(random.randomBytes(20));
this.apiHash = sha256.digest(Buffer.from(this.apiKey, 'ascii'));
this.adminToken = random.randomBytes(32);
this.serviceHash = this.apiHash;
this.noAuth = false;
this.cors = false;
this.walletAuth = false;
this.prefix = null;
this.host = '127.0.0.1';
this.port = 8080;
this.ssl = false;
this.keyFile = null;
this.certFile = null;
this.fromOptions(options);
}
/**
* Inject properties from object.
* @private
* @param {Object} options
* @returns {HTTPOptions}
*/
fromOptions(options) {
assert(options);
assert(options.node && typeof options.node === 'object',
'HTTP Server requires a WalletDB.');
this.node = options.node;
this.network = options.node.network;
this.logger = options.node.logger;
this.port = this.network.walletPort;
if (options.logger != null) {
assert(typeof options.logger === 'object');
this.logger = options.logger;
}
if (options.apiKey != null) {
assert(typeof options.apiKey === 'string',
'API key must be a string.');
assert(options.apiKey.length <= 255,
'API key must be under 255 bytes.');
this.apiKey = options.apiKey;
this.apiHash = sha256.digest(Buffer.from(this.apiKey, 'ascii'));
}
if (options.adminToken != null) {
if (typeof options.adminToken === 'string') {
assert(options.adminToken.length === 64,
'Admin token must be a 32 byte hex string.');
const token = Buffer.from(options.adminToken, 'hex');
assert(token.length === 32,
'Admin token must be a 32 byte hex string.');
this.adminToken = token;
} else {
assert(Buffer.isBuffer(options.adminToken),
'Admin token must be a hex string or buffer.');
assert(options.adminToken.length === 32,
'Admin token must be 32 bytes.');
this.adminToken = options.adminToken;
}
}
if (options.noAuth != null) {
assert(typeof options.noAuth === 'boolean');
this.noAuth = options.noAuth;
}
if (options.cors != null) {
assert(typeof options.cors === 'boolean');
this.cors = options.cors;
}
if (options.walletAuth != null) {
assert(typeof options.walletAuth === 'boolean');
this.walletAuth = options.walletAuth;
}
if (options.prefix != null) {
assert(typeof options.prefix === 'string');
this.prefix = options.prefix;
this.keyFile = path.join(this.prefix, 'key.pem');
this.certFile = path.join(this.prefix, 'cert.pem');
}
if (options.host != null) {
assert(typeof options.host === 'string');
this.host = options.host;
}
if (options.port != null) {
assert((options.port & 0xffff) === options.port,
'Port must be a number.');
this.port = options.port;
}
if (options.ssl != null) {
assert(typeof options.ssl === 'boolean');
this.ssl = options.ssl;
}
if (options.keyFile != null) {
assert(typeof options.keyFile === 'string');
this.keyFile = options.keyFile;
}
if (options.certFile != null) {
assert(typeof options.certFile === 'string');
this.certFile = options.certFile;
}
// Allow no-auth implicitly
// if we're listening locally.
if (!options.apiKey) {
if (this.host === '127.0.0.1' || this.host === '::1')
this.noAuth = true;
}
return this;
}
/**
* Instantiate http options from object.
* @param {Object} options
* @returns {HTTPOptions}
*/
static fromOptions(options) {
return new HTTPOptions().fromOptions(options);
}
}
/*
* Helpers
*/
function enforce(value, msg) {
if (!value) {
const err = new Error(msg);
err.statusCode = 400;
throw err;
}
}
/*
* Expose
*/
module.exports = HTTP;