http: stop using socket.io.
This commit is contained in:
parent
f617283539
commit
649f8cc171
16
bin/cli
16
bin/cli
@ -534,7 +534,7 @@ CLI.prototype.rpc = async function rpc() {
|
||||
|
||||
CLI.prototype.handleWallet = async function handleWallet() {
|
||||
this.wallet = new Wallet({
|
||||
uri: this.config.str(['url', 'uri']),
|
||||
url: this.config.str(['url', 'uri']),
|
||||
apiKey: this.config.str('api-key'),
|
||||
network: this.config.str('network'),
|
||||
id: this.config.str('id', 'primary'),
|
||||
@ -693,7 +693,7 @@ CLI.prototype.handleWallet = async function handleWallet() {
|
||||
|
||||
CLI.prototype.handleNode = async function handleNode() {
|
||||
this.client = new Client({
|
||||
uri: this.config.str(['url', 'uri']),
|
||||
url: this.config.str(['url', 'uri']),
|
||||
apiKey: this.config.str('api-key'),
|
||||
network: this.config.str('network')
|
||||
});
|
||||
@ -776,14 +776,12 @@ CLI.prototype.open = async function open() {
|
||||
}
|
||||
};
|
||||
|
||||
CLI.prototype.destroy = function destroy() {
|
||||
if (this.wallet)
|
||||
this.wallet.client.destroy();
|
||||
CLI.prototype.destroy = async function destroy() {
|
||||
if (this.wallet && this.wallet.opened)
|
||||
await this.wallet.close();
|
||||
|
||||
if (this.client)
|
||||
this.client.destroy();
|
||||
|
||||
return Promise.resolve();
|
||||
if (this.client && this.client.opened)
|
||||
await this.client.close();
|
||||
};
|
||||
|
||||
(async () => {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
1751
lib/http/base.js
1751
lib/http/base.js
File diff suppressed because it is too large
Load Diff
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
1240
lib/http/client.js
1240
lib/http/client.js
File diff suppressed because it is too large
Load Diff
@ -11,11 +11,7 @@
|
||||
* @module http
|
||||
*/
|
||||
|
||||
exports.Base = require('./base');
|
||||
exports.Client = require('./client');
|
||||
exports.request = require('./request');
|
||||
exports.RPCBase = require('./rpcbase');
|
||||
exports.RPCClient = require('./rpcclient');
|
||||
exports.RPC = require('./rpc');
|
||||
exports.Server = require('./server');
|
||||
exports.Wallet = require('./wallet');
|
||||
|
||||
@ -1,458 +0,0 @@
|
||||
/*!
|
||||
* request.js - http request for bcoin
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const EventEmitter = require('events');
|
||||
const URL = require('url');
|
||||
const qs = require('querystring');
|
||||
const fetch = global.fetch;
|
||||
const FetchHeaders = global.Headers;
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)'
|
||||
+ ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36';
|
||||
|
||||
/**
|
||||
* Request Options
|
||||
* @constructor
|
||||
* @ignore
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
function RequestOptions(options) {
|
||||
if (!(this instanceof RequestOptions))
|
||||
return new RequestOptions(options);
|
||||
|
||||
this.uri = 'http://localhost:80/';
|
||||
this.host = 'localhost';
|
||||
this.path = '/';
|
||||
this.port = 80;
|
||||
this.ssl = false;
|
||||
this.method = 'GET';
|
||||
this.strictSSL = true;
|
||||
this.agent = USER_AGENT;
|
||||
|
||||
this.type = null;
|
||||
this.expect = null;
|
||||
this.query = null;
|
||||
this.body = null;
|
||||
this.auth = null;
|
||||
this.limit = 10 << 20;
|
||||
this.timeout = 5000;
|
||||
this.buffer = false;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
RequestOptions.prototype.setURI = function setURI(uri) {
|
||||
assert(typeof uri === 'string');
|
||||
|
||||
if (!/:\/\//.test(uri))
|
||||
uri = (this.ssl ? 'https://' : 'http://') + uri;
|
||||
|
||||
uri = URL.parse(uri);
|
||||
|
||||
assert(uri.protocol === 'http:' || uri.protocol === 'https:');
|
||||
|
||||
this.uri = uri;
|
||||
this.ssl = uri.protocol === 'https:';
|
||||
|
||||
if (uri.search)
|
||||
this.query = qs.parse(uri.search);
|
||||
|
||||
this.host = uri.hostname;
|
||||
this.path = uri.pathname;
|
||||
this.port = uri.port || (this.ssl ? 443 : 80);
|
||||
|
||||
if (uri.auth) {
|
||||
const parts = uri.auth.split(':');
|
||||
this.auth = {
|
||||
username: parts[0] || '',
|
||||
password: parts[1] || ''
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
RequestOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
if (options.ssl != null) {
|
||||
assert(typeof options.ssl === 'boolean');
|
||||
this.ssl = options.ssl;
|
||||
}
|
||||
|
||||
if (options.uri != null)
|
||||
this.setURI(options.uri);
|
||||
|
||||
if (options.url != null)
|
||||
this.setURI(options.url);
|
||||
|
||||
if (options.method != null) {
|
||||
assert(typeof options.method === 'string');
|
||||
this.method = options.method.toUpperCase();
|
||||
}
|
||||
|
||||
if (options.strictSSL != null) {
|
||||
assert(typeof options.strictSSL === 'boolean');
|
||||
this.strictSSL = options.strictSSL;
|
||||
}
|
||||
|
||||
if (options.agent != null) {
|
||||
assert(typeof options.agent === 'string');
|
||||
this.agent = options.agent;
|
||||
}
|
||||
|
||||
if (options.auth != null) {
|
||||
assert(typeof options.auth === 'object');
|
||||
assert(typeof options.auth.username === 'string');
|
||||
assert(typeof options.auth.password === 'string');
|
||||
this.auth = options.auth;
|
||||
}
|
||||
|
||||
if (options.query != null) {
|
||||
if (typeof options.query === 'string') {
|
||||
this.query = qs.stringify(options.query);
|
||||
} else {
|
||||
assert(typeof options.query === 'object');
|
||||
this.query = options.query;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.json != null) {
|
||||
assert(typeof options.json === 'object');
|
||||
this.body = Buffer.from(JSON.stringify(options.json), 'utf8');
|
||||
this.type = 'json';
|
||||
}
|
||||
|
||||
if (options.form != null) {
|
||||
assert(typeof options.form === 'object');
|
||||
this.body = Buffer.from(qs.stringify(options.form), 'utf8');
|
||||
this.type = 'form';
|
||||
}
|
||||
|
||||
if (options.type != null) {
|
||||
assert(typeof options.type === 'string');
|
||||
assert(getType(options.type));
|
||||
this.type = options.type;
|
||||
}
|
||||
|
||||
if (options.expect != null) {
|
||||
assert(typeof options.expect === 'string');
|
||||
assert(getType(options.expect));
|
||||
this.expect = options.expect;
|
||||
}
|
||||
|
||||
if (options.body != null) {
|
||||
if (typeof options.body === 'string') {
|
||||
this.body = Buffer.from(options.body, 'utf8');
|
||||
} else {
|
||||
assert(Buffer.isBuffer(options.body));
|
||||
this.body = options.body;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.timeout != null) {
|
||||
assert(typeof options.timeout === 'number');
|
||||
this.timeout = options.timeout;
|
||||
}
|
||||
|
||||
if (options.limit != null) {
|
||||
assert(typeof options.limit === 'number');
|
||||
this.limit = options.limit;
|
||||
}
|
||||
|
||||
if (options.buffer != null) {
|
||||
assert(typeof options.buffer === 'boolean');
|
||||
this.buffer = options.buffer;
|
||||
}
|
||||
};
|
||||
|
||||
RequestOptions.prototype.isExpected = function isExpected(type) {
|
||||
if (!this.expect)
|
||||
return true;
|
||||
|
||||
return this.expect === type;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.isOverflow = function isOverflow(hdr) {
|
||||
if (!hdr)
|
||||
return false;
|
||||
|
||||
if (!this.buffer)
|
||||
return false;
|
||||
|
||||
const length = parseInt(hdr, 10);
|
||||
|
||||
if (!isFinite(length))
|
||||
return true;
|
||||
|
||||
return length > this.limit;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.getHeaders = function getHeaders() {
|
||||
const headers = new FetchHeaders();
|
||||
|
||||
headers.append('User-Agent', this.agent);
|
||||
|
||||
if (this.type)
|
||||
headers.append('Content-Type', getType(this.type));
|
||||
|
||||
if (this.body)
|
||||
headers.append('Content-Length', this.body.length.toString(10));
|
||||
|
||||
if (this.auth) {
|
||||
const auth = `${this.auth.username}:${this.auth.password}`;
|
||||
const data = Buffer.from(auth, 'utf8');
|
||||
headers.append('Authorization', `Basic ${data.toString('base64')}`);
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.toURL = function toURL() {
|
||||
let url = '';
|
||||
|
||||
if (this.ssl)
|
||||
url += 'https://';
|
||||
else
|
||||
url += 'http://';
|
||||
|
||||
url += this.host;
|
||||
url += ':' + this.port;
|
||||
url += this.path;
|
||||
|
||||
if (this.query)
|
||||
url += '?' + qs.stringify(this.query);
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.toHTTP = function toHTTP() {
|
||||
return {
|
||||
method: this.method,
|
||||
headers: this.getHeaders(),
|
||||
body: this.body.buffer,
|
||||
mode: 'cors',
|
||||
credentials: 'include',
|
||||
cache: 'no-cache',
|
||||
redirect: 'follow',
|
||||
referrer: 'no-referrer'
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Response
|
||||
* @constructor
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
function Response() {
|
||||
this.statusCode = 0;
|
||||
this.headers = Object.create(null);
|
||||
this.type = 'bin';
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
Response.fromFetch = function fromFetch(response) {
|
||||
const res = new Response();
|
||||
|
||||
res.statusCode = response.status;
|
||||
|
||||
for (const [key, value] of response.headers.entries())
|
||||
res.headers[key.toLowerCase()] = value;
|
||||
|
||||
const contentType = res.headers['content-type'];
|
||||
|
||||
res.type = parseType(contentType);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make an HTTP request.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async function _request(options) {
|
||||
if (typeof fetch !== 'function')
|
||||
throw new Error('Fetch API not available.');
|
||||
|
||||
const opt = new RequestOptions(options);
|
||||
const response = await fetch(opt.toURL(), opt.toHTTP());
|
||||
const res = Response.fromFetch(response);
|
||||
|
||||
if (!opt.isExpected(res.type))
|
||||
throw new Error('Wrong content-type for response.');
|
||||
|
||||
const length = res.headers['content-length'];
|
||||
|
||||
if (opt.isOverflow(length))
|
||||
throw new Error('Response exceeded limit.');
|
||||
|
||||
if (opt.buffer) {
|
||||
switch (res.type) {
|
||||
case 'bin': {
|
||||
const data = await response.arrayBuffer();
|
||||
res.body = Buffer.from(data.buffer);
|
||||
if (opt.limit && res.body.length > opt.limit)
|
||||
throw new Error('Response exceeded limit.');
|
||||
break;
|
||||
}
|
||||
case 'json': {
|
||||
res.body = await response.json();
|
||||
break;
|
||||
}
|
||||
case 'form': {
|
||||
const data = await response.formData();
|
||||
res.body = Object.create(null);
|
||||
for (const [key, value] of data.entries())
|
||||
res.body[key] = value;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
res.body = await response.text();
|
||||
if (opt.limit && res.body.length > opt.limit)
|
||||
throw new Error('Response exceeded limit.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.body = await response.arrayBuffer();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP request.
|
||||
* @alias module:http.request
|
||||
* @param {Object} options
|
||||
* @param {String} options.uri
|
||||
* @param {Object?} options.query
|
||||
* @param {Object?} options.body
|
||||
* @param {Object?} options.json
|
||||
* @param {Object?} options.form
|
||||
* @param {String?} options.type - One of `"json"`,
|
||||
* `"form"`, `"text"`, or `"bin"`.
|
||||
* @param {String?} options.agent - User agent string.
|
||||
* @param {Object?} [options.strictSSL=true] - Whether to accept bad certs.
|
||||
* @param {Object?} options.method - HTTP method.
|
||||
* @param {Object?} options.auth
|
||||
* @param {String?} options.auth.username
|
||||
* @param {String?} options.auth.password
|
||||
* @param {String?} options.expect - Type to expect (see options.type).
|
||||
* Error will be returned if the response is not of this type.
|
||||
* @param {Number?} options.limit - Byte limit on response.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async function request(options) {
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
options.buffer = true;
|
||||
|
||||
return _request(options);
|
||||
}
|
||||
|
||||
request.stream = function stream(options) {
|
||||
const s = new EventEmitter();
|
||||
|
||||
s.write = (data) => {
|
||||
options.body = data;
|
||||
return true;
|
||||
};
|
||||
|
||||
s.end = () => {
|
||||
_request(options).then((res) => {
|
||||
s.emit('headers', res.headers);
|
||||
s.emit('type', res.type);
|
||||
s.emit('response', res);
|
||||
s.emit('data', res.body);
|
||||
s.emit('end');
|
||||
s.emit('close');
|
||||
}).catch((err) => {
|
||||
s.emit('error', err);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parseType(hdr) {
|
||||
let type = hdr || '';
|
||||
type = type.split(';')[0];
|
||||
type = type.toLowerCase();
|
||||
type = type.trim();
|
||||
|
||||
switch (type) {
|
||||
case 'text/x-json':
|
||||
case 'application/json':
|
||||
return 'json';
|
||||
case 'application/x-www-form-urlencoded':
|
||||
return 'form';
|
||||
case 'text/html':
|
||||
case 'application/xhtml+xml':
|
||||
return 'html';
|
||||
case 'text/xml':
|
||||
case 'application/xml':
|
||||
return 'xml';
|
||||
case 'text/javascript':
|
||||
case 'application/javascript':
|
||||
return 'js';
|
||||
case 'text/css':
|
||||
return 'css';
|
||||
case 'text/plain':
|
||||
return 'txt';
|
||||
case 'application/octet-stream':
|
||||
return 'bin';
|
||||
default:
|
||||
return 'bin';
|
||||
}
|
||||
}
|
||||
|
||||
function getType(type) {
|
||||
switch (type) {
|
||||
case 'json':
|
||||
return 'application/json; charset=utf-8';
|
||||
case 'form':
|
||||
return 'application/x-www-form-urlencoded; charset=utf-8';
|
||||
case 'html':
|
||||
return 'text/html; charset=utf-8';
|
||||
case 'xml':
|
||||
return 'application/xml; charset=utf-8';
|
||||
case 'js':
|
||||
return 'application/javascript; charset=utf-8';
|
||||
case 'css':
|
||||
return 'text/css; charset=utf-8';
|
||||
case 'txt':
|
||||
return 'text/plain; charset=utf-8';
|
||||
case 'bin':
|
||||
return 'application/octet-stream';
|
||||
default:
|
||||
throw new Error(`Unknown type: ${type}.`);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = request;
|
||||
@ -1,672 +0,0 @@
|
||||
/*!
|
||||
* request.js - http request for bcoin
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const {Stream} = require('stream');
|
||||
|
||||
/*
|
||||
* Lazily Loaded
|
||||
*/
|
||||
|
||||
let url = null;
|
||||
let qs = null;
|
||||
let http = null;
|
||||
let https = null;
|
||||
let StringDecoder = null;
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)'
|
||||
+ ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36';
|
||||
|
||||
/**
|
||||
* Request Options
|
||||
* @constructor
|
||||
* @ignore
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
function RequestOptions(options) {
|
||||
if (!(this instanceof RequestOptions))
|
||||
return new RequestOptions(options);
|
||||
|
||||
this.uri = 'http://localhost:80/';
|
||||
this.host = 'localhost';
|
||||
this.path = '/';
|
||||
this.port = 80;
|
||||
this.ssl = false;
|
||||
this.method = 'GET';
|
||||
this.strictSSL = true;
|
||||
this.pool = false;
|
||||
this.agent = USER_AGENT;
|
||||
|
||||
this.type = null;
|
||||
this.expect = null;
|
||||
this.query = null;
|
||||
this.body = null;
|
||||
this.auth = null;
|
||||
this.limit = 10 << 20;
|
||||
this.maxRedirects = 5;
|
||||
this.timeout = 5000;
|
||||
this.buffer = false;
|
||||
this.headers = null;
|
||||
|
||||
// Hack
|
||||
ensureRequires();
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
RequestOptions.prototype.setURI = function setURI(uri) {
|
||||
assert(typeof uri === 'string');
|
||||
|
||||
if (!/:\/\//.test(uri))
|
||||
uri = (this.ssl ? 'https://' : 'http://') + uri;
|
||||
|
||||
uri = url.parse(uri);
|
||||
|
||||
assert(uri.protocol === 'http:' || uri.protocol === 'https:');
|
||||
|
||||
this.uri = uri;
|
||||
this.ssl = uri.protocol === 'https:';
|
||||
|
||||
if (uri.search)
|
||||
this.query = qs.parse(uri.search);
|
||||
|
||||
this.host = uri.hostname;
|
||||
this.path = uri.pathname;
|
||||
this.port = uri.port || (this.ssl ? 443 : 80);
|
||||
|
||||
if (uri.auth) {
|
||||
const parts = uri.auth.split(':');
|
||||
this.auth = {
|
||||
username: parts[0] || '',
|
||||
password: parts[1] || ''
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
RequestOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
if (options.ssl != null) {
|
||||
assert(typeof options.ssl === 'boolean');
|
||||
this.ssl = options.ssl;
|
||||
}
|
||||
|
||||
if (options.uri != null)
|
||||
this.setURI(options.uri);
|
||||
|
||||
if (options.url != null)
|
||||
this.setURI(options.url);
|
||||
|
||||
if (options.method != null) {
|
||||
assert(typeof options.method === 'string');
|
||||
this.method = options.method.toUpperCase();
|
||||
}
|
||||
|
||||
if (options.strictSSL != null) {
|
||||
assert(typeof options.strictSSL === 'boolean');
|
||||
this.strictSSL = options.strictSSL;
|
||||
}
|
||||
|
||||
if (options.pool != null) {
|
||||
assert(typeof options.pool === 'boolean');
|
||||
this.pool = options.pool;
|
||||
}
|
||||
|
||||
if (options.agent != null) {
|
||||
assert(typeof options.agent === 'string');
|
||||
this.agent = options.agent;
|
||||
}
|
||||
|
||||
if (options.auth != null) {
|
||||
assert(typeof options.auth === 'object');
|
||||
assert(typeof options.auth.username === 'string');
|
||||
assert(typeof options.auth.password === 'string');
|
||||
this.auth = options.auth;
|
||||
}
|
||||
|
||||
if (options.query != null) {
|
||||
if (typeof options.query === 'string') {
|
||||
this.query = qs.stringify(options.query);
|
||||
} else {
|
||||
assert(typeof options.query === 'object');
|
||||
this.query = options.query;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.json != null) {
|
||||
assert(typeof options.json === 'object');
|
||||
this.body = Buffer.from(JSON.stringify(options.json), 'utf8');
|
||||
this.type = 'json';
|
||||
}
|
||||
|
||||
if (options.form != null) {
|
||||
assert(typeof options.form === 'object');
|
||||
this.body = Buffer.from(qs.stringify(options.form), 'utf8');
|
||||
this.type = 'form';
|
||||
}
|
||||
|
||||
if (options.type != null) {
|
||||
assert(typeof options.type === 'string');
|
||||
assert(getType(options.type));
|
||||
this.type = options.type;
|
||||
}
|
||||
|
||||
if (options.expect != null) {
|
||||
assert(typeof options.expect === 'string');
|
||||
assert(getType(options.expect));
|
||||
this.expect = options.expect;
|
||||
}
|
||||
|
||||
if (options.body != null) {
|
||||
if (typeof options.body === 'string') {
|
||||
this.body = Buffer.from(options.body, 'utf8');
|
||||
} else {
|
||||
assert(Buffer.isBuffer(options.body));
|
||||
this.body = options.body;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.timeout != null) {
|
||||
assert(typeof options.timeout === 'number');
|
||||
this.timeout = options.timeout;
|
||||
}
|
||||
|
||||
if (options.limit != null) {
|
||||
assert(typeof options.limit === 'number');
|
||||
this.limit = options.limit;
|
||||
}
|
||||
|
||||
if (options.maxRedirects != null) {
|
||||
assert(typeof options.maxRedirects === 'number');
|
||||
this.maxRedirects = options.maxRedirects;
|
||||
}
|
||||
|
||||
if (options.buffer != null) {
|
||||
assert(typeof options.buffer === 'boolean');
|
||||
this.buffer = options.buffer;
|
||||
}
|
||||
|
||||
if (options.headers != null) {
|
||||
assert(typeof options.headers === 'object');
|
||||
this.headers = options.headers;
|
||||
}
|
||||
};
|
||||
|
||||
RequestOptions.prototype.isExpected = function isExpected(type) {
|
||||
if (!this.expect)
|
||||
return true;
|
||||
|
||||
return this.expect === type;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.isOverflow = function isOverflow(hdr) {
|
||||
if (!hdr)
|
||||
return false;
|
||||
|
||||
if (!this.buffer)
|
||||
return false;
|
||||
|
||||
const length = parseInt(hdr, 10);
|
||||
|
||||
if (!isFinite(length))
|
||||
return true;
|
||||
|
||||
return length > this.limit;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.getBackend = function getBackend() {
|
||||
ensureRequires(this.ssl);
|
||||
return this.ssl ? https : http;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.getHeaders = function getHeaders() {
|
||||
if (this.headers)
|
||||
return this.headers;
|
||||
|
||||
const headers = Object.create(null);
|
||||
|
||||
headers['User-Agent'] = this.agent;
|
||||
|
||||
if (this.type)
|
||||
headers['Content-Type'] = getType(this.type);
|
||||
|
||||
if (this.body)
|
||||
headers['Content-Length'] = this.body.length.toString(10);
|
||||
|
||||
if (this.auth) {
|
||||
const auth = `${this.auth.username}:${this.auth.password}`;
|
||||
const data = Buffer.from(auth, 'utf8');
|
||||
headers['Authorization'] = `Basic ${data.toString('base64')}`;
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
RequestOptions.prototype.toHTTP = function toHTTP() {
|
||||
let query = '';
|
||||
|
||||
if (this.query)
|
||||
query = '?' + qs.stringify(this.query);
|
||||
|
||||
return {
|
||||
method: this.method,
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
path: this.path + query,
|
||||
headers: this.getHeaders(),
|
||||
agent: this.pool ? null : false,
|
||||
rejectUnauthorized: this.strictSSL
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Request
|
||||
* @alias module:http.Request
|
||||
* @constructor
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
function Request(options) {
|
||||
if (!(this instanceof Request))
|
||||
return new Request(options);
|
||||
|
||||
Stream.call(this);
|
||||
|
||||
this.options = new RequestOptions(options);
|
||||
this.request = null;
|
||||
this.response = null;
|
||||
this.statusCode = 0;
|
||||
this.headers = null;
|
||||
this.type = 'bin';
|
||||
this.redirects = 0;
|
||||
this.timeout = null;
|
||||
this.finished = false;
|
||||
|
||||
this.onResponse = this._onResponse.bind(this);
|
||||
this.onData = this._onData.bind(this);
|
||||
this.onEnd = this._onEnd.bind(this);
|
||||
|
||||
this.total = 0;
|
||||
this.decoder = null;
|
||||
this.body = null;
|
||||
this.buffer = null;
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Request.prototype, Stream.prototype);
|
||||
|
||||
Request.prototype.startTimeout = function startTimeout() {
|
||||
if (!this.options.timeout)
|
||||
return;
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.finish(new Error('Request timed out.'));
|
||||
}, this.options.timeout);
|
||||
};
|
||||
|
||||
Request.prototype.stopTimeout = function stopTimeout() {
|
||||
if (this.timeout != null) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
};
|
||||
|
||||
Request.prototype.cleanup = function cleanup() {
|
||||
this.stopTimeout();
|
||||
|
||||
if (this.request) {
|
||||
this.request.removeListener('response', this.onResponse);
|
||||
this.request.removeListener('error', this.onEnd);
|
||||
this.request.addListener('error', () => {});
|
||||
}
|
||||
|
||||
if (this.response) {
|
||||
this.response.removeListener('data', this.onData);
|
||||
this.response.removeListener('error', this.onEnd);
|
||||
this.response.removeListener('end', this.onEnd);
|
||||
this.response.addListener('error', () => {});
|
||||
}
|
||||
};
|
||||
|
||||
Request.prototype.close = function close() {
|
||||
if (this.request) {
|
||||
try {
|
||||
this.request.abort();
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.response) {
|
||||
try {
|
||||
this.response.destroy();
|
||||
} catch (e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanup();
|
||||
|
||||
this.request = null;
|
||||
this.response = null;
|
||||
};
|
||||
|
||||
Request.prototype.destroy = function destroy() {
|
||||
this.close();
|
||||
};
|
||||
|
||||
Request.prototype.start = function start() {
|
||||
const backend = this.options.getBackend();
|
||||
const options = this.options.toHTTP();
|
||||
|
||||
this.startTimeout();
|
||||
|
||||
this.request = backend.request(options);
|
||||
this.response = null;
|
||||
|
||||
if (this.options.body)
|
||||
this.request.write(this.options.body);
|
||||
|
||||
this.request.on('response', this.onResponse);
|
||||
this.request.on('error', this.onEnd);
|
||||
};
|
||||
|
||||
Request.prototype.write = function write(data) {
|
||||
return this.request.write(data);
|
||||
};
|
||||
|
||||
Request.prototype.end = function end() {
|
||||
return this.request.end();
|
||||
};
|
||||
|
||||
Request.prototype.finish = function finish(err) {
|
||||
if (this.finished)
|
||||
return;
|
||||
|
||||
this.finished = true;
|
||||
|
||||
if (err) {
|
||||
this.destroy();
|
||||
this.emit('error', err);
|
||||
return;
|
||||
}
|
||||
|
||||
this.cleanup();
|
||||
|
||||
if (this.options.buffer) {
|
||||
assert(this.buffer != null);
|
||||
switch (this.type) {
|
||||
case 'bin': {
|
||||
this.body = Buffer.concat(this.buffer);
|
||||
this.buffer = null;
|
||||
break;
|
||||
}
|
||||
case 'json': {
|
||||
const buffer = this.buffer.trim();
|
||||
|
||||
this.buffer = null;
|
||||
|
||||
if (buffer.length === 0)
|
||||
break;
|
||||
|
||||
let body;
|
||||
try {
|
||||
body = JSON.parse(buffer);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body || typeof body !== 'object') {
|
||||
this.emit('error', new Error('JSON body is a non-object.'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.body = body;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'form': {
|
||||
const buffer = this.buffer;
|
||||
|
||||
this.buffer = null;
|
||||
|
||||
try {
|
||||
this.body = qs.parse(buffer);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.body = this.buffer;
|
||||
this.buffer = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('end');
|
||||
this.emit('close');
|
||||
};
|
||||
|
||||
Request.prototype._onResponse = function _onResponse(response) {
|
||||
const location = response.headers['location'];
|
||||
|
||||
if (location) {
|
||||
if (++this.redirects > this.options.maxRedirects) {
|
||||
this.finish(new Error('Too many redirects.'));
|
||||
return;
|
||||
}
|
||||
this.close();
|
||||
this.options.setURI(location);
|
||||
this.start();
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = response.headers['content-type'];
|
||||
const type = parseType(contentType);
|
||||
|
||||
if (!this.options.isExpected(type)) {
|
||||
this.finish(new Error('Wrong content-type for response.'));
|
||||
return;
|
||||
}
|
||||
|
||||
const length = response.headers['content-length'];
|
||||
|
||||
if (this.options.isOverflow(length)) {
|
||||
this.finish(new Error('Response exceeded limit.'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.response = response;
|
||||
this.statusCode = response.statusCode;
|
||||
this.headers = response.headers;
|
||||
this.type = type;
|
||||
|
||||
this.response.on('data', this.onData);
|
||||
this.response.on('error', this.onEnd);
|
||||
this.response.on('end', this.onEnd);
|
||||
|
||||
this.emit('headers', response.headers);
|
||||
this.emit('type', this.type);
|
||||
this.emit('response', response);
|
||||
|
||||
if (this.options.buffer) {
|
||||
if (this.type !== 'bin') {
|
||||
this.decoder = new StringDecoder('utf8');
|
||||
this.buffer = '';
|
||||
} else {
|
||||
this.buffer = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Request.prototype._onData = function _onData(data) {
|
||||
this.total += data.length;
|
||||
|
||||
this.emit('data', data);
|
||||
|
||||
if (this.options.buffer) {
|
||||
if (this.options.limit) {
|
||||
if (this.total > this.options.limit) {
|
||||
this.finish(new Error('Response exceeded limit.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.decoder) {
|
||||
this.buffer += this.decoder.write(data);
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer.push(data);
|
||||
}
|
||||
};
|
||||
|
||||
Request.prototype._onEnd = function _onEnd(err) {
|
||||
this.finish(err);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make an HTTP request.
|
||||
* @alias module:http.request
|
||||
* @param {Object} options
|
||||
* @param {String} options.uri
|
||||
* @param {Object?} options.query
|
||||
* @param {Object?} options.body
|
||||
* @param {Object?} options.json
|
||||
* @param {Object?} options.form
|
||||
* @param {String?} options.type - One of `"json"`,
|
||||
* `"form"`, `"text"`, or `"bin"`.
|
||||
* @param {String?} options.agent - User agent string.
|
||||
* @param {Object?} [options.strictSSL=true] - Whether to accept bad certs.
|
||||
* @param {Object?} options.method - HTTP method.
|
||||
* @param {Object?} options.auth
|
||||
* @param {String?} options.auth.username
|
||||
* @param {String?} options.auth.password
|
||||
* @param {String?} options.expect - Type to expect (see options.type).
|
||||
* Error will be returned if the response is not of this type.
|
||||
* @param {Number?} options.limit - Byte limit on response.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
function request(options) {
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
options.buffer = true;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = new Request(options);
|
||||
|
||||
req.on('error', err => reject(err));
|
||||
req.on('end', () => resolve(req));
|
||||
|
||||
req.start();
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
request.stream = function stream(options) {
|
||||
const req = new Request(options);
|
||||
req.start();
|
||||
return req;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parseType(hdr) {
|
||||
let type = hdr || '';
|
||||
type = type.split(';')[0];
|
||||
type = type.toLowerCase();
|
||||
type = type.trim();
|
||||
|
||||
switch (type) {
|
||||
case 'text/x-json':
|
||||
case 'application/json':
|
||||
return 'json';
|
||||
case 'application/x-www-form-urlencoded':
|
||||
return 'form';
|
||||
case 'text/html':
|
||||
case 'application/xhtml+xml':
|
||||
return 'html';
|
||||
case 'text/xml':
|
||||
case 'application/xml':
|
||||
return 'xml';
|
||||
case 'text/javascript':
|
||||
case 'application/javascript':
|
||||
return 'js';
|
||||
case 'text/css':
|
||||
return 'css';
|
||||
case 'text/plain':
|
||||
return 'txt';
|
||||
case 'application/octet-stream':
|
||||
return 'bin';
|
||||
default:
|
||||
return 'bin';
|
||||
}
|
||||
}
|
||||
|
||||
function getType(type) {
|
||||
switch (type) {
|
||||
case 'json':
|
||||
return 'application/json; charset=utf-8';
|
||||
case 'form':
|
||||
return 'application/x-www-form-urlencoded; charset=utf-8';
|
||||
case 'html':
|
||||
return 'text/html; charset=utf-8';
|
||||
case 'xml':
|
||||
return 'application/xml; charset=utf-8';
|
||||
case 'js':
|
||||
return 'application/javascript; charset=utf-8';
|
||||
case 'css':
|
||||
return 'text/css; charset=utf-8';
|
||||
case 'txt':
|
||||
return 'text/plain; charset=utf-8';
|
||||
case 'bin':
|
||||
return 'application/octet-stream';
|
||||
default:
|
||||
throw new Error(`Unknown type: ${type}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureRequires(ssl) {
|
||||
if (!url)
|
||||
url = require('url');
|
||||
|
||||
if (!qs)
|
||||
qs = require('querystring');
|
||||
|
||||
if (!http)
|
||||
http = require('http');
|
||||
|
||||
if (ssl && !https)
|
||||
https = require('https');
|
||||
|
||||
if (!StringDecoder)
|
||||
StringDecoder = require('string_decoder').StringDecoder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = request;
|
||||
4810
lib/http/rpc.js
4810
lib/http/rpc.js
File diff suppressed because it is too large
Load Diff
@ -1,308 +0,0 @@
|
||||
/*!
|
||||
* rpcbase.js - json rpc for bcoin.
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* JSON RPC
|
||||
* @alias module:http.RPCBase
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function RPCBase() {
|
||||
if (!(this instanceof RPCBase))
|
||||
return new RPCBase();
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.calls = Object.create(null);
|
||||
this.mounts = [];
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(RPCBase.prototype, EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* RPC errors.
|
||||
* @enum {Number}
|
||||
* @default
|
||||
*/
|
||||
|
||||
RPCBase.errors = {
|
||||
// Standard JSON-RPC 2.0 errors
|
||||
INVALID_REQUEST: -32600,
|
||||
METHOD_NOT_FOUND: -32601,
|
||||
INVALID_PARAMS: -32602,
|
||||
INTERNAL_ERROR: -32603,
|
||||
PARSE_ERROR: -32700,
|
||||
|
||||
// General application defined errors
|
||||
MISC_ERROR: -1,
|
||||
FORBIDDEN_BY_SAFE_MODE: -2,
|
||||
TYPE_ERROR: -3,
|
||||
INVALID_ADDRESS_OR_KEY: -5,
|
||||
OUT_OF_MEMORY: -7,
|
||||
INVALID_PARAMETER: -8,
|
||||
DATABASE_ERROR: -20,
|
||||
DESERIALIZATION_ERROR: -22,
|
||||
VERIFY_ERROR: -25,
|
||||
VERIFY_REJECTED: -26,
|
||||
VERIFY_ALREADY_IN_CHAIN: -27,
|
||||
IN_WARMUP: -28,
|
||||
|
||||
// Aliases for backward compatibility
|
||||
TRANSACTION_ERROR: -25,
|
||||
TRANSACTION_REJECTED: -26,
|
||||
TRANSACTION_ALREADY_IN_CHAIN: -27,
|
||||
|
||||
// P2P client errors
|
||||
CLIENT_NOT_CONNECTED: -9,
|
||||
CLIENT_IN_INITIAL_DOWNLOAD: -10,
|
||||
CLIENT_NODE_ALREADY_ADDED: -23,
|
||||
CLIENT_NODE_NOT_ADDED: -24,
|
||||
CLIENT_NODE_NOT_CONNECTED: -29,
|
||||
CLIENT_INVALID_IP_OR_SUBNET: -30,
|
||||
CLIENT_P2P_DISABLED: -31,
|
||||
|
||||
// Wallet errors
|
||||
WALLET_ERROR: -4,
|
||||
WALLET_INSUFFICIENT_FUNDS: -6,
|
||||
WALLET_INVALID_ACCOUNT_NAME: -11,
|
||||
WALLET_KEYPOOL_RAN_OUT: -12,
|
||||
WALLET_UNLOCK_NEEDED: -13,
|
||||
WALLET_PASSPHRASE_INCORRECT: -14,
|
||||
WALLET_WRONG_ENC_STATE: -15,
|
||||
WALLET_ENCRYPTION_FAILED: -16,
|
||||
WALLET_ALREADY_UNLOCKED: -17
|
||||
};
|
||||
|
||||
/**
|
||||
* Magic string for signing.
|
||||
* @const {String}
|
||||
* @default
|
||||
*/
|
||||
|
||||
RPCBase.MAGIC_STRING = 'Bitcoin Signed Message:\n';
|
||||
|
||||
/**
|
||||
* Execute batched RPC calls.
|
||||
* @param {Object|Object[]} body
|
||||
* @param {Object} query
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
RPCBase.prototype.call = async function call(body, query) {
|
||||
let cmds = body;
|
||||
let out = [];
|
||||
let array = true;
|
||||
|
||||
if (!query)
|
||||
query = {};
|
||||
|
||||
if (!Array.isArray(cmds)) {
|
||||
cmds = [cmds];
|
||||
array = false;
|
||||
}
|
||||
|
||||
for (const cmd of cmds) {
|
||||
if (!cmd || typeof cmd !== 'object') {
|
||||
out.push({
|
||||
result: null,
|
||||
error: {
|
||||
message: 'Invalid request.',
|
||||
code: RPCBase.errors.INVALID_REQUEST
|
||||
},
|
||||
id: null
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.id && typeof cmd.id === 'object') {
|
||||
out.push({
|
||||
result: null,
|
||||
error: {
|
||||
message: 'Invalid ID.',
|
||||
code: RPCBase.errors.INVALID_REQUEST
|
||||
},
|
||||
id: null
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd.id == null)
|
||||
cmd.id = null;
|
||||
|
||||
if (!cmd.params)
|
||||
cmd.params = [];
|
||||
|
||||
if (typeof cmd.method !== 'string') {
|
||||
out.push({
|
||||
result: null,
|
||||
error: {
|
||||
message: 'Method not found.',
|
||||
code: RPCBase.errors.METHOD_NOT_FOUND
|
||||
},
|
||||
id: cmd.id
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Array.isArray(cmd.params)) {
|
||||
out.push({
|
||||
result: null,
|
||||
error: {
|
||||
message: 'Invalid params.',
|
||||
code: RPCBase.errors.INVALID_PARAMS
|
||||
},
|
||||
id: cmd.id
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
this.emit('call', cmd, query);
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await this.execute(cmd);
|
||||
} catch (err) {
|
||||
let code;
|
||||
|
||||
switch (err.type) {
|
||||
case 'RPCError':
|
||||
code = err.code;
|
||||
break;
|
||||
case 'ValidationError':
|
||||
code = RPCBase.errors.TYPE_ERROR;
|
||||
break;
|
||||
case 'EncodingError':
|
||||
code = RPCBase.errors.DESERIALIZATION_ERROR;
|
||||
break;
|
||||
case 'FundingError':
|
||||
code = RPCBase.errors.WALLET_INSUFFICIENT_FUNDS;
|
||||
break;
|
||||
default:
|
||||
code = RPCBase.errors.INTERNAL_ERROR;
|
||||
this.emit('error', err);
|
||||
break;
|
||||
}
|
||||
|
||||
out.push({
|
||||
result: null,
|
||||
error: {
|
||||
message: err.message,
|
||||
code: code
|
||||
},
|
||||
id: cmd.id
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result === undefined)
|
||||
result = null;
|
||||
|
||||
out.push({
|
||||
result: result,
|
||||
error: null,
|
||||
id: cmd.id
|
||||
});
|
||||
}
|
||||
|
||||
if (!array)
|
||||
out = out[0];
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute an RPC call.
|
||||
* @private
|
||||
* @param {Object} json
|
||||
* @param {Boolean} help
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
RPCBase.prototype.execute = async function execute(json, help) {
|
||||
const func = this.calls[json.method];
|
||||
|
||||
if (!func) {
|
||||
for (const mount of this.mounts) {
|
||||
if (mount.calls[json.method])
|
||||
return await mount.execute(json, help);
|
||||
}
|
||||
throw new RPCError(RPCBase.errors.METHOD_NOT_FOUND,
|
||||
`Method not found: ${json.method}.`);
|
||||
}
|
||||
|
||||
return func.call(this, json.params, help);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a custom RPC call.
|
||||
* @param {String} name
|
||||
* @param {Function} func
|
||||
*/
|
||||
|
||||
RPCBase.prototype.add = function add(name, func) {
|
||||
assert(typeof func === 'function', 'Handler must be a function.');
|
||||
assert(!this.calls[name], 'Duplicate RPC call.');
|
||||
this.calls[name] = func;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mount another RPC object.
|
||||
* @param {Object} rpc
|
||||
*/
|
||||
|
||||
RPCBase.prototype.mount = function mount(rpc) {
|
||||
assert(rpc, 'RPC must be an object.');
|
||||
assert(typeof rpc.execute === 'function', 'Execute must be a method.');
|
||||
this.mounts.push(rpc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach to another RPC object.
|
||||
* @param {Object} rpc
|
||||
*/
|
||||
|
||||
RPCBase.prototype.attach = function attach(rpc) {
|
||||
assert(rpc, 'RPC must be an object.');
|
||||
assert(typeof rpc.execute === 'function', 'Execute must be a method.');
|
||||
rpc.mount(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* RPC Error
|
||||
* @constructor
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
function RPCError(code, msg) {
|
||||
Error.call(this);
|
||||
|
||||
assert(typeof code === 'number');
|
||||
assert(typeof msg === 'string');
|
||||
|
||||
this.type = 'RPCError';
|
||||
this.message = msg;
|
||||
this.code = code;
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, RPCError);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(RPCError.prototype, Error.prototype);
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
exports = RPCBase;
|
||||
exports.RPCError = RPCError;
|
||||
|
||||
module.exports = exports;
|
||||
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
@ -1,101 +0,0 @@
|
||||
/*!
|
||||
* rpcclient.js - json rpc client for bcoin
|
||||
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Network = require('../protocol/network');
|
||||
const request = require('./request');
|
||||
|
||||
/**
|
||||
* Bcoin RPC client.
|
||||
* @alias module:http.RPCClient
|
||||
* @constructor
|
||||
* @param {String} uri
|
||||
* @param {Object?} options
|
||||
*/
|
||||
|
||||
function RPCClient(options) {
|
||||
if (!(this instanceof RPCClient))
|
||||
return new RPCClient(options);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
|
||||
this.options = options;
|
||||
this.network = Network.get(options.network);
|
||||
|
||||
this.uri = options.uri || `http://localhost:${this.network.rpcPort}`;
|
||||
this.apiKey = options.apiKey;
|
||||
this.id = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a json rpc request.
|
||||
* @private
|
||||
* @param {String} method - RPC method name.
|
||||
* @param {Array} params - RPC parameters.
|
||||
* @returns {Promise} - Returns Object?.
|
||||
*/
|
||||
|
||||
RPCClient.prototype.execute = async function execute(method, params) {
|
||||
const res = await request({
|
||||
method: 'POST',
|
||||
uri: this.uri,
|
||||
pool: true,
|
||||
json: {
|
||||
method: method,
|
||||
params: params,
|
||||
id: this.id++
|
||||
},
|
||||
auth: {
|
||||
username: 'bitcoinrpc',
|
||||
password: this.apiKey || ''
|
||||
}
|
||||
});
|
||||
|
||||
if (res.statusCode === 401)
|
||||
throw new RPCError('Unauthorized (bad API key).', -1);
|
||||
|
||||
if (res.type !== 'json')
|
||||
throw new Error('Bad response (wrong content-type).');
|
||||
|
||||
if (!res.body)
|
||||
throw new Error('No body for JSON-RPC response.');
|
||||
|
||||
if (res.body.error)
|
||||
throw new RPCError(res.body.error.message, res.body.error.code);
|
||||
|
||||
if (res.statusCode !== 200)
|
||||
throw new Error(`Status code: ${res.statusCode}.`);
|
||||
|
||||
return res.body.result;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function RPCError(msg, code) {
|
||||
Error.call(this);
|
||||
|
||||
this.type = 'RPCError';
|
||||
this.message = String(msg);
|
||||
this.code = code >>> 0;
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, RPCError);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(RPCError.prototype, Error.prototype);
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = RPCClient;
|
||||
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
1388
lib/http/server.js
1388
lib/http/server.js
File diff suppressed because it is too large
Load Diff
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
1023
lib/http/wallet.js
1023
lib/http/wallet.js
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const request = require('../http/request');
|
||||
const breq = require('breq');
|
||||
const IP = require('../utils/ip');
|
||||
|
||||
/**
|
||||
@ -23,14 +23,14 @@ const external = exports;
|
||||
|
||||
external.getIPv4 = async function getIPv4() {
|
||||
try {
|
||||
const res = await request({
|
||||
const res = await breq({
|
||||
method: 'GET',
|
||||
uri: 'http://ipv4.icanhazip.com',
|
||||
url: 'http://ipv4.icanhazip.com',
|
||||
expect: 'txt',
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
const str = res.body.trim();
|
||||
const str = res.text().trim();
|
||||
const raw = IP.toBuffer(str);
|
||||
|
||||
if (!IP.isIPv4(raw))
|
||||
@ -50,14 +50,14 @@ external.getIPv4 = async function getIPv4() {
|
||||
*/
|
||||
|
||||
external.getIPv42 = async function getIPv42() {
|
||||
const res = await request({
|
||||
const res = await breq({
|
||||
method: 'GET',
|
||||
uri: 'http://checkip.dyndns.org',
|
||||
url: 'http://checkip.dyndns.org',
|
||||
expect: 'html',
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
const match = /IP Address:\s*([0-9a-f.:]+)/i.exec(res.body);
|
||||
const match = /IP Address:\s*([0-9a-f.:]+)/i.exec(res.text());
|
||||
|
||||
if (!match)
|
||||
throw new Error('Could not find IPv4.');
|
||||
@ -78,14 +78,14 @@ external.getIPv42 = async function getIPv42() {
|
||||
*/
|
||||
|
||||
external.getIPv6 = async function getIPv6() {
|
||||
const res = await request({
|
||||
const res = await breq({
|
||||
method: 'GET',
|
||||
uri: 'http://ipv6.icanhazip.com',
|
||||
url: 'http://ipv6.icanhazip.com',
|
||||
expect: 'txt',
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
const str = res.body.trim();
|
||||
const str = res.text().trim();
|
||||
const raw = IP.toBuffer(str);
|
||||
|
||||
if (!IP.isIPv6(raw))
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
const assert = require('assert');
|
||||
const dgram = require('dgram');
|
||||
const url = require('url');
|
||||
const request = require('../http/request');
|
||||
const breq = require('breq');
|
||||
const co = require('../utils/co');
|
||||
const Lock = require('../utils/lock');
|
||||
const IP = require('../utils/ip');
|
||||
@ -220,9 +220,9 @@ UPNP.prototype.resolve = async function resolve(location, targets) {
|
||||
if (!targets)
|
||||
targets = UPNP.WAN_SERVICES;
|
||||
|
||||
const res = await request({
|
||||
const res = await breq({
|
||||
method: 'GET',
|
||||
uri: location,
|
||||
url: location,
|
||||
timeout: UPNP.RESPONSE_TIMEOUT,
|
||||
expect: 'xml'
|
||||
});
|
||||
@ -368,9 +368,9 @@ UPNPService.prototype.soapRequest = async function soapRequest(action, args) {
|
||||
const type = this.serviceType;
|
||||
const req = this.createRequest(action, args);
|
||||
|
||||
const res = await request({
|
||||
const res = await breq({
|
||||
method: 'POST',
|
||||
uri: this.controlURL,
|
||||
url: this.controlURL,
|
||||
timeout: UPNP.RESPONSE_TIMEOUT,
|
||||
expect: 'xml',
|
||||
headers: {
|
||||
|
||||
@ -137,21 +137,19 @@ function FullNode(options) {
|
||||
this.rpc = new RPC(this);
|
||||
|
||||
// HTTP needs access to the node.
|
||||
if (!HTTPServer.unsupported) {
|
||||
this.http = new HTTPServer({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
node: this,
|
||||
prefix: this.config.prefix,
|
||||
ssl: this.config.bool('ssl'),
|
||||
keyFile: this.config.path('ssl-key'),
|
||||
certFile: this.config.path('ssl-cert'),
|
||||
host: this.config.str('http-host'),
|
||||
port: this.config.uint('http-port'),
|
||||
apiKey: this.config.str('api-key'),
|
||||
noAuth: this.config.bool('no-auth')
|
||||
});
|
||||
}
|
||||
this.http = new HTTPServer({
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
node: this,
|
||||
prefix: this.config.prefix,
|
||||
ssl: this.config.bool('ssl'),
|
||||
keyFile: this.config.path('ssl-key'),
|
||||
certFile: this.config.path('ssl-cert'),
|
||||
host: this.config.str('http-host'),
|
||||
port: this.config.uint('http-port'),
|
||||
apiKey: this.config.str('api-key'),
|
||||
noAuth: this.config.bool('no-auth')
|
||||
});
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
@ -572,15 +572,16 @@ Block.prototype.toJSON = function toJSON() {
|
||||
* @param {Network} network
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height
|
||||
* @param {Number} depth
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
Block.prototype.getJSON = function getJSON(network, view, height, confirmations) {
|
||||
Block.prototype.getJSON = function getJSON(network, view, height, depth) {
|
||||
network = Network.get(network);
|
||||
return {
|
||||
hash: this.rhash(),
|
||||
height: height,
|
||||
confirmations: confirmations,
|
||||
depth: depth,
|
||||
version: this.version,
|
||||
prevBlock: util.revHex(this.prevBlock),
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
@ -7,325 +7,207 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const IOClient = require('socket.io-client');
|
||||
const Network = require('../protocol/network');
|
||||
const AsyncObject = require('../utils/asyncobject');
|
||||
const {Client} = require('bcurl');
|
||||
const TX = require('../primitives/tx');
|
||||
const {BlockMeta} = require('./records');
|
||||
const Headers = require('../primitives/headers');
|
||||
const util = require('../utils/util');
|
||||
const BufferReader = require('../utils/reader');
|
||||
|
||||
/**
|
||||
* Bcoin HTTP client.
|
||||
* @alias module:wallet.WalletClient
|
||||
* @constructor
|
||||
* @param {Object|String} options
|
||||
*/
|
||||
class WalletClient extends Client {
|
||||
/**
|
||||
* Bcoin HTTP client.
|
||||
* @alias module:wallet.WalletClient
|
||||
* @constructor
|
||||
* @param {Object|String} options
|
||||
*/
|
||||
|
||||
function WalletClient(options) {
|
||||
if (!(this instanceof WalletClient))
|
||||
return new WalletClient(options);
|
||||
constructor(options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
/**
|
||||
* Open the client, wait for socket to connect.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
if (typeof options === 'string')
|
||||
options = { uri: options };
|
||||
async open() {
|
||||
await super.open();
|
||||
|
||||
AsyncObject.call(this);
|
||||
this.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.options = options;
|
||||
this.network = Network.get(options.network);
|
||||
this.listen('block connect', (entry, txs) => {
|
||||
this.emit('block connect', ...parseBlock(entry, txs));
|
||||
});
|
||||
|
||||
this.uri = options.uri || `http://localhost:${this.network.rpcPort}`;
|
||||
this.apiKey = options.apiKey;
|
||||
this.listen('block disconnect', (entry) => {
|
||||
this.emit('block disconnect', parseEntry(entry));
|
||||
});
|
||||
|
||||
this.socket = null;
|
||||
}
|
||||
this.listen('block rescan', (entry, txs) => {
|
||||
this.emit('block rescan', ...parseBlock(entry, txs));
|
||||
});
|
||||
|
||||
Object.setPrototypeOf(WalletClient.prototype, AsyncObject.prototype);
|
||||
this.listen('chain reset', (tip) => {
|
||||
this.emit('chain reset', parseEntry(tip));
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the client, wait for socket to connect.
|
||||
* @alias WalletClient#open
|
||||
* @returns {Promise}
|
||||
*/
|
||||
this.listen('tx', (tx) => {
|
||||
this.emit('tx', TX.fromRaw(tx));
|
||||
});
|
||||
|
||||
WalletClient.prototype._open = async function _open() {
|
||||
this.socket = new IOClient(this.uri, {
|
||||
transports: ['websocket'],
|
||||
forceNew: true
|
||||
});
|
||||
await this.watchChain();
|
||||
await this.watchMempool();
|
||||
}
|
||||
|
||||
this.socket.on('error', (err) => {
|
||||
this.emit('error', err);
|
||||
});
|
||||
/**
|
||||
* Auth with server.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
this.socket.on('version', (info) => {
|
||||
if (info.network !== this.network.type)
|
||||
this.emit('error', new Error('Wrong network.'));
|
||||
});
|
||||
auth() {
|
||||
return this.call('auth', this.password);
|
||||
}
|
||||
|
||||
this.socket.on('block connect', (entry, txs) => {
|
||||
let block;
|
||||
/**
|
||||
* Make an RPC call.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
try {
|
||||
block = parseBlock(entry, txs);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
execute(name, params) {
|
||||
return super.execute('/', name, params);
|
||||
}
|
||||
|
||||
this.emit('block connect', block.entry, block.txs);
|
||||
});
|
||||
/**
|
||||
* Watch the blockchain.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
this.socket.on('block disconnect', (entry) => {
|
||||
let block;
|
||||
watchChain() {
|
||||
return this.call('watch chain');
|
||||
}
|
||||
|
||||
try {
|
||||
block = parseEntry(entry);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Watch the blockchain.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
this.emit('block disconnect', block);
|
||||
});
|
||||
watchMempool() {
|
||||
return this.call('watch mempool');
|
||||
}
|
||||
|
||||
this.socket.on('block rescan', (entry, txs, cb) => {
|
||||
let block;
|
||||
/**
|
||||
* Get chain tip.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
try {
|
||||
block = parseBlock(entry, txs);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
async getTip() {
|
||||
const raw = await this.call('get tip');
|
||||
return parseEntry(raw);
|
||||
}
|
||||
|
||||
this.fire('block rescan', block.entry, block.txs).then(cb, cb);
|
||||
});
|
||||
/**
|
||||
* Get chain entry.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
this.socket.on('chain reset', (tip) => {
|
||||
let block;
|
||||
|
||||
try {
|
||||
block = parseEntry(tip);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit('chain reset', block);
|
||||
});
|
||||
|
||||
this.socket.on('tx', (tx) => {
|
||||
try {
|
||||
tx = parseTX(tx);
|
||||
} catch (e) {
|
||||
this.emit('error', e);
|
||||
return;
|
||||
}
|
||||
this.emit('tx', tx);
|
||||
});
|
||||
|
||||
await this.onConnect();
|
||||
await this.sendAuth();
|
||||
await this.watchChain();
|
||||
await this.watchMempool();
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the client, wait for the socket to close.
|
||||
* @alias WalletClient#close
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype._close = function _close() {
|
||||
if (!this.socket)
|
||||
return Promise.resolve();
|
||||
|
||||
this.socket.disconnect();
|
||||
this.socket = null;
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
* Wait for websocket connection.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.onConnect = function onConnect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.once('connect', resolve);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Wait for websocket auth.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.sendAuth = function sendAuth() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('auth', this.apiKey, wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the blockchain.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.watchChain = function watchChain() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('watch chain', wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch the blockchain.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.watchMempool = function watchMempool() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('watch mempool', wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get chain tip.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.getTip = function getTip() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('get tip', wrap(resolve, reject, parseEntry));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get chain entry.
|
||||
* @param {Hash} hash
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.getEntry = function getEntry(block) {
|
||||
return new Promise((resolve, reject) => {
|
||||
async getEntry(block) {
|
||||
if (typeof block === 'string')
|
||||
block = util.revHex(block);
|
||||
|
||||
this.socket.emit('get entry', block, wrap(resolve, reject, parseEntry));
|
||||
});
|
||||
};
|
||||
const raw = await this.call('get entry', block);
|
||||
return parseEntry(raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hashes.
|
||||
* @param {Number} [start=-1]
|
||||
* @param {Number} [end=-1]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Get hashes.
|
||||
* @param {Number} [start=-1]
|
||||
* @param {Number} [end=-1]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.getHashes = function getHashes(start = -1, end = -1) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('get hashes', start, end, wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
getHashes(start = -1, end = -1) {
|
||||
return this.call('get hashes', start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a transaction. Do not wait for promise.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Send a transaction. Do not wait for promise.
|
||||
* @param {TX} tx
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.send = function send(tx) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('send', tx.toRaw(), wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
send(tx) {
|
||||
return this.call('send', tx.toRaw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bloom filter.
|
||||
* @param {Bloom} filter
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Set bloom filter.
|
||||
* @param {Bloom} filter
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.setFilter = function setFilter(filter) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('set filter', filter.toRaw(), wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
setFilter(filter) {
|
||||
return this.call('set filter', filter.toRaw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data to filter.
|
||||
* @param {Buffer} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Add data to filter.
|
||||
* @param {Buffer} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.addFilter = function addFilter(chunks) {
|
||||
if (!Array.isArray(chunks))
|
||||
chunks = [chunks];
|
||||
addFilter(chunks) {
|
||||
if (!Array.isArray(chunks))
|
||||
chunks = [chunks];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('add filter', chunks, wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
return this.call('add filter', chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset filter.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Reset filter.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.resetFilter = function resetFilter() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('reset filter', wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
resetFilter() {
|
||||
return this.call('reset filter');
|
||||
}
|
||||
|
||||
/**
|
||||
* Esimate smart fee.
|
||||
* @param {Number?} blocks
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Esimate smart fee.
|
||||
* @param {Number?} blocks
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.estimateFee = function estimateFee(blocks) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.emit('estimate fee', blocks, wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
estimateFee(blocks) {
|
||||
return this.call('estimate fee', blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescan for any missed transactions.
|
||||
* @param {Number|Hash} start - Start block.
|
||||
* @param {Bloom} filter
|
||||
* @param {Function} iter - Iterator.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
/**
|
||||
* Rescan for any missed transactions.
|
||||
* @param {Number|Hash} start - Start block.
|
||||
* @param {Bloom} filter
|
||||
* @param {Function} iter - Iterator.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
WalletClient.prototype.rescan = function rescan(start) {
|
||||
return new Promise((resolve, reject) => {
|
||||
rescan(start) {
|
||||
if (typeof start === 'string')
|
||||
start = util.revHex(start);
|
||||
|
||||
this.socket.emit('rescan', start, wrap(resolve, reject));
|
||||
});
|
||||
};
|
||||
return this.call('rescan', start);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parseEntry(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = Buffer.from(data, 'hex');
|
||||
|
||||
function parseEntry(data) {
|
||||
const block = Headers.fromHead(data);
|
||||
|
||||
const br = new BufferReader(data);
|
||||
@ -334,7 +216,7 @@ function parseEntry(data, enc) {
|
||||
const height = br.readU32();
|
||||
const hash = block.hash('hex');
|
||||
|
||||
return new BlockMeta(hash, height, block.time);
|
||||
return { hash, height, time: block.time };
|
||||
}
|
||||
|
||||
function parseBlock(entry, txs) {
|
||||
@ -342,48 +224,11 @@ function parseBlock(entry, txs) {
|
||||
const out = [];
|
||||
|
||||
for (const raw of txs) {
|
||||
const tx = parseTX(raw);
|
||||
const tx = TX.fromRaw(raw);
|
||||
out.push(tx);
|
||||
}
|
||||
|
||||
return new BlockResult(block, out);
|
||||
}
|
||||
|
||||
function parseTX(data) {
|
||||
return TX.fromRaw(data, 'hex');
|
||||
}
|
||||
|
||||
function BlockResult(entry, txs) {
|
||||
this.entry = entry;
|
||||
this.txs = txs;
|
||||
}
|
||||
|
||||
function wrap(resolve, reject, parse) {
|
||||
return function(err, result) {
|
||||
if (err) {
|
||||
reject(new Error(err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parse) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
result = parse(result);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
};
|
||||
return [block, out];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.unsupported = true;
|
||||
1734
lib/wallet/http.js
1734
lib/wallet/http.js
File diff suppressed because it is too large
Load Diff
@ -56,10 +56,7 @@ plugin.init = function init(node) {
|
||||
listen: false
|
||||
});
|
||||
|
||||
if (node.http && wdb.http)
|
||||
wdb.http.attach(node.http);
|
||||
|
||||
wdb.rpc.attach(node.rpc);
|
||||
wdb.http.attach('/wallet', node.http);
|
||||
|
||||
return wdb;
|
||||
};
|
||||
|
||||
2690
lib/wallet/rpc.js
2690
lib/wallet/rpc.js
File diff suppressed because it is too large
Load Diff
@ -62,22 +62,19 @@ function WalletDB(options) {
|
||||
this.db = LDB(this.options);
|
||||
this.rpc = new RPC(this);
|
||||
this.primary = null;
|
||||
this.http = null;
|
||||
|
||||
if (!HTTPServer.unsupported) {
|
||||
this.http = new HTTPServer({
|
||||
walletdb: this,
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
prefix: this.options.prefix,
|
||||
apiKey: this.options.apiKey,
|
||||
walletAuth: this.options.walletAuth,
|
||||
noAuth: this.options.noAuth,
|
||||
host: this.options.host,
|
||||
port: this.options.port,
|
||||
ssl: this.options.ssl
|
||||
});
|
||||
}
|
||||
this.http = new HTTPServer({
|
||||
walletdb: this,
|
||||
network: this.network,
|
||||
logger: this.logger,
|
||||
prefix: this.options.prefix,
|
||||
apiKey: this.options.apiKey,
|
||||
walletAuth: this.options.walletAuth,
|
||||
noAuth: this.options.noAuth,
|
||||
host: this.options.host,
|
||||
port: this.options.port,
|
||||
ssl: this.options.ssl
|
||||
});
|
||||
|
||||
this.state = new ChainState();
|
||||
this.height = 0;
|
||||
|
||||
@ -14,6 +14,8 @@ 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');
|
||||
const network = Network.get('regtest');
|
||||
|
||||
const node = new FullNode({
|
||||
network: 'regtest',
|
||||
@ -24,8 +26,13 @@ const node = new FullNode({
|
||||
plugins: [require('../lib/wallet/plugin')]
|
||||
});
|
||||
|
||||
const client = new HTTP.Client({
|
||||
port: network.rpcPort,
|
||||
apiKey: 'foo'
|
||||
});
|
||||
|
||||
const wallet = new HTTP.Wallet({
|
||||
network: 'regtest',
|
||||
port: network.rpcPort,
|
||||
apiKey: 'foo'
|
||||
});
|
||||
|
||||
@ -40,6 +47,7 @@ describe('HTTP', function() {
|
||||
it('should open node', async () => {
|
||||
consensus.COINBASE_MATURITY = 0;
|
||||
await node.open();
|
||||
await client.open();
|
||||
});
|
||||
|
||||
it('should create wallet', async () => {
|
||||
@ -48,7 +56,7 @@ describe('HTTP', function() {
|
||||
});
|
||||
|
||||
it('should get info', async () => {
|
||||
const info = await wallet.client.getInfo();
|
||||
const info = await client.getInfo();
|
||||
assert.strictEqual(info.network, node.network.type);
|
||||
assert.strictEqual(info.version, pkg.version);
|
||||
assert.typeOf(info.pool, 'object');
|
||||
@ -154,12 +162,12 @@ describe('HTTP', function() {
|
||||
});
|
||||
|
||||
it('should execute an rpc call', async () => {
|
||||
const info = await wallet.client.rpc.execute('getblockchaininfo', []);
|
||||
const info = await client.execute('getblockchaininfo', []);
|
||||
assert.strictEqual(info.blocks, 0);
|
||||
});
|
||||
|
||||
it('should execute an rpc call with bool parameter', async () => {
|
||||
const info = await wallet.client.rpc.execute('getrawmempool', [true]);
|
||||
const info = await client.execute('getrawmempool', [true]);
|
||||
assert.deepStrictEqual(info, {});
|
||||
});
|
||||
|
||||
@ -188,7 +196,7 @@ describe('HTTP', function() {
|
||||
});
|
||||
|
||||
it('should get a block template', async () => {
|
||||
const json = await wallet.client.rpc.execute('getblocktemplate', []);
|
||||
const json = await client.execute('getblocktemplate', []);
|
||||
assert.deepStrictEqual(json, {
|
||||
capabilities: ['proposal'],
|
||||
mutable: ['time', 'transactions', 'prevblock'],
|
||||
@ -223,7 +231,7 @@ describe('HTTP', function() {
|
||||
const attempt = await node.miner.createBlock();
|
||||
const block = attempt.toBlock();
|
||||
const hex = block.toRaw().toString('hex');
|
||||
const json = await wallet.client.rpc.execute('getblocktemplate', [{
|
||||
const json = await client.execute('getblocktemplate', [{
|
||||
mode: 'proposal',
|
||||
data: hex
|
||||
}]);
|
||||
@ -231,7 +239,7 @@ describe('HTTP', function() {
|
||||
});
|
||||
|
||||
it('should validate an address', async () => {
|
||||
const json = await wallet.client.rpc.execute('validateaddress', [
|
||||
const json = await client.execute('validateaddress', [
|
||||
addr.toString(node.network)
|
||||
]);
|
||||
assert.deepStrictEqual(json, {
|
||||
@ -246,6 +254,7 @@ describe('HTTP', function() {
|
||||
it('should cleanup', async () => {
|
||||
consensus.COINBASE_MATURITY = 100;
|
||||
await wallet.close();
|
||||
await client.close();
|
||||
await node.close();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user