wallet: get server working.
This commit is contained in:
parent
d0ed214067
commit
5a23ba96d0
55
bin/bwallet
Executable file
55
bin/bwallet
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rl=0
|
||||||
|
daemon=0
|
||||||
|
|
||||||
|
if ! type perl > /dev/null 2>& 1; then
|
||||||
|
if uname | grep -i 'darwin' > /dev/null; then
|
||||||
|
echo 'Bcoin requires perl to start on OSX.' >& 2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rl=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $rl -eq 1; then
|
||||||
|
file=$(readlink -f "$0")
|
||||||
|
else
|
||||||
|
# Have to do it this way
|
||||||
|
# because OSX isn't a real OS
|
||||||
|
file=$(perl -MCwd -e "print Cwd::realpath('$0')")
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
|
||||||
|
if test x"$1" = x'cli'; then
|
||||||
|
shift
|
||||||
|
exec "${dir}/cli" "wallet" "$@"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--daemon)
|
||||||
|
daemon=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if test $daemon -eq 1; then
|
||||||
|
# And yet again, OSX doesn't support something.
|
||||||
|
if ! type setsid > /dev/null 2>& 1; then
|
||||||
|
(
|
||||||
|
"${dir}/wallet" "$@" > /dev/null 2>& 1 &
|
||||||
|
echo "$!"
|
||||||
|
)
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
(
|
||||||
|
setsid "${dir}/wallet" "$@" > /dev/null 2>& 1 &
|
||||||
|
echo "$!"
|
||||||
|
)
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exec "${dir}/wallet" "$@"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
6
bin/cli
6
bin/cli
@ -17,7 +17,11 @@ const ANTIREPLAY = ''
|
|||||||
+ '220456c656374726f6e696320436173682053797374656d';
|
+ '220456c656374726f6e696320436173682053797374656d';
|
||||||
|
|
||||||
function CLI() {
|
function CLI() {
|
||||||
this.config = new Config('bcoin');
|
this.config = new Config('bcoin', {
|
||||||
|
suffix: 'network',
|
||||||
|
fallback: 'main',
|
||||||
|
alias: { 'n': 'network' }
|
||||||
|
});
|
||||||
|
|
||||||
this.config.load({
|
this.config.load({
|
||||||
argv: true,
|
argv: true,
|
||||||
|
|||||||
34
bin/wallet
34
bin/wallet
@ -2,24 +2,46 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
process.title = 'bcoin-wallet';
|
process.title = 'bwallet';
|
||||||
|
|
||||||
const server = require('../lib/wallet/server');
|
if (process.argv.indexOf('--help') !== -1
|
||||||
|
|| process.argv.indexOf('-h') !== -1) {
|
||||||
|
console.error('See the bcoin wiki at: https://github.com/bcoin-org/bcoin/wiki.');
|
||||||
|
process.exit(1);
|
||||||
|
throw new Error('Could not exit.');
|
||||||
|
}
|
||||||
|
|
||||||
const wdb = server.create({
|
if (process.argv.indexOf('--version') !== -1
|
||||||
|
|| process.argv.indexOf('-v') !== -1) {
|
||||||
|
const pkg = require('../package.json');
|
||||||
|
console.log(pkg.version);
|
||||||
|
process.exit(0);
|
||||||
|
throw new Error('Could not exit.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const Node = require('../lib/wallet/server');
|
||||||
|
|
||||||
|
const node = new Node({
|
||||||
config: true,
|
config: true,
|
||||||
argv: true,
|
argv: true,
|
||||||
env: true,
|
env: true,
|
||||||
logFile: true,
|
logFile: true,
|
||||||
logConsole: true,
|
logConsole: true,
|
||||||
logLevel: 'debug',
|
logLevel: 'debug',
|
||||||
db: 'leveldb'
|
db: 'leveldb',
|
||||||
|
workers: true,
|
||||||
|
listen: true,
|
||||||
|
loader: require
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('unhandledRejection', (err, promise) => {
|
process.on('unhandledRejection', (err, promise) => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
wdb.open().catch((err) => {
|
(async () => {
|
||||||
throw err;
|
await node.ensure();
|
||||||
|
await node.open();
|
||||||
|
})().catch((err) => {
|
||||||
|
console.error(err.stack);
|
||||||
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -366,7 +366,7 @@ class HTTPServer extends Server {
|
|||||||
|
|
||||||
socket.join('auth');
|
socket.join('auth');
|
||||||
|
|
||||||
this.logger.info('Successful auth from %s.', socket.remoteAddress);
|
this.logger.info('Successful auth from %s.', socket.host);
|
||||||
this.handleAuth(socket);
|
this.handleAuth(socket);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -465,7 +465,7 @@ class HTTPServer extends Server {
|
|||||||
if (!data)
|
if (!data)
|
||||||
throw new Error('Bad data chunk.');
|
throw new Error('Bad data chunk.');
|
||||||
|
|
||||||
this.filter.add(data);
|
socket.filter.add(data);
|
||||||
|
|
||||||
if (this.node.spv)
|
if (this.node.spv)
|
||||||
this.pool.watch(data);
|
this.pool.watch(data);
|
||||||
@ -655,7 +655,7 @@ class HTTPServer extends Server {
|
|||||||
for (const tx of txs)
|
for (const tx of txs)
|
||||||
raw.push(tx.toRaw());
|
raw.push(tx.toRaw());
|
||||||
|
|
||||||
socket.fire('block rescan', block, raw);
|
return socket.fire('block rescan', block, raw);
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,18 +18,21 @@ const HOME = os.homedir ? os.homedir() : '/';
|
|||||||
* @alias module:node.Config
|
* @alias module:node.Config
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {String} module - Module name (e.g. `bcoin`).
|
* @param {String} module - Module name (e.g. `bcoin`).
|
||||||
|
* @param {Object?} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Config(module) {
|
function Config(module, options) {
|
||||||
if (!(this instanceof Config))
|
if (!(this instanceof Config))
|
||||||
return new Config(module);
|
return new Config(module, options);
|
||||||
|
|
||||||
assert(typeof module === 'string');
|
assert(typeof module === 'string');
|
||||||
assert(module.length > 0);
|
assert(module.length > 0);
|
||||||
|
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.network = 'main';
|
|
||||||
this.prefix = Path.join(HOME, `.${module}`);
|
this.prefix = Path.join(HOME, `.${module}`);
|
||||||
|
this.suffix = null;
|
||||||
|
this.fallback = null;
|
||||||
|
this.alias = Object.create(null);
|
||||||
|
|
||||||
this.options = Object.create(null);
|
this.options = Object.create(null);
|
||||||
this.data = Object.create(null);
|
this.data = Object.create(null);
|
||||||
@ -39,18 +42,39 @@ function Config(module) {
|
|||||||
this.pass = [];
|
this.pass = [];
|
||||||
this.query = Object.create(null);
|
this.query = Object.create(null);
|
||||||
this.hash = Object.create(null);
|
this.hash = Object.create(null);
|
||||||
|
|
||||||
|
if (options)
|
||||||
|
this.init(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option name aliases.
|
* Initialize options.
|
||||||
* @const {Object}
|
* @private
|
||||||
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Config.alias = {
|
Config.prototype.init = function init(options) {
|
||||||
'seed': 'seeds',
|
assert(options && typeof options === 'object');
|
||||||
'node': 'nodes',
|
|
||||||
'n': 'network'
|
if (options.suffix != null) {
|
||||||
};
|
assert(typeof options.suffix === 'string');
|
||||||
|
this.suffix = options.suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.fallback != null) {
|
||||||
|
assert(typeof options.fallback === 'string');
|
||||||
|
this.fallback = options.fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.alias) {
|
||||||
|
assert(typeof options.alias === 'object');
|
||||||
|
for (const key of Object.keys(options.alias)) {
|
||||||
|
const value = options.alias[key];
|
||||||
|
assert(typeof value === 'string');
|
||||||
|
this.alias[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject options.
|
* Inject options.
|
||||||
@ -92,7 +116,6 @@ Config.prototype.load = function load(options) {
|
|||||||
if (options.argv)
|
if (options.argv)
|
||||||
this.parseArg(options.argv);
|
this.parseArg(options.argv);
|
||||||
|
|
||||||
this.network = this.getNetwork();
|
|
||||||
this.prefix = this.getPrefix();
|
this.prefix = this.getPrefix();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,7 +142,6 @@ Config.prototype.open = function open(file) {
|
|||||||
|
|
||||||
this.parseConfig(text);
|
this.parseConfig(text);
|
||||||
|
|
||||||
this.network = this.getNetwork();
|
|
||||||
this.prefix = this.getPrefix();
|
this.prefix = this.getPrefix();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -650,20 +672,19 @@ Config.prototype.mb = function mb(key, fallback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grab network type from config data.
|
* Grab suffix from config data.
|
||||||
* @private
|
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Config.prototype.getNetwork = function getNetwork() {
|
Config.prototype.getSuffix = function getSuffix() {
|
||||||
let network = this.str('network');
|
if (!this.suffix)
|
||||||
|
throw new Error('No suffix presented.');
|
||||||
|
|
||||||
if (!network)
|
const suffix = this.str(this.suffix, this.fallback);
|
||||||
network = 'main';
|
|
||||||
|
|
||||||
assert(isAlpha(network), 'Bad network.');
|
assert(isAlpha(suffix), 'Bad suffix.');
|
||||||
|
|
||||||
return network;
|
return suffix;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -678,17 +699,18 @@ Config.prototype.getPrefix = function getPrefix() {
|
|||||||
if (prefix) {
|
if (prefix) {
|
||||||
if (prefix[0] === '~')
|
if (prefix[0] === '~')
|
||||||
prefix = Path.join(HOME, prefix.substring(1));
|
prefix = Path.join(HOME, prefix.substring(1));
|
||||||
return prefix;
|
} else {
|
||||||
|
prefix = Path.join(HOME, `.${this.module}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix = Path.join(HOME, `.${this.module}`);
|
if (this.suffix) {
|
||||||
|
const suffix = this.str(this.suffix);
|
||||||
|
|
||||||
const network = this.str('network');
|
if (suffix) {
|
||||||
|
assert(isAlpha(suffix), 'Bad suffix.');
|
||||||
if (network) {
|
if (this.fallback && suffix !== this.fallback)
|
||||||
assert(isAlpha(network), 'Bad network.');
|
prefix = Path.join(prefix, suffix);
|
||||||
if (network !== 'main')
|
}
|
||||||
prefix = Path.join(prefix, network);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Path.normalize(prefix);
|
return Path.normalize(prefix);
|
||||||
@ -748,8 +770,6 @@ Config.prototype.parseConfig = function parseConfig(text) {
|
|||||||
text = text.replace(/\r/g, '\n');
|
text = text.replace(/\r/g, '\n');
|
||||||
text = text.replace(/\\\n/g, '');
|
text = text.replace(/\\\n/g, '');
|
||||||
|
|
||||||
let colons = true;
|
|
||||||
let seen = false;
|
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
||||||
for (const chunk of text.split('\n')) {
|
for (const chunk of text.split('\n')) {
|
||||||
@ -763,29 +783,10 @@ Config.prototype.parseConfig = function parseConfig(text) {
|
|||||||
if (line[0] === '#')
|
if (line[0] === '#')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const equal = line.indexOf('=');
|
const index = line.indexOf(':');
|
||||||
const colon = line.indexOf(':');
|
|
||||||
|
|
||||||
let index = -1;
|
if (index === -1)
|
||||||
|
throw new Error(`Expected ':' on line ${num}: "${line}".`);
|
||||||
if (colon !== -1 && (colon < equal || equal === -1)) {
|
|
||||||
if (seen && !colons)
|
|
||||||
throw new Error(`Expected '=' on line ${num}: "${line}".`);
|
|
||||||
|
|
||||||
index = colon;
|
|
||||||
seen = true;
|
|
||||||
colons = true;
|
|
||||||
} else if (equal !== -1) {
|
|
||||||
if (seen && colons)
|
|
||||||
throw new Error(`Expected ':' on line ${num}: "${line}".`);
|
|
||||||
|
|
||||||
index = equal;
|
|
||||||
seen = true;
|
|
||||||
colons = false;
|
|
||||||
} else {
|
|
||||||
const symbol = colons ? ':' : '=';
|
|
||||||
throw new Error(`Expected '${symbol}' on line ${num}: "${line}".`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = line.substring(0, index).trim();
|
let key = line.substring(0, index).trim();
|
||||||
|
|
||||||
@ -799,7 +800,7 @@ Config.prototype.parseConfig = function parseConfig(text) {
|
|||||||
if (value.length === 0)
|
if (value.length === 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const alias = Config.alias[key];
|
const alias = this.alias[key];
|
||||||
|
|
||||||
if (alias)
|
if (alias)
|
||||||
key = alias;
|
key = alias;
|
||||||
@ -874,7 +875,7 @@ Config.prototype.parseArg = function parseArg(argv) {
|
|||||||
|
|
||||||
// Do not allow one-letter aliases.
|
// Do not allow one-letter aliases.
|
||||||
if (key.length > 1) {
|
if (key.length > 1) {
|
||||||
const alias = Config.alias[key];
|
const alias = this.alias[key];
|
||||||
if (alias)
|
if (alias)
|
||||||
key = alias;
|
key = alias;
|
||||||
}
|
}
|
||||||
@ -901,7 +902,7 @@ Config.prototype.parseArg = function parseArg(argv) {
|
|||||||
throw new Error(`Invalid argument: -${key}.`);
|
throw new Error(`Invalid argument: -${key}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alias = Config.alias[key];
|
const alias = this.alias[key];
|
||||||
|
|
||||||
if (alias)
|
if (alias)
|
||||||
key = alias;
|
key = alias;
|
||||||
@ -971,7 +972,7 @@ Config.prototype.parseEnv = function parseEnv(env) {
|
|||||||
|
|
||||||
// Do not allow one-letter aliases.
|
// Do not allow one-letter aliases.
|
||||||
if (key.length > 1) {
|
if (key.length > 1) {
|
||||||
const alias = Config.alias[key];
|
const alias = this.alias[key];
|
||||||
if (alias)
|
if (alias)
|
||||||
key = alias;
|
key = alias;
|
||||||
}
|
}
|
||||||
@ -1063,7 +1064,7 @@ Config.prototype.parseForm = function parseForm(query, map) {
|
|||||||
if (value.length === 0)
|
if (value.length === 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const alias = Config.alias[key];
|
const alias = this.alias[key];
|
||||||
|
|
||||||
if (alias)
|
if (alias)
|
||||||
key = alias;
|
key = alias;
|
||||||
@ -1098,7 +1099,7 @@ function unescape(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isAlpha(str) {
|
function isAlpha(str) {
|
||||||
return /^[a-z0-9]+$/.test(str);
|
return /^[a-z0-9_\-]+$/i.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isKey(key) {
|
function isKey(key) {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ function FullNode(options) {
|
|||||||
if (!(this instanceof FullNode))
|
if (!(this instanceof FullNode))
|
||||||
return new FullNode(options);
|
return new FullNode(options);
|
||||||
|
|
||||||
Node.call(this, options);
|
Node.call(this, 'bcoin', 'bcoin.conf', 'debug.log', options);
|
||||||
|
|
||||||
// SPV flag.
|
// SPV flag.
|
||||||
this.spv = false;
|
this.spv = false;
|
||||||
|
|||||||
@ -26,20 +26,25 @@ const Config = require('./config');
|
|||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Node(options) {
|
function Node(module, config, file, options) {
|
||||||
if (!(this instanceof Node))
|
if (!(this instanceof Node))
|
||||||
return new Node(options);
|
return new Node(module, config, file, options);
|
||||||
|
|
||||||
AsyncObject.call(this);
|
AsyncObject.call(this);
|
||||||
|
|
||||||
this.config = new Config('bcoin');
|
this.config = new Config(module, {
|
||||||
|
suffix: 'network',
|
||||||
|
fallback: 'main',
|
||||||
|
alias: { 'n': 'network' }
|
||||||
|
});
|
||||||
|
|
||||||
this.config.inject(options);
|
this.config.inject(options);
|
||||||
this.config.load(options);
|
this.config.load(options);
|
||||||
|
|
||||||
if (options.config)
|
if (options.config)
|
||||||
this.config.open('bcoin.conf');
|
this.config.open(config);
|
||||||
|
|
||||||
this.network = Network.get(this.config.network);
|
this.network = Network.get(this.config.getSuffix());
|
||||||
this.startTime = -1;
|
this.startTime = -1;
|
||||||
this.bound = [];
|
this.bound = [];
|
||||||
this.plugins = Object.create(null);
|
this.plugins = Object.create(null);
|
||||||
@ -56,7 +61,7 @@ function Node(options) {
|
|||||||
this.miner = null;
|
this.miner = null;
|
||||||
this.http = null;
|
this.http = null;
|
||||||
|
|
||||||
this.init();
|
this.init(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.setPrototypeOf(Node.prototype, AsyncObject.prototype);
|
Object.setPrototypeOf(Node.prototype, AsyncObject.prototype);
|
||||||
@ -67,16 +72,17 @@ Object.setPrototypeOf(Node.prototype, AsyncObject.prototype);
|
|||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Node.prototype.initOptions = function initOptions() {
|
Node.prototype.initOptions = function initOptions(file) {
|
||||||
let logger = new Logger();
|
|
||||||
const config = this.config;
|
const config = this.config;
|
||||||
|
|
||||||
|
let logger = new Logger();
|
||||||
|
|
||||||
if (config.has('logger'))
|
if (config.has('logger'))
|
||||||
logger = config.obj('logger');
|
logger = config.obj('logger');
|
||||||
|
|
||||||
logger.set({
|
logger.set({
|
||||||
filename: config.bool('log-file')
|
filename: config.bool('log-file')
|
||||||
? config.location('debug.log')
|
? config.location(file)
|
||||||
: null,
|
: null,
|
||||||
level: config.str('log-level'),
|
level: config.str('log-level'),
|
||||||
console: config.bool('log-console'),
|
console: config.bool('log-console'),
|
||||||
@ -99,8 +105,8 @@ Node.prototype.initOptions = function initOptions() {
|
|||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Node.prototype.init = function init() {
|
Node.prototype.init = function init(file) {
|
||||||
this.initOptions();
|
this.initOptions(file);
|
||||||
|
|
||||||
this.on('error', () => {});
|
this.on('error', () => {});
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ function SPVNode(options) {
|
|||||||
if (!(this instanceof SPVNode))
|
if (!(this instanceof SPVNode))
|
||||||
return new SPVNode(options);
|
return new SPVNode(options);
|
||||||
|
|
||||||
Node.call(this, options);
|
Node.call(this, 'bcoin', 'bcoin.conf', 'debug.log', options);
|
||||||
|
|
||||||
// SPV flag.
|
// SPV flag.
|
||||||
this.spv = true;
|
this.spv = true;
|
||||||
@ -80,21 +80,19 @@ function SPVNode(options) {
|
|||||||
|
|
||||||
this.rpc = new RPC(this);
|
this.rpc = new RPC(this);
|
||||||
|
|
||||||
if (!HTTPServer.unsupported) {
|
this.http = new HTTPServer({
|
||||||
this.http = new HTTPServer({
|
network: this.network,
|
||||||
network: this.network,
|
logger: this.logger,
|
||||||
logger: this.logger,
|
node: this,
|
||||||
node: this,
|
prefix: this.config.prefix,
|
||||||
prefix: this.config.prefix,
|
ssl: this.config.bool('ssl'),
|
||||||
ssl: this.config.bool('ssl'),
|
keyFile: this.config.path('ssl-key'),
|
||||||
keyFile: this.config.path('ssl-key'),
|
certFile: this.config.path('ssl-cert'),
|
||||||
certFile: this.config.path('ssl-cert'),
|
host: this.config.str('http-host'),
|
||||||
host: this.config.str('http-host'),
|
port: this.config.uint('http-port'),
|
||||||
port: this.config.uint('http-port'),
|
apiKey: this.config.str('api-key'),
|
||||||
apiKey: this.config.str('api-key'),
|
noAuth: this.config.bool('no-auth')
|
||||||
noAuth: this.config.bool('no-auth')
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rescanJob = null;
|
this.rescanJob = null;
|
||||||
this.scanLock = new Lock();
|
this.scanLock = new Lock();
|
||||||
|
|||||||
@ -48,6 +48,7 @@ function Network(options) {
|
|||||||
this.addressPrefix = options.addressPrefix;
|
this.addressPrefix = options.addressPrefix;
|
||||||
this.requireStandard = options.requireStandard;
|
this.requireStandard = options.requireStandard;
|
||||||
this.rpcPort = options.rpcPort;
|
this.rpcPort = options.rpcPort;
|
||||||
|
this.walletPort = options.walletPort;
|
||||||
this.minRelay = options.minRelay;
|
this.minRelay = options.minRelay;
|
||||||
this.feeRate = options.feeRate;
|
this.feeRate = options.feeRate;
|
||||||
this.maxFeeRate = options.maxFeeRate;
|
this.maxFeeRate = options.maxFeeRate;
|
||||||
|
|||||||
@ -436,6 +436,14 @@ main.requireStandard = true;
|
|||||||
|
|
||||||
main.rpcPort = 8332;
|
main.rpcPort = 8332;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default wallet port.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
main.walletPort = 8334;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default min relay rate.
|
* Default min relay rate.
|
||||||
* @const {Rate}
|
* @const {Rate}
|
||||||
@ -646,6 +654,8 @@ testnet.requireStandard = false;
|
|||||||
|
|
||||||
testnet.rpcPort = 18332;
|
testnet.rpcPort = 18332;
|
||||||
|
|
||||||
|
testnet.walletPort = 18334;
|
||||||
|
|
||||||
testnet.minRelay = 1000;
|
testnet.minRelay = 1000;
|
||||||
|
|
||||||
testnet.feeRate = 20000;
|
testnet.feeRate = 20000;
|
||||||
@ -807,6 +817,8 @@ regtest.requireStandard = false;
|
|||||||
|
|
||||||
regtest.rpcPort = 48332;
|
regtest.rpcPort = 48332;
|
||||||
|
|
||||||
|
regtest.walletPort = 48334;
|
||||||
|
|
||||||
regtest.minRelay = 1000;
|
regtest.minRelay = 1000;
|
||||||
|
|
||||||
regtest.feeRate = 20000;
|
regtest.feeRate = 20000;
|
||||||
@ -970,6 +982,8 @@ simnet.requireStandard = false;
|
|||||||
|
|
||||||
simnet.rpcPort = 18556;
|
simnet.rpcPort = 18556;
|
||||||
|
|
||||||
|
simnet.walletPort = 18558;
|
||||||
|
|
||||||
simnet.minRelay = 1000;
|
simnet.minRelay = 1000;
|
||||||
|
|
||||||
simnet.feeRate = 20000;
|
simnet.feeRate = 20000;
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const {NodeClient} = require('bclient');
|
const {NodeClient} = require('bclient');
|
||||||
const TX = require('../primitives/tx');
|
const TX = require('../primitives/tx');
|
||||||
const digest = require('bcrypto/lib/digest');
|
const hash256 = require('bcrypto/lib/hash256');
|
||||||
const util = require('../utils/util');
|
const util = require('../utils/util');
|
||||||
|
|
||||||
class WalletClient extends NodeClient {
|
class WalletClient extends NodeClient {
|
||||||
@ -77,7 +77,7 @@ function parseEntry(data) {
|
|||||||
assert(Buffer.isBuffer(data));
|
assert(Buffer.isBuffer(data));
|
||||||
assert(data.length >= 84);
|
assert(data.length >= 84);
|
||||||
|
|
||||||
const h = digest.hash256(data.slice(0, 80));
|
const h = hash256.digest(data.slice(0, 80));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hash: h.toString('hex'),
|
hash: h.toString('hex'),
|
||||||
@ -90,10 +90,8 @@ function parseBlock(entry, txs) {
|
|||||||
const block = parseEntry(entry);
|
const block = parseEntry(entry);
|
||||||
const out = [];
|
const out = [];
|
||||||
|
|
||||||
for (const raw of txs) {
|
for (const tx of txs)
|
||||||
const tx = TX.fromRaw(raw);
|
out.push(TX.fromRaw(tx));
|
||||||
out.push(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [block, out];
|
return [block, out];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,8 +41,9 @@ class HTTPServer extends Server {
|
|||||||
|
|
||||||
this.network = this.options.network;
|
this.network = this.options.network;
|
||||||
this.logger = this.options.logger.context('http');
|
this.logger = this.options.logger.context('http');
|
||||||
this.wdb = this.options.walletdb;
|
this.wdb = this.options.node.wdb;
|
||||||
this.rpc = this.wdb.rpc;
|
this.rpc = this.options.node.rpc;
|
||||||
|
this.plugin = this.options.node.plugin;
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
@ -90,7 +91,11 @@ class HTTPServer extends Server {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this.use(this.jsonRPC());
|
this.use(this.jsonRPC());
|
||||||
this.use(this.router());
|
|
||||||
|
if (!this.plugin)
|
||||||
|
this.use('/wallet', this.router());
|
||||||
|
else
|
||||||
|
this.use(this.router());
|
||||||
|
|
||||||
this.error((err, req, res) => {
|
this.error((err, req, res) => {
|
||||||
const code = err.statusCode || 500;
|
const code = err.statusCode || 500;
|
||||||
@ -118,6 +123,11 @@ class HTTPServer extends Server {
|
|||||||
const id = valid.str('id');
|
const id = valid.str('id');
|
||||||
const token = valid.buf('token');
|
const token = valid.buf('token');
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
res.json(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.options.walletAuth) {
|
if (!this.options.walletAuth) {
|
||||||
const wallet = await this.wdb.get(id);
|
const wallet = await this.wdb.get(id);
|
||||||
|
|
||||||
@ -893,7 +903,7 @@ class HTTPServer extends Server {
|
|||||||
|
|
||||||
socket.join('wallet auth');
|
socket.join('wallet auth');
|
||||||
|
|
||||||
this.logger.info('Successful auth from %s.', socket.remoteAddress);
|
this.logger.info('Successful auth from %s.', socket.host);
|
||||||
|
|
||||||
this.handleAuth(socket);
|
this.handleAuth(socket);
|
||||||
|
|
||||||
@ -967,7 +977,7 @@ class HTTPOptions {
|
|||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.network = Network.primary;
|
this.network = Network.primary;
|
||||||
this.logger = null;
|
this.logger = null;
|
||||||
this.walletdb = null;
|
this.node = null;
|
||||||
this.apiKey = base58.encode(random.randomBytes(20));
|
this.apiKey = base58.encode(random.randomBytes(20));
|
||||||
this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'ascii'));
|
this.apiHash = digest.hash256(Buffer.from(this.apiKey, 'ascii'));
|
||||||
this.serviceHash = this.apiHash;
|
this.serviceHash = this.apiHash;
|
||||||
@ -993,13 +1003,13 @@ class HTTPOptions {
|
|||||||
|
|
||||||
fromOptions(options) {
|
fromOptions(options) {
|
||||||
assert(options);
|
assert(options);
|
||||||
assert(options.walletdb && typeof options.walletdb === 'object',
|
assert(options.node && typeof options.node === 'object',
|
||||||
'HTTP Server requires a WalletDB.');
|
'HTTP Server requires a WalletDB.');
|
||||||
|
|
||||||
this.walletdb = options.walletdb;
|
this.node = options.node;
|
||||||
this.network = options.walletdb.network;
|
this.network = options.node.network;
|
||||||
this.logger = options.walletdb.logger;
|
this.logger = options.node.logger;
|
||||||
this.port = this.network.rpcPort + 2;
|
this.port = this.network.walletPort;
|
||||||
|
|
||||||
if (options.logger != null) {
|
if (options.logger != null) {
|
||||||
assert(typeof options.logger === 'object');
|
assert(typeof options.logger === 'object');
|
||||||
|
|||||||
@ -73,6 +73,7 @@ NodeClient.prototype._init = function _init() {
|
|||||||
|
|
||||||
NodeClient.prototype._open = function _open(options) {
|
NodeClient.prototype._open = function _open(options) {
|
||||||
this.listen = true;
|
this.listen = true;
|
||||||
|
this.emit('open');
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ NodeClient.prototype._open = function _open(options) {
|
|||||||
|
|
||||||
NodeClient.prototype._close = function _close() {
|
NodeClient.prototype._close = function _close() {
|
||||||
this.listen = false;
|
this.listen = false;
|
||||||
|
this.emit('close');
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const EventEmitter = require('events');
|
||||||
const WalletDB = require('./walletdb');
|
const WalletDB = require('./walletdb');
|
||||||
const NodeClient = require('./nodeclient');
|
const NodeClient = require('./nodeclient');
|
||||||
|
const HTTPServer = require('./http');
|
||||||
|
const RPC = require('./rpc');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exports wallet/plugin
|
* @exports wallet/plugin
|
||||||
@ -15,6 +18,74 @@ const NodeClient = require('./nodeclient');
|
|||||||
|
|
||||||
const plugin = exports;
|
const plugin = exports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin
|
||||||
|
* @constructor
|
||||||
|
* @param {Node} node
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Plugin(node) {
|
||||||
|
if (!(this instanceof Plugin))
|
||||||
|
return new Plugin(node);
|
||||||
|
|
||||||
|
const config = node.config;
|
||||||
|
|
||||||
|
this.network = node.network;
|
||||||
|
this.logger = node.logger;
|
||||||
|
|
||||||
|
this.client = new NodeClient(node);
|
||||||
|
this.plugin = true;
|
||||||
|
|
||||||
|
this.wdb = new WalletDB({
|
||||||
|
network: node.network,
|
||||||
|
logger: node.logger,
|
||||||
|
workers: node.workers,
|
||||||
|
client: this.client,
|
||||||
|
prefix: config.prefix,
|
||||||
|
db: config.str(['wallet-db', 'db']),
|
||||||
|
maxFiles: config.uint('wallet-max-files'),
|
||||||
|
cacheSize: config.mb('wallet-cache-size'),
|
||||||
|
witness: config.bool('wallet-witness'),
|
||||||
|
checkpoints: config.bool('wallet-checkpoints'),
|
||||||
|
startHeight: config.uint('wallet-start-height'),
|
||||||
|
wipeNoReally: config.bool('wallet-wipe-no-really'),
|
||||||
|
spv: node.spv
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rpc = new RPC(this);
|
||||||
|
|
||||||
|
this.http = new HTTPServer({
|
||||||
|
network: node.network,
|
||||||
|
logger: node.logger,
|
||||||
|
node: this,
|
||||||
|
apiKey: config.str(['wallet-api-key', 'api-key']),
|
||||||
|
walletAuth: config.bool('wallet-auth'),
|
||||||
|
noAuth: config.bool(['wallet-no-auth', 'no-auth'])
|
||||||
|
});
|
||||||
|
|
||||||
|
this.http.attach('/wallet', node.http);
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.setPrototypeOf(Plugin.prototype, EventEmitter.prototype);
|
||||||
|
|
||||||
|
Plugin.prototype.init = function init() {
|
||||||
|
this.client.on('error', err => this.emit('error', err));
|
||||||
|
this.wdb.on('error', err => this.emit('error', err));
|
||||||
|
this.http.on('error', err => this.emit('error', err));
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugin.prototype.open = async function open() {
|
||||||
|
await this.wdb.open();
|
||||||
|
this.rpc.wallet = this.wdb.primary;
|
||||||
|
};
|
||||||
|
|
||||||
|
Plugin.prototype.close = async function close() {
|
||||||
|
this.rpc.wallet = this.wdb.primary;
|
||||||
|
await this.wdb.open();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin name.
|
* Plugin name.
|
||||||
* @const {String}
|
* @const {String}
|
||||||
@ -29,35 +100,5 @@ plugin.id = 'walletdb';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugin.init = function init(node) {
|
plugin.init = function init(node) {
|
||||||
const config = node.config;
|
return new Plugin(node);
|
||||||
const client = new NodeClient(node);
|
|
||||||
|
|
||||||
const wdb = new WalletDB({
|
|
||||||
network: node.network,
|
|
||||||
logger: node.logger,
|
|
||||||
workers: node.workers,
|
|
||||||
client: client,
|
|
||||||
prefix: config.prefix,
|
|
||||||
db: config.str(['wallet-db', 'db']),
|
|
||||||
maxFiles: config.uint('wallet-max-files'),
|
|
||||||
cacheSize: config.mb('wallet-cache-size'),
|
|
||||||
witness: config.bool('wallet-witness'),
|
|
||||||
checkpoints: config.bool('wallet-checkpoints'),
|
|
||||||
startHeight: config.uint('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(['wallet-no-auth', 'no-auth']),
|
|
||||||
ssl: config.str('wallet-ssl'),
|
|
||||||
host: config.str('wallet-host'),
|
|
||||||
port: config.uint('wallet-port'),
|
|
||||||
spv: node.spv,
|
|
||||||
verify: node.spv,
|
|
||||||
listen: true,
|
|
||||||
plugin: true
|
|
||||||
});
|
|
||||||
|
|
||||||
wdb.http.attach('/wallet', node.http);
|
|
||||||
|
|
||||||
return wdb;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -81,15 +81,15 @@ class RPC extends RPCBase {
|
|||||||
* @param {WalletDB} wdb
|
* @param {WalletDB} wdb
|
||||||
*/
|
*/
|
||||||
|
|
||||||
constructor(wdb) {
|
constructor(node) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
assert(wdb, 'RPC requires a WalletDB.');
|
assert(node, 'RPC requires a WalletDB.');
|
||||||
|
|
||||||
this.wdb = wdb;
|
this.wdb = node.wdb;
|
||||||
this.network = wdb.network;
|
this.network = node.network;
|
||||||
this.logger = wdb.logger.context('rpc');
|
this.logger = node.logger.context('rpc');
|
||||||
this.client = wdb.client;
|
this.client = node.client;
|
||||||
this.locker = new Lock();
|
this.locker = new Lock();
|
||||||
|
|
||||||
this.wallet = null;
|
this.wallet = null;
|
||||||
|
|||||||
@ -6,104 +6,128 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const Node = require('../node/node');
|
||||||
const WalletDB = require('./walletdb');
|
const WalletDB = require('./walletdb');
|
||||||
const WorkerPool = require('../workers/workerpool');
|
const HTTPServer = require('./http');
|
||||||
const Config = require('../node/config');
|
|
||||||
const Logger = require('../node/logger');
|
|
||||||
const Client = require('./client');
|
const Client = require('./client');
|
||||||
|
const RPC = require('./rpc');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exports wallet/server
|
* Wallet Node
|
||||||
|
* @extends Node
|
||||||
|
* @constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const server = exports;
|
function WalletNode(options) {
|
||||||
|
if (!(this instanceof WalletNode))
|
||||||
|
return new WalletNode(options);
|
||||||
|
|
||||||
|
Node.call(this, 'bcoin', 'wallet.conf', 'wallet.log', options);
|
||||||
|
|
||||||
|
this.client = new Client({
|
||||||
|
network: this.network,
|
||||||
|
url: this.config.str('node-url'),
|
||||||
|
host: this.config.str('node-host'),
|
||||||
|
port: this.config.str('node-port', this.network.rpcPort),
|
||||||
|
ssl: this.config.str('node-ssl'),
|
||||||
|
apiKey: this.config.str('node-api-key')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wdb = new WalletDB({
|
||||||
|
network: this.network,
|
||||||
|
logger: this.logger,
|
||||||
|
workers: this.workers,
|
||||||
|
client: this.client,
|
||||||
|
prefix: this.config.prefix,
|
||||||
|
db: this.config.str('db'),
|
||||||
|
maxFiles: this.config.uint('max-files'),
|
||||||
|
cacheSize: this.config.mb('cache-size'),
|
||||||
|
witness: this.config.bool('witness'),
|
||||||
|
checkpoints: this.config.bool('checkpoints'),
|
||||||
|
startHeight: this.config.uint('start-height'),
|
||||||
|
wipeNoReally: this.config.bool('wipe-no-really'),
|
||||||
|
apiKey: this.config.str('api-key'),
|
||||||
|
walletAuth: this.config.bool('auth'),
|
||||||
|
noAuth: this.config.bool('no-auth'),
|
||||||
|
ssl: this.config.str('ssl'),
|
||||||
|
host: this.config.str('host'),
|
||||||
|
port: this.config.uint('port'),
|
||||||
|
spv: this.config.bool('spv')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rpc = new RPC(this);
|
||||||
|
|
||||||
|
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'),
|
||||||
|
walletAuth: this.config.bool('wallet-auth')
|
||||||
|
});
|
||||||
|
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.setPrototypeOf(WalletNode.prototype, Node.prototype);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a wallet server.
|
* Initialize the node.
|
||||||
* @param {Object} options
|
* @private
|
||||||
* @returns {WalletDB}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
server.create = function create(options) {
|
WalletNode.prototype._init = function _init() {
|
||||||
const config = new Config('bcoin');
|
this.client.on('error', err => this.error(err));
|
||||||
let logger = new Logger('debug');
|
this.wdb.on('error', err => this.error(err));
|
||||||
|
this.http.on('error', err => this.error(err));
|
||||||
|
|
||||||
config.inject(options);
|
this.loadPlugins();
|
||||||
config.load(options);
|
|
||||||
|
|
||||||
if (options.config)
|
|
||||||
config.open('wallet.conf');
|
|
||||||
|
|
||||||
if (config.has('logger'))
|
|
||||||
logger = config.obj('logger');
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
network: config.network,
|
|
||||||
uri: config.str('node-uri'),
|
|
||||||
apiKey: config.str('node-api-key')
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.set({
|
|
||||||
filename: config.bool('log-file')
|
|
||||||
? config.location('wallet.log')
|
|
||||||
: null,
|
|
||||||
level: config.str('log-level'),
|
|
||||||
console: config.bool('log-console'),
|
|
||||||
shrink: config.bool('log-shrink')
|
|
||||||
});
|
|
||||||
|
|
||||||
const workers = new WorkerPool({
|
|
||||||
enabled: config.str('workers-enabled'),
|
|
||||||
size: config.uint('workers-size'),
|
|
||||||
timeout: config.uint('workers-timeout')
|
|
||||||
});
|
|
||||||
|
|
||||||
const wdb = new WalletDB({
|
|
||||||
network: config.network,
|
|
||||||
logger: logger,
|
|
||||||
workers: workers,
|
|
||||||
client: client,
|
|
||||||
prefix: config.prefix,
|
|
||||||
db: config.str('db'),
|
|
||||||
maxFiles: config.uint('max-files'),
|
|
||||||
cacheSize: config.mb('cache-size'),
|
|
||||||
witness: config.bool('witness'),
|
|
||||||
checkpoints: config.bool('checkpoints'),
|
|
||||||
startHeight: config.uint('start-height'),
|
|
||||||
wipeNoReally: config.bool('wipe-no-really'),
|
|
||||||
apiKey: config.str('api-key'),
|
|
||||||
walletAuth: config.bool('auth'),
|
|
||||||
noAuth: config.bool('no-auth'),
|
|
||||||
ssl: config.str('ssl'),
|
|
||||||
host: config.str('host'),
|
|
||||||
port: config.uint('port'),
|
|
||||||
spv: config.bool('spv'),
|
|
||||||
verify: config.bool('spv'),
|
|
||||||
listen: true
|
|
||||||
});
|
|
||||||
|
|
||||||
wdb.on('error', () => {});
|
|
||||||
|
|
||||||
workers.on('spawn', (child) => {
|
|
||||||
logger.info('Spawning worker process: %d.', child.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
workers.on('exit', (code, child) => {
|
|
||||||
logger.warning('Worker %d exited: %s.', child.id, code);
|
|
||||||
});
|
|
||||||
|
|
||||||
workers.on('log', (text, child) => {
|
|
||||||
logger.debug('Worker %d says:', child.id);
|
|
||||||
logger.debug(text);
|
|
||||||
});
|
|
||||||
|
|
||||||
workers.on('error', (err, child) => {
|
|
||||||
if (child) {
|
|
||||||
logger.error('Worker %d error: %s', child.id, err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wdb.emit('error', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return wdb;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the node and all its child objects,
|
||||||
|
* wait for the database to load.
|
||||||
|
* @alias WalletNode#open
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletNode.prototype._open = async function _open(callback) {
|
||||||
|
// await this.client.open();
|
||||||
|
await this.wdb.open();
|
||||||
|
|
||||||
|
this.rpc.wallet = this.wdb.primary;
|
||||||
|
|
||||||
|
await this.openPlugins();
|
||||||
|
|
||||||
|
await this.http.open();
|
||||||
|
|
||||||
|
this.logger.info('Node is loaded.');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the node, wait for the database to close.
|
||||||
|
* @alias WalletNode#close
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletNode.prototype._close = async function _close() {
|
||||||
|
await this.http.close();
|
||||||
|
|
||||||
|
await this.closePlugins();
|
||||||
|
|
||||||
|
this.rpc.wallet = null;
|
||||||
|
await this.wdb.close();
|
||||||
|
// await this.client.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = WalletNode;
|
||||||
|
|||||||
@ -27,8 +27,6 @@ const Logger = require('../node/logger');
|
|||||||
const Outpoint = require('../primitives/outpoint');
|
const Outpoint = require('../primitives/outpoint');
|
||||||
const layouts = require('./layout');
|
const layouts = require('./layout');
|
||||||
const records = require('./records');
|
const records = require('./records');
|
||||||
const HTTPServer = require('./http');
|
|
||||||
const RPC = require('./rpc');
|
|
||||||
const StaticWriter = require('../utils/staticwriter');
|
const StaticWriter = require('../utils/staticwriter');
|
||||||
const layout = layouts.walletdb;
|
const layout = layouts.walletdb;
|
||||||
const ChainState = records.ChainState;
|
const ChainState = records.ChainState;
|
||||||
@ -55,27 +53,11 @@ function WalletDB(options) {
|
|||||||
this.network = this.options.network;
|
this.network = this.options.network;
|
||||||
this.logger = this.options.logger.context('wallet');
|
this.logger = this.options.logger.context('wallet');
|
||||||
this.workers = this.options.workers;
|
this.workers = this.options.workers;
|
||||||
|
|
||||||
this.client = this.options.client;
|
this.client = this.options.client;
|
||||||
this.feeRate = this.options.feeRate;
|
this.feeRate = this.options.feeRate;
|
||||||
|
|
||||||
this.db = LDB(this.options);
|
this.db = LDB(this.options);
|
||||||
this.rpc = new RPC(this);
|
|
||||||
this.primary = null;
|
this.primary = null;
|
||||||
|
|
||||||
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.state = new ChainState();
|
||||||
this.height = 0;
|
this.height = 0;
|
||||||
this.wallets = new Map();
|
this.wallets = new Map();
|
||||||
@ -86,6 +68,7 @@ function WalletDB(options) {
|
|||||||
this.readLock = new MappedLock();
|
this.readLock = new MappedLock();
|
||||||
this.writeLock = new Lock();
|
this.writeLock = new Lock();
|
||||||
this.txLock = new Lock();
|
this.txLock = new Lock();
|
||||||
|
this.scanLock = new Lock();
|
||||||
|
|
||||||
this.filter = new Bloom();
|
this.filter = new Bloom();
|
||||||
|
|
||||||
@ -130,9 +113,6 @@ WalletDB.prototype._init = function _init() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype._open = async function _open() {
|
WalletDB.prototype._open = async function _open() {
|
||||||
if (!this.options.plugin)
|
|
||||||
await this.logger.open();
|
|
||||||
|
|
||||||
await this.db.open();
|
await this.db.open();
|
||||||
await this.db.checkVersion('V', 7);
|
await this.db.checkVersion('V', 7);
|
||||||
|
|
||||||
@ -158,10 +138,6 @@ WalletDB.prototype._open = async function _open() {
|
|||||||
wallet.id, wallet.wid, await wallet.receiveAddress());
|
wallet.id, wallet.wid, await wallet.receiveAddress());
|
||||||
|
|
||||||
this.primary = wallet;
|
this.primary = wallet;
|
||||||
this.rpc.wallet = wallet;
|
|
||||||
|
|
||||||
if (this.options.listen)
|
|
||||||
await this.http.open();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,18 +149,12 @@ WalletDB.prototype._open = async function _open() {
|
|||||||
WalletDB.prototype._close = async function _close() {
|
WalletDB.prototype._close = async function _close() {
|
||||||
await this.disconnect();
|
await this.disconnect();
|
||||||
|
|
||||||
if (this.options.listen)
|
|
||||||
await this.http.close();
|
|
||||||
|
|
||||||
for (const wallet of this.wallets.values()) {
|
for (const wallet of this.wallets.values()) {
|
||||||
await wallet.destroy();
|
await wallet.destroy();
|
||||||
this.unregister(wallet);
|
this.unregister(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.db.close();
|
await this.db.close();
|
||||||
|
|
||||||
if (!this.options.plugin)
|
|
||||||
await this.logger.close();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,9 +165,23 @@ WalletDB.prototype._close = async function _close() {
|
|||||||
WalletDB.prototype.load = async function load() {
|
WalletDB.prototype.load = async function load() {
|
||||||
const unlock = await this.txLock.lock();
|
const unlock = await this.txLock.lock();
|
||||||
try {
|
try {
|
||||||
|
await this.watch();
|
||||||
await this.connect();
|
await this.connect();
|
||||||
await this.init();
|
await this.init();
|
||||||
await this.watch();
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize state with server on every connect.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.resync = async function resync() {
|
||||||
|
const unlock = await this.txLock.lock();
|
||||||
|
try {
|
||||||
|
await this.setFilter();
|
||||||
await this.sync();
|
await this.sync();
|
||||||
await this.resend();
|
await this.resend();
|
||||||
} finally {
|
} finally {
|
||||||
@ -223,6 +207,14 @@ WalletDB.prototype.bind = function bind() {
|
|||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.client.on('open', async () => {
|
||||||
|
try {
|
||||||
|
await this.resync();
|
||||||
|
} catch (e) {
|
||||||
|
this.emit('error', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.client.on('block connect', async (entry, txs) => {
|
this.client.on('block connect', async (entry, txs) => {
|
||||||
try {
|
try {
|
||||||
await this.addBlock(entry, txs);
|
await this.addBlock(entry, txs);
|
||||||
@ -239,7 +231,7 @@ WalletDB.prototype.bind = function bind() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client.hook('block rescan', async (entry, txs) => {
|
this.client.on('block rescan', async (entry, txs) => {
|
||||||
try {
|
try {
|
||||||
await this.rescanBlock(entry, txs);
|
await this.rescanBlock(entry, txs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -276,7 +268,6 @@ WalletDB.prototype.connect = async function connect() {
|
|||||||
this.bind();
|
this.bind();
|
||||||
|
|
||||||
await this.client.open();
|
await this.client.open();
|
||||||
await this.setFilter();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -381,8 +372,6 @@ WalletDB.prototype.watch = async function watch() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info('Added %d outpoints to WalletDB filter.', outpoints);
|
this.logger.info('Added %d outpoints to WalletDB filter.', outpoints);
|
||||||
|
|
||||||
await this.setFilter();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1787,6 +1776,8 @@ WalletDB.prototype._addBlock = async function _addBlock(entry, txs) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('Adding block: %d.', entry.height);
|
||||||
|
|
||||||
if (tip.height === this.state.height) {
|
if (tip.height === this.state.height) {
|
||||||
// We let blocks of the same height
|
// We let blocks of the same height
|
||||||
// through specifically for rescans:
|
// through specifically for rescans:
|
||||||
@ -1796,7 +1787,9 @@ WalletDB.prototype._addBlock = async function _addBlock(entry, txs) {
|
|||||||
// processed (in the case of a crash).
|
// processed (in the case of a crash).
|
||||||
this.logger.warning('Already saw WalletDB block (%d).', tip.height);
|
this.logger.warning('Already saw WalletDB block (%d).', tip.height);
|
||||||
} else if (tip.height !== this.state.height + 1) {
|
} else if (tip.height !== this.state.height + 1) {
|
||||||
throw new Error('WDB: Bad connection (height mismatch).');
|
// throw new Error('WDB: Bad connection (height mismatch).');
|
||||||
|
await this.scan(this.state.height);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync the state to the new tip.
|
// Sync the state to the new tip.
|
||||||
@ -1898,11 +1891,33 @@ WalletDB.prototype._removeBlock = async function _removeBlock(entry) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
WalletDB.prototype.rescanBlock = async function rescanBlock(entry, txs) {
|
WalletDB.prototype.rescanBlock = async function rescanBlock(entry, txs) {
|
||||||
|
const unlock = await this.scanLock.lock();
|
||||||
|
try {
|
||||||
|
return await this._rescanBlock(entry, txs);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rescan a block.
|
||||||
|
* @private
|
||||||
|
* @param {ChainEntry} entry
|
||||||
|
* @param {TX[]} txs
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype._rescanBlock = async function _rescanBlock(entry, txs) {
|
||||||
if (!this.rescanning) {
|
if (!this.rescanning) {
|
||||||
this.logger.warning('Unsolicited rescan block: %s.', entry.height);
|
this.logger.warning('Unsolicited rescan block: %s.', entry.height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry.height > this.state.height + 1) {
|
||||||
|
this.logger.warning('Unsolicited rescan block: %s.', entry.height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._addBlock(entry, txs);
|
await this._addBlock(entry, txs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -2034,14 +2049,6 @@ function WalletOptions(options) {
|
|||||||
this.checkpoints = false;
|
this.checkpoints = false;
|
||||||
this.startHeight = 0;
|
this.startHeight = 0;
|
||||||
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;
|
|
||||||
this.plugin = false;
|
|
||||||
this.listen = false;
|
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
@ -2137,46 +2144,6 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.plugin != null) {
|
|
||||||
assert(typeof options.plugin === 'boolean');
|
|
||||||
this.plugin = options.plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.listen != null) {
|
|
||||||
assert(typeof options.listen === 'boolean');
|
|
||||||
this.listen = options.listen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const wallet = new WalletClient({
|
|||||||
apiKey: 'foo'
|
apiKey: 'foo'
|
||||||
});
|
});
|
||||||
|
|
||||||
const wdb = node.require('walletdb');
|
const {wdb} = node.require('walletdb');
|
||||||
|
|
||||||
let addr = null;
|
let addr = null;
|
||||||
let hash = null;
|
let hash = null;
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const node = new FullNode({
|
|||||||
|
|
||||||
const chain = node.chain;
|
const chain = node.chain;
|
||||||
const miner = node.miner;
|
const miner = node.miner;
|
||||||
const wdb = node.require('walletdb');
|
const {wdb} = node.require('walletdb');
|
||||||
|
|
||||||
let wallet = null;
|
let wallet = null;
|
||||||
let tip1 = null;
|
let tip1 = null;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user