refactor: config, plugins, and walletdb.
This commit is contained in:
parent
70a0147080
commit
5cbbdbfb2f
1057
lib/http/base.js
1057
lib/http/base.js
File diff suppressed because it is too large
Load Diff
@ -108,8 +108,8 @@ HTTPClient.prototype._open = co(function* _open() {
|
|||||||
self.emit('balance', balance);
|
self.emit('balance', balance);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield this._onConnect();
|
yield this.onConnect();
|
||||||
yield this._sendAuth();
|
yield this.sendAuth();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +134,7 @@ HTTPClient.prototype._close = function close() {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HTTPClient.prototype._onConnect = function _onConnect() {
|
HTTPClient.prototype.onConnect = function onConnect() {
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
self.socket.once('connect', resolve);
|
self.socket.once('connect', resolve);
|
||||||
@ -147,12 +147,29 @@ HTTPClient.prototype._onConnect = function _onConnect() {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HTTPClient.prototype._sendAuth = function _sendAuth() {
|
HTTPClient.prototype.sendAuth = function sendAuth() {
|
||||||
var self = this;
|
var self = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
self.socket.emit('auth', self.apiKey, function(err) {
|
self.socket.emit('auth', self.apiKey, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return reject(new Error(err.error));
|
return reject(new Error(err.message));
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for websocket auth.
|
||||||
|
* @private
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HTTPClient.prototype.sendWalletAuth = function sendWalletAuth() {
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
self.socket.emit('wallet auth', self.apiKey, function(err) {
|
||||||
|
if (err)
|
||||||
|
return reject(new Error(err.message));
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -220,7 +237,7 @@ HTTPClient.prototype._request = co(function* _request(method, endpoint, json) {
|
|||||||
|
|
||||||
if (res.statusCode !== 200) {
|
if (res.statusCode !== 200) {
|
||||||
if (res.body.error)
|
if (res.body.error)
|
||||||
throw new Error(res.body.error);
|
throw new Error(res.body.error.message);
|
||||||
throw new Error('Status code: ' + res.statusCode);
|
throw new Error('Status code: ' + res.statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +386,7 @@ HTTPClient.prototype.broadcast = function broadcast(tx) {
|
|||||||
|
|
||||||
HTTPClient.prototype.rescan = function rescan(height) {
|
HTTPClient.prototype.rescan = function rescan(height) {
|
||||||
var options = { height: height };
|
var options = { height: height };
|
||||||
return this._post('/rescan', options);
|
return this._post('/_admin/rescan', options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -380,7 +397,7 @@ HTTPClient.prototype.rescan = function rescan(height) {
|
|||||||
|
|
||||||
HTTPClient.prototype.reset = function reset(block) {
|
HTTPClient.prototype.reset = function reset(block) {
|
||||||
var options = { block: block };
|
var options = { block: block };
|
||||||
return this._post('/reset', options);
|
return this._post('/_admin/reset', options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -389,7 +406,7 @@ HTTPClient.prototype.reset = function reset(block) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HTTPClient.prototype.resend = function resend() {
|
HTTPClient.prototype.resend = function resend() {
|
||||||
return this._post('/resend', {});
|
return this._post('/_admin/resend', {});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -400,7 +417,7 @@ HTTPClient.prototype.resend = function resend() {
|
|||||||
|
|
||||||
HTTPClient.prototype.backup = function backup(path) {
|
HTTPClient.prototype.backup = function backup(path) {
|
||||||
var options = { path: path };
|
var options = { path: path };
|
||||||
return this._post('/backup', options);
|
return this._post('/_admin/backup', options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -417,7 +434,7 @@ HTTPClient.prototype.join = function join(id, token) {
|
|||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
self.socket.emit('wallet join', id, token, function(err) {
|
self.socket.emit('wallet join', id, token, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return reject(new Error(err.error));
|
return reject(new Error(err.message));
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -437,7 +454,7 @@ HTTPClient.prototype.leave = function leave(id) {
|
|||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
self.socket.emit('wallet leave', id, function(err) {
|
self.socket.emit('wallet leave', id, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return reject(new Error(err.error));
|
return reject(new Error(err.message));
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -475,7 +492,7 @@ HTTPClient.prototype.getWallets = function getWallets() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HTTPClient.prototype.createWallet = function createWallet(options) {
|
HTTPClient.prototype.createWallet = function createWallet(options) {
|
||||||
return this._post('/wallet', options);
|
return this._put('/wallet/' + options.id, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -934,9 +951,9 @@ HTTPClient.prototype.createAccount = function createAccount(id, options) {
|
|||||||
if (typeof options === 'string')
|
if (typeof options === 'string')
|
||||||
options = { account: options };
|
options = { account: options };
|
||||||
|
|
||||||
path = '/wallet/' + id + '/account';
|
path = '/wallet/' + id + '/account' + options.account;
|
||||||
|
|
||||||
return this._post(path, options);
|
return this._put(path, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
1838
lib/http/rpc.js
1838
lib/http/rpc.js
File diff suppressed because it is too large
Load Diff
1800
lib/http/server.js
1800
lib/http/server.js
File diff suppressed because it is too large
Load Diff
@ -115,6 +115,7 @@ HTTPWallet.prototype.open = co(function* open(options) {
|
|||||||
assert(this.id, 'No ID provided.');
|
assert(this.id, 'No ID provided.');
|
||||||
|
|
||||||
yield this.client.open();
|
yield this.client.open();
|
||||||
|
yield this.client.sendWalletAuth();
|
||||||
yield this.client.join(this.id, this.token);
|
yield this.client.join(this.id, this.token);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -128,6 +129,7 @@ HTTPWallet.prototype.create = co(function* create(options) {
|
|||||||
var wallet;
|
var wallet;
|
||||||
|
|
||||||
yield this.client.open();
|
yield this.client.open();
|
||||||
|
yield this.client.sendWalletAuth();
|
||||||
|
|
||||||
wallet = yield this.client.createWallet(options);
|
wallet = yield this.client.createWallet(options);
|
||||||
|
|
||||||
|
|||||||
@ -107,6 +107,9 @@ Miner.prototype._open = co(function* open() {
|
|||||||
|
|
||||||
this.logger.info('Miner loaded (flags=%s).',
|
this.logger.info('Miner loaded (flags=%s).',
|
||||||
this.options.coinbaseFlags.toString('utf8'));
|
this.options.coinbaseFlags.toString('utf8'));
|
||||||
|
|
||||||
|
if (this.addresses.length === 0)
|
||||||
|
this.logger.warning('No reward address is set for miner!');
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,7 +390,8 @@ Miner.prototype.addAddress = function addAddress(address) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Miner.prototype.getAddress = function getAddress() {
|
Miner.prototype.getAddress = function getAddress() {
|
||||||
assert(this.addresses.length !== 0, 'No address passed in for miner.');
|
if (this.addresses.length === 0)
|
||||||
|
return;
|
||||||
return this.addresses[Math.random() * this.addresses.length | 0];
|
return this.addresses[Math.random() * this.addresses.length | 0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ var util = require('../utils/util');
|
|||||||
var co = require('../utils/co');
|
var co = require('../utils/co');
|
||||||
var StaticWriter = require('../utils/staticwriter');
|
var StaticWriter = require('../utils/staticwriter');
|
||||||
var Network = require('../protocol/network');
|
var Network = require('../protocol/network');
|
||||||
|
var Address = require('../primitives/address');
|
||||||
var TX = require('../primitives/tx');
|
var TX = require('../primitives/tx');
|
||||||
var Block = require('../primitives/block');
|
var Block = require('../primitives/block');
|
||||||
var Input = require('../primitives/input');
|
var Input = require('../primitives/input');
|
||||||
@ -232,8 +233,10 @@ MinerBlock.prototype._init = function _init() {
|
|||||||
input.script.compile();
|
input.script.compile();
|
||||||
|
|
||||||
// Setup output script (variable size).
|
// Setup output script (variable size).
|
||||||
output.script.clear();
|
if (this.address) {
|
||||||
output.script.fromAddress(this.address);
|
output.script.clear();
|
||||||
|
output.script.fromAddress(this.address);
|
||||||
|
}
|
||||||
|
|
||||||
// Update commitments.
|
// Update commitments.
|
||||||
this.refresh();
|
this.refresh();
|
||||||
@ -345,6 +348,23 @@ MinerBlock.prototype.extraNonce = function extraNonce() {
|
|||||||
return bw.render();
|
return bw.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the reward output address.
|
||||||
|
* @param {Address} address
|
||||||
|
*/
|
||||||
|
|
||||||
|
MinerBlock.prototype.setAddress = function setAddress(address) {
|
||||||
|
var output = this.coinbase.outputs[0];
|
||||||
|
|
||||||
|
this.address = Address(address);
|
||||||
|
|
||||||
|
output.script.clear();
|
||||||
|
output.script.fromAddress(this.address);
|
||||||
|
|
||||||
|
// Update commitments.
|
||||||
|
this.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a transaction to the block. Rebuilds the merkle tree,
|
* Add a transaction to the block. Rebuilds the merkle tree,
|
||||||
* updates coinbase and commitment.
|
* updates coinbase and commitment.
|
||||||
|
|||||||
@ -268,7 +268,6 @@ Pool.prototype.resetChain = function resetChain() {
|
|||||||
|
|
||||||
Pool.prototype._close = co(function* close() {
|
Pool.prototype._close = co(function* close() {
|
||||||
yield this.disconnect();
|
yield this.disconnect();
|
||||||
yield this.hosts.close();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -243,8 +243,8 @@ Config.prototype.str = function str(key, fallback) {
|
|||||||
if (value === null)
|
if (value === null)
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
assert(typeof value === 'string',
|
if (typeof value !== 'string')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a string.');
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -266,15 +266,18 @@ Config.prototype.num = function num(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(typeof value === 'number',
|
if (typeof value !== 'number')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a string.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!/^\d+$/.test(value))
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
value = parseInt(value, 10);
|
value = parseInt(value, 10);
|
||||||
|
|
||||||
if (!isFinite(value))
|
if (!isFinite(value))
|
||||||
return fallback;
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -296,8 +299,8 @@ Config.prototype.bool = function bool(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(typeof value === 'boolean',
|
if (typeof value !== 'boolean')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a boolean.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +310,7 @@ Config.prototype.bool = function bool(key, fallback) {
|
|||||||
if (value === 'false' || value === '0')
|
if (value === 'false' || value === '0')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return fallback;
|
throw new Error(key + ' must be a boolean.');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -319,6 +322,7 @@ Config.prototype.bool = function bool(key, fallback) {
|
|||||||
|
|
||||||
Config.prototype.buf = function buf(key, fallback) {
|
Config.prototype.buf = function buf(key, fallback) {
|
||||||
var value = this.get(key);
|
var value = this.get(key);
|
||||||
|
var data;
|
||||||
|
|
||||||
if (fallback === undefined)
|
if (fallback === undefined)
|
||||||
fallback = null;
|
fallback = null;
|
||||||
@ -327,12 +331,17 @@ Config.prototype.buf = function buf(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(Buffer.isBuffer(value),
|
if (!Buffer.isBuffer(value))
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a buffer.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Buffer(value, 'hex');
|
data = new Buffer(value, 'hex');
|
||||||
|
|
||||||
|
if (data.length !== value.length / 2)
|
||||||
|
throw new Error(key + ' must be a hex string.');
|
||||||
|
|
||||||
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -352,8 +361,8 @@ Config.prototype.list = function list(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(Array.isArray(value),
|
if (!Array.isArray(value))
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be an array.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,8 +386,8 @@ Config.prototype.obj = function obj(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(value && typeof value === 'object',
|
if (!value || typeof value !== 'object')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be an object.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,8 +397,8 @@ Config.prototype.obj = function obj(key, fallback) {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(value && typeof value === 'object',
|
if (!value || typeof value !== 'object')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be an object.');
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -411,13 +420,12 @@ Config.prototype.func = function func(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(value && typeof value === 'function',
|
if (!value || typeof value !== 'function')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a function.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false,
|
throw new Error(key + ' must be a function.');
|
||||||
'Passed in config option is of wrong type.');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -486,8 +494,8 @@ Config.prototype.boolpath = function boolpath(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
assert(typeof value === 'boolean' || typeof value === 'string',
|
if (typeof value !== 'boolean' && typeof value !== 'string')
|
||||||
'Passed in config option is of wrong type.');
|
throw new Error(key + ' must be a boolean or string.');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,12 +15,11 @@ var Fees = require('../mempool/fees');
|
|||||||
var Mempool = require('../mempool/mempool');
|
var Mempool = require('../mempool/mempool');
|
||||||
var Pool = require('../net/pool');
|
var Pool = require('../net/pool');
|
||||||
var Miner = require('../mining/miner');
|
var Miner = require('../mining/miner');
|
||||||
var WalletDB = require('../wallet/walletdb');
|
|
||||||
var HTTPServer = require('../http/server');
|
var HTTPServer = require('../http/server');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Respresents a fullnode complete with a
|
* Respresents a fullnode complete with a
|
||||||
* chain, mempool, miner, wallet, etc.
|
* chain, mempool, miner, etc.
|
||||||
* @alias module:node.FullNode
|
* @alias module:node.FullNode
|
||||||
* @extends Node
|
* @extends Node
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -30,7 +29,6 @@ var HTTPServer = require('../http/server');
|
|||||||
* @property {Mempool} mempool
|
* @property {Mempool} mempool
|
||||||
* @property {Pool} pool
|
* @property {Pool} pool
|
||||||
* @property {Miner} miner
|
* @property {Miner} miner
|
||||||
* @property {WalletDB} walletdb
|
|
||||||
* @property {HTTPServer} http
|
* @property {HTTPServer} http
|
||||||
* @emits FullNode#block
|
* @emits FullNode#block
|
||||||
* @emits FullNode#tx
|
* @emits FullNode#tx
|
||||||
@ -126,22 +124,6 @@ function FullNode(options) {
|
|||||||
reservedSigops: this.config.num('reserved-sigops')
|
reservedSigops: this.config.num('reserved-sigops')
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wallet database needs access to fees.
|
|
||||||
this.walletdb = new WalletDB({
|
|
||||||
network: this.network,
|
|
||||||
logger: this.logger,
|
|
||||||
client: this.client,
|
|
||||||
db: this.config.str('db'),
|
|
||||||
prefix: this.config.prefix,
|
|
||||||
maxFiles: this.config.num('walletdb-max-files'),
|
|
||||||
cacheSize: this.config.mb('walletdb-cache-size'),
|
|
||||||
witness: false,
|
|
||||||
checkpoints: this.config.bool('checkpoints'),
|
|
||||||
startHeight: this.config.num('start-height'),
|
|
||||||
wipeNoReally: this.config.bool('wipe-no-really'),
|
|
||||||
verify: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// HTTP needs access to the node.
|
// HTTP needs access to the node.
|
||||||
if (!HTTPServer.unsupported) {
|
if (!HTTPServer.unsupported) {
|
||||||
this.http = new HTTPServer({
|
this.http = new HTTPServer({
|
||||||
@ -180,7 +162,6 @@ FullNode.prototype._init = function _init() {
|
|||||||
this.mempool.on('error', onError);
|
this.mempool.on('error', onError);
|
||||||
this.pool.on('error', onError);
|
this.pool.on('error', onError);
|
||||||
this.miner.on('error', onError);
|
this.miner.on('error', onError);
|
||||||
this.walletdb.on('error', onError);
|
|
||||||
|
|
||||||
if (this.http)
|
if (this.http)
|
||||||
this.http.on('error', onError);
|
this.http.on('error', onError);
|
||||||
@ -217,6 +198,8 @@ FullNode.prototype._init = function _init() {
|
|||||||
}
|
}
|
||||||
self.emit('reset', tip);
|
self.emit('reset', tip);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.loadPlugins();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,10 +214,8 @@ FullNode.prototype._open = co(function* open() {
|
|||||||
yield this.mempool.open();
|
yield this.mempool.open();
|
||||||
yield this.miner.open();
|
yield this.miner.open();
|
||||||
yield this.pool.open();
|
yield this.pool.open();
|
||||||
yield this.walletdb.open();
|
|
||||||
|
|
||||||
// Ensure primary wallet.
|
yield this.openPlugins();
|
||||||
yield this.openWallet();
|
|
||||||
|
|
||||||
if (this.http)
|
if (this.http)
|
||||||
yield this.http.open();
|
yield this.http.open();
|
||||||
@ -252,11 +233,8 @@ FullNode.prototype._close = co(function* close() {
|
|||||||
if (this.http)
|
if (this.http)
|
||||||
yield this.http.close();
|
yield this.http.close();
|
||||||
|
|
||||||
yield this.wallet.destroy();
|
yield this.closePlugins();
|
||||||
|
|
||||||
this.wallet = null;
|
|
||||||
|
|
||||||
yield this.walletdb.close();
|
|
||||||
yield this.pool.close();
|
yield this.pool.close();
|
||||||
yield this.miner.close();
|
yield this.miner.close();
|
||||||
yield this.mempool.close();
|
yield this.mempool.close();
|
||||||
|
|||||||
@ -13,7 +13,6 @@ var util = require('../utils/util');
|
|||||||
var co = require('../utils/co');
|
var co = require('../utils/co');
|
||||||
var Network = require('../protocol/network');
|
var Network = require('../protocol/network');
|
||||||
var Logger = require('./logger');
|
var Logger = require('./logger');
|
||||||
var NodeClient = require('./nodeclient');
|
|
||||||
var workerPool = require('../workers/workerpool').pool;
|
var workerPool = require('../workers/workerpool').pool;
|
||||||
var ec = require('../crypto/ec');
|
var ec = require('../crypto/ec');
|
||||||
var native = require('../utils/native');
|
var native = require('../utils/native');
|
||||||
@ -48,8 +47,6 @@ function Node(options) {
|
|||||||
this.mempool = null;
|
this.mempool = null;
|
||||||
this.pool = null;
|
this.pool = null;
|
||||||
this.miner = null;
|
this.miner = null;
|
||||||
this.walletdb = null;
|
|
||||||
this.wallet = null;
|
|
||||||
this.http = null;
|
this.http = null;
|
||||||
this.client = null;
|
this.client = null;
|
||||||
|
|
||||||
@ -90,15 +87,15 @@ Node.prototype.init = function init() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.initOptions();
|
this.initOptions();
|
||||||
this.loadPlugins();
|
|
||||||
|
|
||||||
// Local client for walletdb
|
|
||||||
this.client = new NodeClient(this);
|
|
||||||
|
|
||||||
this.hook('preopen', function() {
|
this.hook('preopen', function() {
|
||||||
return self.handlePreopen();
|
return self.handlePreopen();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.hook('preopen', function() {
|
||||||
|
return self.handlePreclose();
|
||||||
|
});
|
||||||
|
|
||||||
this.hook('open', function() {
|
this.hook('open', function() {
|
||||||
return self.handleOpen();
|
return self.handleOpen();
|
||||||
});
|
});
|
||||||
@ -117,6 +114,7 @@ Node.prototype.handlePreopen = co(function* handlePreopen() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
yield fs.mkdirp(this.config.prefix);
|
yield fs.mkdirp(this.config.prefix);
|
||||||
|
|
||||||
yield this.logger.open();
|
yield this.logger.open();
|
||||||
|
|
||||||
this.bind(this.network.time, 'offset', function(offset) {
|
this.bind(this.network.time, 'offset', function(offset) {
|
||||||
@ -173,8 +171,14 @@ Node.prototype.handleOpen = co(function* handleOpen() {
|
|||||||
this.logger.warning('Warning: worker pool is disabled.');
|
this.logger.warning('Warning: worker pool is disabled.');
|
||||||
this.logger.warning('Verification will be slow.');
|
this.logger.warning('Verification will be slow.');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
yield this.openPlugins();
|
/**
|
||||||
|
* Open node. Bind all events.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Node.prototype.handlePreclose = co(function* handlePreclose() {
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,8 +189,6 @@ Node.prototype.handleOpen = co(function* handleOpen() {
|
|||||||
Node.prototype.handleClose = co(function* handleClose() {
|
Node.prototype.handleClose = co(function* handleClose() {
|
||||||
var i, bound;
|
var i, bound;
|
||||||
|
|
||||||
yield this.closePlugins();
|
|
||||||
|
|
||||||
this.startTime = -1;
|
this.startTime = -1;
|
||||||
|
|
||||||
for (i = 0; i < this.bound.length; i++) {
|
for (i = 0; i < this.bound.length; i++) {
|
||||||
@ -264,36 +266,6 @@ Node.prototype.uptime = function uptime() {
|
|||||||
return util.now() - this.startTime;
|
return util.now() - this.startTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Open and ensure primary wallet.
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Node.prototype.openWallet = co(function* openWallet() {
|
|
||||||
var options, wallet;
|
|
||||||
|
|
||||||
assert(!this.wallet);
|
|
||||||
|
|
||||||
options = {
|
|
||||||
id: 'primary',
|
|
||||||
passphrase: this.config.str('passphrase')
|
|
||||||
};
|
|
||||||
|
|
||||||
wallet = yield this.walletdb.ensure(options);
|
|
||||||
|
|
||||||
this.logger.info(
|
|
||||||
'Loaded wallet with id=%s wid=%d address=%s',
|
|
||||||
wallet.id, wallet.wid, wallet.getAddress());
|
|
||||||
|
|
||||||
if (this.miner && this.miner.addresses.length === 0)
|
|
||||||
this.miner.addAddress(wallet.getAddress());
|
|
||||||
|
|
||||||
this.wallet = wallet;
|
|
||||||
|
|
||||||
if (this.http && this.http.rpc && !this.http.rpc.wallet)
|
|
||||||
this.http.rpc.wallet = wallet;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach a plugin.
|
* Attach a plugin.
|
||||||
* @param {Object} plugin
|
* @param {Object} plugin
|
||||||
@ -312,25 +284,24 @@ Node.prototype.use = function use(plugin) {
|
|||||||
assert(typeof instance.open === 'function', '`open` must be a function.');
|
assert(typeof instance.open === 'function', '`open` must be a function.');
|
||||||
assert(typeof instance.close === 'function', '`close` must be a function.');
|
assert(typeof instance.close === 'function', '`close` must be a function.');
|
||||||
|
|
||||||
if (plugin.name) {
|
if (plugin.id) {
|
||||||
assert(typeof plugin.name === 'string', '`name` must be a string.');
|
assert(typeof plugin.id === 'string', '`name` must be a string.');
|
||||||
|
|
||||||
// Reserved names
|
// Reserved names
|
||||||
switch (plugin.name) {
|
switch (plugin.id) {
|
||||||
case 'logger':
|
case 'logger':
|
||||||
case 'chain':
|
case 'chain':
|
||||||
case 'fees':
|
case 'fees':
|
||||||
case 'mempool':
|
case 'mempool':
|
||||||
case 'miner':
|
case 'miner':
|
||||||
case 'pool':
|
case 'pool':
|
||||||
case 'walletdb':
|
assert(false, plugin.id + ' is already added.');
|
||||||
assert(false, plugin.name + ' is already added.');
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!this.plugins[plugin.name], plugin.name + ' is already added.');
|
assert(!this.plugins[plugin.id], plugin.id + ' is already added.');
|
||||||
|
|
||||||
this.plugins[plugin.name] = instance;
|
this.plugins[plugin.id] = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stack.push(instance);
|
this.stack.push(instance);
|
||||||
@ -366,9 +337,6 @@ Node.prototype.require = function require(name) {
|
|||||||
case 'pool':
|
case 'pool':
|
||||||
assert(this.pool, 'pool is not loaded.');
|
assert(this.pool, 'pool is not loaded.');
|
||||||
return this.pool;
|
return this.pool;
|
||||||
case 'walletdb':
|
|
||||||
assert(this.walletdb, 'walletdb is not loaded.');
|
|
||||||
return this.walletdb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin = this.plugins[name];
|
plugin = this.plugins[name];
|
||||||
@ -392,9 +360,16 @@ Node.prototype.loadPlugins = function loadPlugins() {
|
|||||||
|
|
||||||
for (i = 0; i < plugins.length; i++) {
|
for (i = 0; i < plugins.length; i++) {
|
||||||
name = plugins[i];
|
name = plugins[i];
|
||||||
|
|
||||||
assert(typeof name === 'string',
|
assert(typeof name === 'string',
|
||||||
'Plugin name must be a string.');
|
'Plugin name must be a string.');
|
||||||
|
|
||||||
|
// Temporary until we separate walletdb out.
|
||||||
|
if (name === 'walletdb')
|
||||||
|
name = __dirname + '/../wallet/walletdb';
|
||||||
|
|
||||||
plugin = loader(name);
|
plugin = loader(name);
|
||||||
|
|
||||||
this.use(plugin);
|
this.use(plugin);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,12 +13,11 @@ var Lock = require('../utils/lock');
|
|||||||
var Node = require('./node');
|
var Node = require('./node');
|
||||||
var Chain = require('../blockchain/chain');
|
var Chain = require('../blockchain/chain');
|
||||||
var Pool = require('../net/pool');
|
var Pool = require('../net/pool');
|
||||||
var WalletDB = require('../wallet/walletdb');
|
|
||||||
var HTTPServer = require('../http/server');
|
var HTTPServer = require('../http/server');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an spv node which only maintains
|
* Create an spv node which only maintains
|
||||||
* a chain, a pool, and a wallet database.
|
* a chain, a pool, and an http server.
|
||||||
* @alias module:node.SPVNode
|
* @alias module:node.SPVNode
|
||||||
* @extends Node
|
* @extends Node
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -27,11 +26,9 @@ var HTTPServer = require('../http/server');
|
|||||||
* @param {Buffer?} options.sslCert
|
* @param {Buffer?} options.sslCert
|
||||||
* @param {Number?} options.httpPort
|
* @param {Number?} options.httpPort
|
||||||
* @param {String?} options.httpHost
|
* @param {String?} options.httpHost
|
||||||
* @param {Object?} options.wallet - Primary {@link Wallet} options.
|
|
||||||
* @property {Boolean} loaded
|
* @property {Boolean} loaded
|
||||||
* @property {Chain} chain
|
* @property {Chain} chain
|
||||||
* @property {Pool} pool
|
* @property {Pool} pool
|
||||||
* @property {WalletDB} walletdb
|
|
||||||
* @property {HTTPServer} http
|
* @property {HTTPServer} http
|
||||||
* @emits SPVNode#block
|
* @emits SPVNode#block
|
||||||
* @emits SPVNode#tx
|
* @emits SPVNode#tx
|
||||||
@ -74,22 +71,6 @@ function SPVNode(options) {
|
|||||||
listen: false
|
listen: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.walletdb = new WalletDB({
|
|
||||||
network: this.network,
|
|
||||||
logger: this.logger,
|
|
||||||
client: this.client,
|
|
||||||
db: this.config.str('db'),
|
|
||||||
prefix: this.config.prefix,
|
|
||||||
maxFiles: this.config.num('max-files'),
|
|
||||||
cacheSize: this.config.mb('cache-size'),
|
|
||||||
witness: false,
|
|
||||||
checkpoints: this.config.bool('checkpoints'),
|
|
||||||
startHeight: this.config.num('start-height'),
|
|
||||||
wipeNoReally: this.config.bool('wipe-no-really'),
|
|
||||||
verify: true,
|
|
||||||
spv: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!HTTPServer.unsupported) {
|
if (!HTTPServer.unsupported) {
|
||||||
this.http = new HTTPServer({
|
this.http = new HTTPServer({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
@ -129,7 +110,6 @@ SPVNode.prototype._init = function _init() {
|
|||||||
// Bind to errors
|
// Bind to errors
|
||||||
this.chain.on('error', onError);
|
this.chain.on('error', onError);
|
||||||
this.pool.on('error', onError);
|
this.pool.on('error', onError);
|
||||||
this.walletdb.on('error', onError);
|
|
||||||
|
|
||||||
if (this.http)
|
if (this.http)
|
||||||
this.http.on('error', onError);
|
this.http.on('error', onError);
|
||||||
@ -165,6 +145,8 @@ SPVNode.prototype._init = function _init() {
|
|||||||
this.chain.on('reset', function(tip) {
|
this.chain.on('reset', function(tip) {
|
||||||
self.emit('reset', tip);
|
self.emit('reset', tip);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loadPlugins();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,10 +159,8 @@ SPVNode.prototype._init = function _init() {
|
|||||||
SPVNode.prototype._open = co(function* open(callback) {
|
SPVNode.prototype._open = co(function* open(callback) {
|
||||||
yield this.chain.open();
|
yield this.chain.open();
|
||||||
yield this.pool.open();
|
yield this.pool.open();
|
||||||
yield this.walletdb.open();
|
|
||||||
|
|
||||||
// Ensure primary wallet.
|
yield this.openPlugins();
|
||||||
yield this.openWallet();
|
|
||||||
|
|
||||||
if (this.http)
|
if (this.http)
|
||||||
yield this.http.open();
|
yield this.http.open();
|
||||||
@ -198,11 +178,8 @@ SPVNode.prototype._close = co(function* close() {
|
|||||||
if (this.http)
|
if (this.http)
|
||||||
yield this.http.close();
|
yield this.http.close();
|
||||||
|
|
||||||
yield this.wallet.destroy();
|
yield this.closePlugins();
|
||||||
|
|
||||||
this.wallet = null;
|
|
||||||
|
|
||||||
yield this.walletdb.close();
|
|
||||||
yield this.pool.close();
|
yield this.pool.close();
|
||||||
yield this.chain.close();
|
yield this.chain.close();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -31,3 +31,4 @@ exports.BufferReader = require('./reader');
|
|||||||
exports.StaticWriter = require('./staticwriter');
|
exports.StaticWriter = require('./staticwriter');
|
||||||
exports.util = require('./util');
|
exports.util = require('./util');
|
||||||
exports.BufferWriter = require('./writer');
|
exports.BufferWriter = require('./writer');
|
||||||
|
exports.Validator = require('./validator');
|
||||||
|
|||||||
@ -1037,3 +1037,30 @@ util.promisify = function promisify(func) {
|
|||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get memory usage info.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
util.memoryUsage = function memoryUsage() {
|
||||||
|
var mem;
|
||||||
|
|
||||||
|
if (!process.memoryUsage) {
|
||||||
|
return {
|
||||||
|
total: 0,
|
||||||
|
jsHeap: 0,
|
||||||
|
jsHeapTotal: 0,
|
||||||
|
nativeHeap: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = process.memoryUsage();
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: util.mb(mem.rss),
|
||||||
|
jsHeap: util.mb(mem.heapUsed),
|
||||||
|
jsHeapTotal: util.mb(mem.heapTotal),
|
||||||
|
nativeHeap: util.mb(mem.rss - mem.heapTotal)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
428
lib/utils/validator.js
Normal file
428
lib/utils/validator.js
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator
|
||||||
|
* @alias module:utils.Validator
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Validator(data) {
|
||||||
|
if (!(this instanceof Validator))
|
||||||
|
return new Validator(data);
|
||||||
|
|
||||||
|
this.data = [];
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
this.init(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether a config option is present.
|
||||||
|
* @param {String} key
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.init = function init(data) {
|
||||||
|
var i, obj;
|
||||||
|
|
||||||
|
assert(data && typeof data === 'object');
|
||||||
|
|
||||||
|
if (!Array.isArray(data))
|
||||||
|
data = [data];
|
||||||
|
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
obj = data[i];
|
||||||
|
assert(obj && typeof obj === 'object');
|
||||||
|
this.data.push(obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether a config option is present.
|
||||||
|
* @param {String} key
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.has = function has(key) {
|
||||||
|
var i, map, value;
|
||||||
|
|
||||||
|
assert(typeof key === 'string' || typeof key === 'number',
|
||||||
|
'Key must be a string.');
|
||||||
|
|
||||||
|
for (i = 0; i < this.data.length; i++) {
|
||||||
|
map = this.data[i];
|
||||||
|
value = map[key];
|
||||||
|
if (value != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option.
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Object|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.get = function get(key, fallback) {
|
||||||
|
var i, keys, value, map;
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (Array.isArray(key)) {
|
||||||
|
keys = key;
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
key = keys[i];
|
||||||
|
value = this.get(key);
|
||||||
|
if (value !== null)
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(typeof key === 'string' || typeof key === 'number',
|
||||||
|
'Key must be a string.');
|
||||||
|
|
||||||
|
for (i = 0; i < this.data.length; i++) {
|
||||||
|
map = this.data[i];
|
||||||
|
value = map[key];
|
||||||
|
if (value != null)
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a string).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {String|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.str = function str(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string')
|
||||||
|
throw new Error(key + ' must be a string.');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a number).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Number|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.num = function num(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (typeof value !== 'number')
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^\d+$/.test(value))
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
|
value = parseInt(value, 10);
|
||||||
|
|
||||||
|
if (!isFinite(value))
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a number).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Number|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.amt = function amt(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (typeof value !== 'number')
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^\d+(\.\d{0,8})?$/.test(value))
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
|
value = parseFloat(value);
|
||||||
|
|
||||||
|
if (!isFinite(value))
|
||||||
|
throw new Error(key + ' must be a number.');
|
||||||
|
|
||||||
|
return value * 1e8;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a number).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Number|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.hash = function hash(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
var out = '';
|
||||||
|
var i;
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (!Buffer.isBuffer(value))
|
||||||
|
throw new Error(key + ' must be a buffer.');
|
||||||
|
if (value.length !== 32)
|
||||||
|
throw new Error(key + ' must be a buffer.');
|
||||||
|
return value.toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.length !== 64)
|
||||||
|
throw new Error(key + ' must be a hex string.');
|
||||||
|
|
||||||
|
if (!/^[0-9a-f]+$/i.test(value))
|
||||||
|
throw new Error(key + ' must be a hex string.');
|
||||||
|
|
||||||
|
for (i = 0; i < value.length; i += 2)
|
||||||
|
out = value.slice(i, i + 2) + out;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a number).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Number|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.numstr = function numstr(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
var num;
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (typeof value !== 'number')
|
||||||
|
throw new Error(key + ' must be a number or string.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = parseInt(value, 10);
|
||||||
|
|
||||||
|
if (!isFinite(num))
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a boolean).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Boolean|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.bool = function bool(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
assert(typeof value === 'boolean',
|
||||||
|
'Passed in config option is of wrong type.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'true' || value === '1')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (value === 'false' || value === '0')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
throw new Error(key + ' must be a boolean.');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a buffer).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Buffer|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.buf = function buf(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
var data;
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
assert(Buffer.isBuffer(value),
|
||||||
|
'Passed in config option is of wrong type.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new Buffer(value, 'hex');
|
||||||
|
|
||||||
|
if (data.length !== value.length / 2)
|
||||||
|
throw new Error(key + ' must be a hex string.');
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as an array of strings).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {String[]|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.array = function array(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
throw new Error(key + ' must be a list/array.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.trim().split(/\s*,\s*/);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as an object).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Object|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.obj = function obj(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (!value || typeof value !== 'object')
|
||||||
|
throw new Error(key + ' must be an object.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value || typeof value !== 'object')
|
||||||
|
throw new Error(key + ' must be an object.');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as an object).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Object|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.next = function next(key, fallback) {
|
||||||
|
var value = this.obj(key, fallback);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
return new Validator(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a config option (as a function).
|
||||||
|
* @param {String} key
|
||||||
|
* @param {Object?} fallback
|
||||||
|
* @returns {Function|null}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Validator.prototype.func = function func(key, fallback) {
|
||||||
|
var value = this.get(key);
|
||||||
|
|
||||||
|
if (fallback === undefined)
|
||||||
|
fallback = null;
|
||||||
|
|
||||||
|
if (value === null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
if (typeof value !== 'function')
|
||||||
|
throw new Error(key + ' must be a function.');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(key + ' must be a function.');
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Validator;
|
||||||
@ -23,6 +23,9 @@ common.isName = function isName(key) {
|
|||||||
if (typeof key !== 'string')
|
if (typeof key !== 'string')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (key === '_admin')
|
||||||
|
return false;
|
||||||
|
|
||||||
if (key === '__proto__')
|
if (key === '__proto__')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
1087
lib/wallet/http.js
Normal file
1087
lib/wallet/http.js
Normal file
File diff suppressed because it is too large
Load Diff
1847
lib/wallet/rpc.js
Normal file
1847
lib/wallet/rpc.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1424,7 +1424,7 @@ Wallet.prototype._fund = co(function* fund(mtx, options) {
|
|||||||
rate = options.rate;
|
rate = options.rate;
|
||||||
|
|
||||||
if (rate == null)
|
if (rate == null)
|
||||||
rate = yield this.db.estimateFee();
|
rate = yield this.db.estimateFee(options.blocks);
|
||||||
|
|
||||||
if (options.smart) {
|
if (options.smart) {
|
||||||
coins = yield this.getSmartCoins(options.account);
|
coins = yield this.getSmartCoins(options.account);
|
||||||
|
|||||||
@ -26,6 +26,8 @@ var Logger = require('../node/logger');
|
|||||||
var Outpoint = require('../primitives/outpoint');
|
var Outpoint = require('../primitives/outpoint');
|
||||||
var layouts = require('./layout');
|
var layouts = require('./layout');
|
||||||
var records = require('./records');
|
var records = require('./records');
|
||||||
|
var NodeClient = require('../node/nodeclient');
|
||||||
|
var HTTP = require('./http');
|
||||||
var layout = layouts.walletdb;
|
var layout = layouts.walletdb;
|
||||||
var ChainState = records.ChainState;
|
var ChainState = records.ChainState;
|
||||||
var BlockMapRecord = records.BlockMapRecord;
|
var BlockMapRecord = records.BlockMapRecord;
|
||||||
@ -62,6 +64,20 @@ function WalletDB(options) {
|
|||||||
this.logger = this.options.logger;
|
this.logger = this.options.logger;
|
||||||
this.client = this.options.client;
|
this.client = this.options.client;
|
||||||
this.db = LDB(this.options);
|
this.db = LDB(this.options);
|
||||||
|
this.primary = null;
|
||||||
|
|
||||||
|
this.http = new HTTP({
|
||||||
|
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.state = new ChainState();
|
||||||
this.wallets = Object.create(null);
|
this.wallets = Object.create(null);
|
||||||
@ -83,6 +99,54 @@ function WalletDB(options) {
|
|||||||
|
|
||||||
util.inherits(WalletDB, AsyncObject);
|
util.inherits(WalletDB, AsyncObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin name.
|
||||||
|
* @const {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.id = 'walletdb';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin initialization.
|
||||||
|
* @param {Node} node
|
||||||
|
* @returns {WalletDB}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.init = function init(node) {
|
||||||
|
var config = node.config;
|
||||||
|
var client = new NodeClient(node);
|
||||||
|
var wdb;
|
||||||
|
|
||||||
|
wdb = new WalletDB({
|
||||||
|
network: node.network,
|
||||||
|
logger: node.logger,
|
||||||
|
client: client,
|
||||||
|
prefix: config.prefix,
|
||||||
|
db: config.str(['wallet-db', 'db']),
|
||||||
|
maxFiles: config.num('wallet-max-files'),
|
||||||
|
cacheSize: config.mb('wallet-cache-size'),
|
||||||
|
witness: config.bool('wallet-witness'),
|
||||||
|
checkpoints: config.bool('wallet-checkpoints'),
|
||||||
|
startHeight: config.num('wallet-start-height'),
|
||||||
|
wipeNoReally: config.bool('wallet-wipe-no-really'),
|
||||||
|
apiKey: config.str(['wallet-api-key', 'api-key']),
|
||||||
|
walletAuth: config.bool('wallet-auth'),
|
||||||
|
noAuth: config.bool('no-auth'),
|
||||||
|
ssl: config.str('wallet-ssl'),
|
||||||
|
host: config.str('wallet-host'),
|
||||||
|
port: config.num('wallet-port'),
|
||||||
|
spv: node.chain.options.spv,
|
||||||
|
verify: node.chain.options.spv
|
||||||
|
});
|
||||||
|
|
||||||
|
if (node.http) {
|
||||||
|
wdb.http.attach(node.http);
|
||||||
|
wdb.http.rpc.attach(node.http.rpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wdb;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database layout.
|
* Database layout.
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
@ -119,6 +183,8 @@ WalletDB.prototype._init = function _init() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype._open = co(function* open() {
|
WalletDB.prototype._open = co(function* open() {
|
||||||
|
var wallet;
|
||||||
|
|
||||||
yield this.db.open();
|
yield this.db.open();
|
||||||
yield this.db.checkVersion('V', 6);
|
yield this.db.checkVersion('V', 6);
|
||||||
|
|
||||||
@ -134,6 +200,17 @@ WalletDB.prototype._open = co(function* open() {
|
|||||||
this.depth,
|
this.depth,
|
||||||
this.state.height,
|
this.state.height,
|
||||||
this.state.startHeight);
|
this.state.startHeight);
|
||||||
|
|
||||||
|
wallet = yield this.ensure({
|
||||||
|
id: 'primary'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.info(
|
||||||
|
'Loaded wallet with id=%s wid=%d address=%s',
|
||||||
|
wallet.id, wallet.wid, wallet.getAddress());
|
||||||
|
|
||||||
|
this.primary = wallet;
|
||||||
|
this.http.rpc.wallet = wallet;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2185,6 +2262,12 @@ function WalletOptions(options) {
|
|||||||
this.startHeight = 0;
|
this.startHeight = 0;
|
||||||
this.keepBlocks = this.network.block.keepBlocks;
|
this.keepBlocks = this.network.block.keepBlocks;
|
||||||
this.wipeNoReally = false;
|
this.wipeNoReally = false;
|
||||||
|
this.apiKey = null;
|
||||||
|
this.walletAuth = false;
|
||||||
|
this.noAuth = false;
|
||||||
|
this.ssl = false;
|
||||||
|
this.host = '127.0.0.1';
|
||||||
|
this.port = this.network.rpcPort + 2;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
@ -2201,6 +2284,7 @@ WalletOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
if (options.network != null) {
|
if (options.network != null) {
|
||||||
this.network = Network.get(options.network);
|
this.network = Network.get(options.network);
|
||||||
this.keepBlocks = this.network.block.keepBlocks;
|
this.keepBlocks = this.network.block.keepBlocks;
|
||||||
|
this.port = this.network.rpcPort + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.logger != null) {
|
if (options.logger != null) {
|
||||||
@ -2270,6 +2354,36 @@ WalletOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.wipeNoReally = options.wipeNoReally;
|
this.wipeNoReally = options.wipeNoReally;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.apiKey != null) {
|
||||||
|
assert(typeof options.apiKey === 'string');
|
||||||
|
this.apiKey = options.apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.walletAuth != null) {
|
||||||
|
assert(typeof options.walletAuth === 'boolean');
|
||||||
|
this.walletAuth = options.walletAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.noAuth != null) {
|
||||||
|
assert(typeof options.noAuth === 'boolean');
|
||||||
|
this.noAuth = options.noAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.ssl != null) {
|
||||||
|
assert(typeof options.ssl === 'boolean');
|
||||||
|
this.ssl = options.ssl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.host != null) {
|
||||||
|
assert(typeof options.host === 'string');
|
||||||
|
this.host = options.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.port != null) {
|
||||||
|
assert(typeof options.port === 'number');
|
||||||
|
this.port = options.port;
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -13,13 +13,15 @@ var FullNode = require('../lib/node/fullnode');
|
|||||||
var pkg = require('../lib/pkg');
|
var pkg = require('../lib/pkg');
|
||||||
|
|
||||||
describe('HTTP', function() {
|
describe('HTTP', function() {
|
||||||
var node, wallet, addr, hash;
|
var node, wallet, walletdb, addr, hash;
|
||||||
|
|
||||||
node = new FullNode({
|
node = new FullNode({
|
||||||
network: 'regtest',
|
network: 'regtest',
|
||||||
apiKey: 'foo',
|
apiKey: 'foo',
|
||||||
walletAuth: true,
|
walletAuth: true,
|
||||||
db: 'memory'
|
db: 'memory',
|
||||||
|
loader: require,
|
||||||
|
plugins: ['walletdb']
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet = new HTTP.Wallet({
|
wallet = new HTTP.Wallet({
|
||||||
@ -27,6 +29,8 @@ describe('HTTP', function() {
|
|||||||
apiKey: 'foo'
|
apiKey: 'foo'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
walletdb = node.require('walletdb');
|
||||||
|
|
||||||
node.on('error', function() {});
|
node.on('error', function() {});
|
||||||
|
|
||||||
this.timeout(15000);
|
this.timeout(15000);
|
||||||
@ -82,7 +86,7 @@ describe('HTTP', function() {
|
|||||||
details = d;
|
details = d;
|
||||||
});
|
});
|
||||||
|
|
||||||
yield node.walletdb.addTX(tx);
|
yield walletdb.addTX(tx);
|
||||||
yield co.timeout(300);
|
yield co.timeout(300);
|
||||||
|
|
||||||
assert(receive);
|
assert(receive);
|
||||||
|
|||||||
@ -11,14 +11,19 @@ var MTX = require('../lib/primitives/mtx');
|
|||||||
// var Client = require('../lib/wallet/client');
|
// var Client = require('../lib/wallet/client');
|
||||||
|
|
||||||
describe('Node', function() {
|
describe('Node', function() {
|
||||||
var node = new FullNode({ db: 'memory', apiKey: 'foo', network: 'regtest' });
|
var node = new FullNode({
|
||||||
|
db: 'memory',
|
||||||
|
apiKey: 'foo',
|
||||||
|
network: 'regtest',
|
||||||
|
loader: require,
|
||||||
|
plugins: ['../lib/wallet/walletdb']
|
||||||
|
});
|
||||||
var chain = node.chain;
|
var chain = node.chain;
|
||||||
var walletdb = node.walletdb;
|
var walletdb = node.require('walletdb');
|
||||||
var miner = node.miner;
|
var miner = node.miner;
|
||||||
var wallet, tip1, tip2, cb1, cb2, mineBlock;
|
var wallet, tip1, tip2, cb1, cb2, mineBlock;
|
||||||
|
|
||||||
// walletdb.client = new Client({ apiKey: 'foo', network: 'regtest' });
|
// walletdb.client = new Client({ apiKey: 'foo', network: 'regtest' });
|
||||||
walletdb.options.resolution = false;
|
|
||||||
|
|
||||||
node.on('error', function() {});
|
node.on('error', function() {});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user