itns-sidechain/lib/node/http.js

775 lines
18 KiB
JavaScript
Raw Normal View History

/*!
2016-03-10 02:15:29 -08:00
* server.js - http server for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
2017-02-03 22:47:26 -08:00
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
2016-06-09 16:18:50 -07:00
* https://github.com/bcoin-org/bcoin
2016-03-10 02:15:29 -08:00
*/
2016-06-13 01:06:01 -07:00
'use strict';
2017-06-29 20:54:07 -07:00
const assert = require('assert');
const path = require('path');
2017-10-22 05:44:16 -07:00
const {Server} = require('bweb');
2017-11-16 10:41:19 -08:00
const Validator = require('bval');
const {base58} = require('bstring');
2017-11-16 12:24:45 -08:00
const {BloomFilter} = require('bfilter');
2017-11-01 15:41:32 -07:00
const sha256 = require('bcrypto/lib/sha256');
const random = require('bcrypto/lib/random');
const ccmp = require('bcrypto/lib/ccmp');
const util = require('../utils/util');
2017-06-29 20:54:07 -07:00
const TX = require('../primitives/tx');
const Outpoint = require('../primitives/outpoint');
const Network = require('../protocol/network');
const pkg = require('../pkg');
2016-03-10 02:15:29 -08:00
2017-11-16 19:43:07 -08:00
/**
* HTTP
* @alias module:http.Server
*/
2017-10-26 12:31:08 -07:00
class HTTP extends Server {
2017-10-22 05:44:16 -07:00
/**
2017-11-16 19:43:07 -08:00
* Create an http server.
2017-10-22 05:44:16 -07:00
* @constructor
* @param {Object} options
*/
constructor(options) {
super(new HTTPOptions(options));
this.network = this.options.network;
this.logger = this.options.logger.context('http');
this.node = this.options.node;
this.chain = this.node.chain;
this.mempool = this.node.mempool;
this.pool = this.node.pool;
this.fees = this.node.fees;
this.miner = this.node.miner;
this.rpc = this.node.rpc;
this.init();
}
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
/**
* Initialize routes.
* @private
*/
2016-06-05 06:55:35 -07:00
2017-10-22 05:44:16 -07:00
init() {
this.on('request', (req, res) => {
if (req.method === 'POST' && req.pathname === '/')
return;
2016-07-13 07:07:14 -07:00
2017-10-22 05:44:16 -07:00
this.logger.debug('Request for method=%s path=%s (%s).',
req.method, req.pathname, req.socket.remoteAddress);
});
2016-04-04 18:45:02 -07:00
2017-10-22 05:44:16 -07:00
this.on('listening', (address) => {
this.logger.info('Node HTTP server listening on %s (port=%d).',
address.address, address.port);
});
2016-04-04 18:45:02 -07:00
2017-10-22 05:44:16 -07:00
this.initRouter();
this.initSockets();
}
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
/**
* Initialize routes.
* @private
*/
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
initRouter() {
2018-01-09 14:30:37 -08:00
// this.use(this.cors());
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
if (!this.options.noAuth) {
this.use(this.basicAuth({
2017-11-01 15:41:32 -07:00
hash: sha256.digest,
2017-10-22 05:44:16 -07:00
password: this.options.apiKey,
realm: 'node'
}));
}
2017-10-22 05:44:16 -07:00
this.use(this.bodyParser({
type: 'json'
}));
2016-09-30 12:48:19 -07:00
2017-10-22 05:44:16 -07:00
this.use(this.jsonRPC());
this.use(this.router());
2017-10-22 15:51:30 -07:00
this.error((err, req, res) => {
const code = err.statusCode || 500;
res.json(code, {
error: {
type: err.type,
code: err.code,
message: err.message
}
});
});
2017-10-22 05:44:16 -07:00
this.get('/', async (req, res) => {
const totalTX = this.mempool ? this.mempool.map.size : 0;
const size = this.mempool ? this.mempool.getSize() : 0;
2017-11-01 15:41:32 -07:00
2017-10-22 05:44:16 -07:00
let addr = this.pool.hosts.getLocal();
if (!addr)
addr = this.pool.hosts.address;
res.json(200, {
version: pkg.version,
network: this.network.type,
chain: {
height: this.chain.height,
tip: this.chain.tip.rhash(),
progress: this.chain.getProgress()
},
pool: {
host: addr.host,
port: addr.port,
agent: this.pool.options.agent,
services: this.pool.options.services.toString(2),
outbound: this.pool.peers.outbound,
inbound: this.pool.peers.inbound
},
mempool: {
tx: totalTX,
size: size
},
time: {
uptime: this.node.uptime(),
system: util.now(),
adjusted: this.network.now(),
offset: this.network.time.offset
},
2017-10-26 04:07:36 -07:00
memory: this.logger.memoryUsage()
2017-10-22 05:44:16 -07:00
});
});
2016-04-06 18:20:03 -07:00
2017-10-22 05:44:16 -07:00
// UTXO by address
this.get('/coin/address/:address', async (req, res) => {
const valid = Validator.fromRequest(req);
const address = valid.str('address');
2016-08-17 18:54:54 -07:00
2017-10-22 05:44:16 -07:00
enforce(address, 'Address is required.');
enforce(!this.chain.options.spv, 'Cannot get coins in SPV mode.');
2016-09-22 01:29:48 -07:00
2017-10-22 05:44:16 -07:00
const coins = await this.node.getCoinsByAddress(address);
const result = [];
2016-09-22 01:29:48 -07:00
2017-10-22 05:44:16 -07:00
for (const coin of coins)
result.push(coin.getJSON(this.network));
2016-09-22 01:29:48 -07:00
2017-10-22 05:44:16 -07:00
res.json(200, result);
});
2017-10-22 05:44:16 -07:00
// UTXO by id
this.get('/coin/:hash/:index', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.rhash('hash');
const index = valid.u32('index');
2016-07-12 11:52:16 -07:00
2017-10-22 05:44:16 -07:00
enforce(hash, 'Hash is required.');
enforce(index != null, 'Index is required.');
enforce(!this.chain.options.spv, 'Cannot get coins in SPV mode.');
2017-10-22 05:44:16 -07:00
const coin = await this.node.getCoin(hash, index);
2017-10-22 05:44:16 -07:00
if (!coin) {
2017-10-22 15:51:30 -07:00
res.json(404);
2017-10-22 05:44:16 -07:00
return;
}
2016-10-25 08:40:08 -07:00
2017-10-22 05:44:16 -07:00
res.json(200, coin.getJSON(this.network));
});
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
// Bulk read UTXOs
this.post('/coin/address', async (req, res) => {
const valid = Validator.fromRequest(req);
const address = valid.array('addresses');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(address, 'Address is required.');
enforce(!this.chain.options.spv, 'Cannot get coins in SPV mode.');
2017-10-22 05:44:16 -07:00
const coins = await this.node.getCoinsByAddress(address);
const result = [];
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
for (const coin of coins)
result.push(coin.getJSON(this.network));
2017-10-22 05:44:16 -07:00
res.json(200, result);
});
2017-10-22 05:44:16 -07:00
// TX by hash
this.get('/tx/:hash', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.rhash('hash');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(hash, 'Hash is required.');
enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.');
2017-10-22 05:44:16 -07:00
const meta = await this.node.getMeta(hash);
2017-10-22 05:44:16 -07:00
if (!meta) {
2017-10-22 15:51:30 -07:00
res.json(404);
2017-10-22 05:44:16 -07:00
return;
}
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
const view = await this.node.getMetaView(meta);
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
res.json(200, meta.getJSON(this.network, view, this.chain.height));
});
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
// TX by address
this.get('/tx/address/:address', async (req, res) => {
const valid = Validator.fromRequest(req);
const address = valid.str('address');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(address, 'Address is required.');
enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.');
2017-10-22 05:44:16 -07:00
const metas = await this.node.getMetaByAddress(address);
const result = [];
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
for (const meta of metas) {
const view = await this.node.getMetaView(meta);
result.push(meta.getJSON(this.network, view, this.chain.height));
}
2017-10-22 05:44:16 -07:00
res.json(200, result);
});
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
// Bulk read TXs
this.post('/tx/address', async (req, res) => {
const valid = Validator.fromRequest(req);
const address = valid.array('addresses');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(address, 'Address is required.');
enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.');
2017-10-22 05:44:16 -07:00
const metas = await this.node.getMetaByAddress(address);
const result = [];
2017-10-22 05:44:16 -07:00
for (const meta of metas) {
const view = await this.node.getMetaView(meta);
result.push(meta.getJSON(this.network, view, this.chain.height));
}
2017-10-22 05:44:16 -07:00
res.json(200, result);
});
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
// Block by hash/height
this.get('/block/:block', async (req, res) => {
const valid = Validator.fromRequest(req);
const hash = valid.uintrhash('block');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(hash != null, 'Hash or height required.');
enforce(!this.chain.options.spv, 'Cannot get block in SPV mode.');
2017-10-22 05:44:16 -07:00
const block = await this.chain.getBlock(hash);
2017-10-22 05:44:16 -07:00
if (!block) {
2017-10-22 15:51:30 -07:00
res.json(404);
2017-10-22 05:44:16 -07:00
return;
}
2017-10-22 05:44:16 -07:00
const view = await this.chain.getBlockView(block);
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
if (!view) {
2017-10-22 15:51:30 -07:00
res.json(404);
2017-10-22 05:44:16 -07:00
return;
}
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
const height = await this.chain.getHeight(hash);
const depth = this.chain.height - height + 1;
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
res.json(200, block.getJSON(this.network, view, height, depth));
});
2017-10-19 01:48:09 -07:00
2017-10-22 05:44:16 -07:00
// Mempool snapshot
this.get('/mempool', async (req, res) => {
enforce(this.mempool, 'No mempool available.');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
const hashes = this.mempool.getSnapshot();
const result = [];
2017-10-22 05:44:16 -07:00
for (const hash of hashes)
2017-11-17 00:00:36 -08:00
result.push(util.revHex(hash));
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
res.json(200, result);
});
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
// Broadcast TX
this.post('/broadcast', async (req, res) => {
const valid = Validator.fromRequest(req);
const raw = valid.buf('tx');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(raw, 'TX is required.');
2017-06-26 21:29:24 -07:00
2017-10-22 05:44:16 -07:00
const tx = TX.fromRaw(raw);
2017-06-26 21:29:24 -07:00
2017-10-22 05:44:16 -07:00
await this.node.sendTX(tx);
2017-06-26 21:29:24 -07:00
2017-10-22 05:44:16 -07:00
res.json(200, { success: true });
});
2017-06-26 21:29:24 -07:00
2017-10-22 05:44:16 -07:00
// Estimate fee
this.get('/fee', async (req, res) => {
const valid = Validator.fromRequest(req);
const blocks = valid.u32('blocks');
2017-10-22 05:44:16 -07:00
if (!this.fees) {
2017-10-22 15:51:30 -07:00
res.json(200, { rate: this.network.feeRate });
2017-10-22 05:44:16 -07:00
return;
}
2016-09-22 01:29:48 -07:00
2017-10-22 05:44:16 -07:00
const fee = this.fees.estimateFee(blocks);
2016-09-22 01:29:48 -07:00
2017-10-22 05:44:16 -07:00
res.json(200, { rate: fee });
});
2017-10-22 05:44:16 -07:00
// Reset chain
this.post('/reset', async (req, res) => {
const valid = Validator.fromRequest(req);
const height = valid.u32('height');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
enforce(height != null, 'Height is required.');
2016-10-13 17:14:56 -07:00
2017-10-22 05:44:16 -07:00
await this.chain.reset(height);
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
res.json(200, { success: true });
});
}
2017-10-22 05:44:16 -07:00
/**
* Handle new websocket.
* @private
* @param {WebSocket} socket
*/
2016-03-19 08:24:22 -07:00
2017-10-22 05:44:16 -07:00
handleSocket(socket) {
socket.hook('auth', (...args) => {
2017-10-23 12:40:32 -07:00
if (socket.channel('auth'))
2017-10-22 05:44:16 -07:00
throw new Error('Already authed.');
2017-10-22 05:44:16 -07:00
if (!this.options.noAuth) {
const valid = new Validator(args);
const key = valid.str(0, '');
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
if (key.length > 255)
throw new Error('Invalid API key.');
2016-04-01 17:02:14 -07:00
2017-10-22 05:44:16 -07:00
const data = Buffer.from(key, 'ascii');
2017-11-01 15:41:32 -07:00
const hash = sha256.digest(data);
2016-08-13 18:27:28 -07:00
2017-10-22 05:44:16 -07:00
if (!ccmp(hash, this.options.apiHash))
throw new Error('Invalid API key.');
}
2017-01-12 02:23:16 -08:00
2017-10-23 12:40:32 -07:00
socket.join('auth');
2017-10-26 11:58:50 -07:00
this.logger.info('Successful auth from %s.', socket.host);
2017-10-22 05:44:16 -07:00
this.handleAuth(socket);
2017-10-22 05:44:16 -07:00
return null;
});
2016-08-13 05:33:29 -07:00
2017-10-22 05:44:16 -07:00
socket.fire('version', {
version: pkg.version,
network: this.network.type
});
}
2016-07-14 16:53:13 -07:00
2017-10-22 05:44:16 -07:00
/**
* Handle new auth'd websocket.
* @private
* @param {WebSocket} socket
*/
2016-07-14 16:53:13 -07:00
2017-10-22 05:44:16 -07:00
handleAuth(socket) {
socket.hook('watch chain', () => {
socket.join('chain');
return null;
});
2016-08-13 05:33:29 -07:00
2017-10-22 05:44:16 -07:00
socket.hook('unwatch chain', () => {
socket.leave('chain');
return null;
});
2016-08-13 05:33:29 -07:00
2017-10-22 05:44:16 -07:00
socket.hook('watch mempool', () => {
socket.join('mempool');
return null;
});
2016-08-13 05:33:29 -07:00
2017-10-22 05:44:16 -07:00
socket.hook('unwatch mempool', () => {
socket.leave('mempool');
return null;
});
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
socket.hook('set filter', (...args) => {
const valid = new Validator(args);
const data = valid.buf(0);
2016-11-19 18:00:13 -08:00
2017-10-22 05:44:16 -07:00
if (!data)
throw new Error('Invalid parameter.');
2016-10-10 07:13:42 -07:00
2017-10-30 21:22:31 -07:00
socket.filter = BloomFilter.fromRaw(data);
2016-11-19 21:40:48 -08:00
2017-10-22 05:44:16 -07:00
return null;
});
2016-10-10 07:13:42 -07:00
2017-10-22 05:44:16 -07:00
socket.hook('get tip', () => {
return this.chain.tip.toRaw();
});
2016-11-19 21:40:48 -08:00
2017-10-22 05:44:16 -07:00
socket.hook('get entry', async (...args) => {
const valid = new Validator(args);
const block = valid.uintrhash(0);
2016-11-19 18:00:13 -08:00
2017-10-22 05:44:16 -07:00
if (block == null)
throw new Error('Invalid parameter.');
2016-11-19 18:00:13 -08:00
2017-10-22 05:44:16 -07:00
const entry = await this.chain.getEntry(block);
2016-11-19 18:00:13 -08:00
2017-10-22 05:44:16 -07:00
if (!entry)
return null;
2016-11-19 21:40:31 -08:00
2017-10-22 05:44:16 -07:00
if (!await this.chain.isMainChain(entry))
return null;
2016-11-19 21:40:31 -08:00
2017-10-22 05:44:16 -07:00
return entry.toRaw();
});
2017-10-22 05:44:16 -07:00
socket.hook('get hashes', async (...args) => {
const valid = new Validator(args);
const start = valid.i32(0, -1);
const end = valid.i32(1, -1);
2016-08-22 15:24:15 -07:00
2017-12-12 22:32:27 -08:00
return this.chain.getHashes(start, end);
2017-10-22 05:44:16 -07:00
});
2017-10-22 05:44:16 -07:00
socket.hook('add filter', (...args) => {
const valid = new Validator(args);
const chunks = valid.array(0);
2017-10-22 05:44:16 -07:00
if (!chunks)
throw new Error('Invalid parameter.');
2016-08-13 18:27:28 -07:00
2017-10-22 05:44:16 -07:00
if (!socket.filter)
throw new Error('No filter set.');
2016-10-05 07:11:13 -07:00
2017-10-22 05:44:16 -07:00
const items = new Validator(chunks);
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
for (let i = 0; i < chunks.length; i++) {
const data = items.buf(i);
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
if (!data)
throw new Error('Bad data chunk.');
2017-03-09 14:06:13 -08:00
2017-10-26 11:58:50 -07:00
socket.filter.add(data);
2016-08-22 15:24:15 -07:00
2017-10-22 05:44:16 -07:00
if (this.node.spv)
this.pool.watch(data);
}
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
return null;
});
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
socket.hook('reset filter', () => {
socket.filter = null;
return null;
});
2016-11-19 18:00:13 -08:00
2017-10-22 05:44:16 -07:00
socket.hook('estimate fee', (...args) => {
const valid = new Validator(args);
const blocks = valid.u32(0);
2017-10-22 05:44:16 -07:00
if (!this.fees)
return this.network.feeRate;
2017-10-22 05:44:16 -07:00
return this.fees.estimateFee(blocks);
});
2016-08-12 14:25:47 -07:00
2017-10-22 05:44:16 -07:00
socket.hook('send', (...args) => {
const valid = new Validator(args);
const data = valid.buf(0);
2016-08-12 14:25:47 -07:00
2017-10-22 05:44:16 -07:00
if (!data)
throw new Error('Invalid parameter.');
2016-06-02 06:19:47 -07:00
2017-10-22 05:44:16 -07:00
const tx = TX.fromRaw(data);
2017-02-28 22:43:56 -08:00
2017-10-26 15:35:23 -07:00
this.node.relay(tx);
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
return null;
});
2017-02-28 22:43:56 -08:00
2017-10-22 05:44:16 -07:00
socket.hook('rescan', (...args) => {
const valid = new Validator(args);
const start = valid.uintrhash(0);
2017-01-26 01:37:27 -08:00
2017-10-22 05:44:16 -07:00
if (start == null)
throw new Error('Invalid parameter.');
2017-10-22 05:44:16 -07:00
return this.scan(socket, start);
});
}
2017-10-22 05:44:16 -07:00
/**
* Bind to chain events.
* @private
*/
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
initSockets() {
const pool = this.mempool || this.pool;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.chain.on('connect', (entry, block, view) => {
const sockets = this.channel('chain');
2017-03-10 05:32:42 -08:00
2017-10-22 05:44:16 -07:00
if (!sockets)
return;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
const raw = entry.toRaw();
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.to('chain', 'chain connect', raw);
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
for (const socket of sockets) {
const txs = this.filterBlock(socket, block);
socket.fire('block connect', raw, txs);
}
});
2016-03-10 02:15:29 -08:00
2017-10-22 05:44:16 -07:00
this.chain.on('disconnect', (entry, block, view) => {
const sockets = this.channel('chain');
2017-10-22 05:44:16 -07:00
if (!sockets)
return;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
const raw = entry.toRaw();
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.to('chain', 'chain disconnect', raw);
this.to('chain', 'block disconnect', raw);
});
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.chain.on('reset', (tip) => {
const sockets = this.channel('chain');
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
if (!sockets)
return;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.to('chain', 'chain reset', tip.toRaw());
});
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
pool.on('tx', (tx) => {
const sockets = this.channel('mempool');
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
if (!sockets)
return;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
const raw = tx.toRaw();
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
for (const socket of sockets) {
if (!this.filterTX(socket, tx))
continue;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
socket.fire('tx', raw);
}
});
}
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
/**
* Filter block by socket.
* @private
* @param {WebSocket} socket
* @param {Block} block
* @returns {TX[]}
*/
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
filterBlock(socket, block) {
if (!socket.filter)
return [];
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
const txs = [];
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
for (const tx of block.txs) {
if (this.filterTX(socket, tx))
txs.push(tx.toRaw());
2017-03-09 14:06:13 -08:00
}
2017-03-10 05:32:42 -08:00
2017-10-22 05:44:16 -07:00
return txs;
2017-03-09 14:06:13 -08:00
}
2017-10-22 05:44:16 -07:00
/**
* Filter transaction by socket.
* @private
* @param {WebSocket} socket
* @param {TX} tx
* @returns {Boolean}
*/
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
filterTX(socket, tx) {
if (!socket.filter)
return false;
2017-03-09 14:06:13 -08:00
2018-01-08 01:19:41 -08:00
return tx.test(socket.filter);
2017-10-22 05:44:16 -07:00
}
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
/**
* Scan using a socket's filter.
* @private
* @param {WebSocket} socket
* @param {Hash} start
* @returns {Promise}
*/
2017-03-10 05:32:42 -08:00
2017-10-22 05:44:16 -07:00
async scan(socket, start) {
await this.node.scan(start, socket.filter, (entry, txs) => {
const block = entry.toRaw();
const raw = [];
2017-03-10 05:32:42 -08:00
2017-10-22 05:44:16 -07:00
for (const tx of txs)
raw.push(tx.toRaw());
2017-03-09 14:06:13 -08:00
2017-10-26 15:35:23 -07:00
return socket.call('block rescan', block, raw);
2017-10-22 05:44:16 -07:00
});
return null;
}
}
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
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));
2017-11-01 15:41:32 -07:00
this.apiHash = sha256.digest(Buffer.from(this.apiKey, 'ascii'));
2017-10-22 05:44:16 -07:00
this.noAuth = false;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.prefix = null;
this.host = '127.0.0.1';
this.port = 8080;
this.ssl = false;
this.keyFile = null;
this.certFile = null;
2017-03-09 14:06:13 -08:00
2017-10-22 05:44:16 -07:00
this.fromOptions(options);
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
/**
* Inject properties from object.
* @private
* @param {Object} options
* @returns {HTTPOptions}
*/
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
fromOptions(options) {
assert(options);
assert(options.node && typeof options.node === 'object',
'HTTP Server requires a Node.');
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
this.node = options.node;
this.network = options.node.network;
this.logger = options.node.logger;
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
this.port = this.network.rpcPort;
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.logger != null) {
assert(typeof options.logger === 'object');
this.logger = options.logger;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
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 256 bytes.');
this.apiKey = options.apiKey;
2017-11-01 15:41:32 -07:00
this.apiHash = sha256.digest(Buffer.from(this.apiKey, 'ascii'));
2017-10-22 05:44:16 -07:00
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.noAuth != null) {
assert(typeof options.noAuth === 'boolean');
this.noAuth = options.noAuth;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
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');
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.host != null) {
assert(typeof options.host === 'string');
this.host = options.host;
}
2017-10-22 05:44:16 -07:00
if (options.port != null) {
2017-10-26 04:07:36 -07:00
assert((options.port & 0xffff) === options.port,
'Port must be a number.');
2017-10-22 05:44:16 -07:00
this.port = options.port;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.ssl != null) {
assert(typeof options.ssl === 'boolean');
this.ssl = options.ssl;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.keyFile != null) {
assert(typeof options.keyFile === 'string');
this.keyFile = options.keyFile;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
if (options.certFile != null) {
assert(typeof options.certFile === 'string');
this.certFile = options.certFile;
}
2017-10-22 05:44:16 -07:00
// 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;
}
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
return this;
}
2017-10-22 05:44:16 -07:00
/**
* Instantiate http options from object.
* @param {Object} options
* @returns {HTTPOptions}
*/
2017-01-14 14:41:55 -08:00
2017-10-22 05:44:16 -07:00
static fromOptions(options) {
return new HTTPOptions().fromOptions(options);
}
}
2017-01-14 14:41:55 -08:00
2016-08-13 01:23:25 -07:00
/*
* Helpers
*/
function enforce(value, msg) {
if (!value) {
2017-07-27 09:27:00 -07:00
const err = new Error(msg);
err.statusCode = 400;
throw err;
}
}
2016-05-15 18:07:06 -07:00
/*
* Expose
*/
2017-10-26 12:31:08 -07:00
module.exports = HTTP;