http: api refactor.

This commit is contained in:
Christopher Jeffrey 2016-08-13 00:10:22 -07:00
parent cbed60e900
commit 2b630ad99c
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 718 additions and 786 deletions

View File

@ -2,7 +2,6 @@
rl=0
daemon=0
cli=0
cmd='node'
if ! type perl > /dev/null 2>& 1; then
@ -25,7 +24,14 @@ dir=$(dirname "$file")
if test x"$1" = x'cli'; then
shift
cli=1
exec "${dir}/cli" "$@"
exit 1
fi
if test x"$1" = x'rpc'; then
shift
exec "${dir}/cli" rpc "$@"
exit 1
fi
for arg in "$@"; do
@ -39,11 +45,6 @@ for arg in "$@"; do
esac
done
if test $cli -eq 1; then
exec "${dir}/cli" "$@"
exit 1
fi
if test $daemon -eq 1; then
if ! type setsid > /dev/null 2>& 1; then
echo 'BCoin requires setsid to start as a daemon.' >& 2

735
bin/cli
View File

@ -2,487 +2,564 @@
'use strict';
var argv = parseArg(process.argv);
var bcoin = require('../').set(argv.network);
var network = bcoin.network.get();
var utils = bcoin.utils;
var config = require('../lib/bcoin/config');
var utils = require('../lib/bcoin/utils');
var Client = require('../lib/bcoin/http/client');
var Wallet = require('../lib/bcoin/http/wallet');
var assert = utils.assert;
var Client = bcoin.http.client;
var client = new Client(
argv.url
|| process.env.BCOIN_URL
|| 'http://localhost:' + network.rpcPort
);
client.on('error', function(err) {
;
});
function getID() {
if (argv.id)
return argv.id;
if (process.env.BCOIN_WALLET_ID)
return process.env.BCOIN_WALLET_ID;
return argv.args.shift();
function CLI() {
this.config = config({
config: true,
arg: true,
env: true,
network: 'main'
}).data;
this.argv = this.config.args;
this.client = null;
this.wallet = null;
}
function createWallet(callback) {
var options = { id: getID() };
CLI.prototype.log = function log(json) {
if (typeof json === 'string')
return console.log.apply(console, arguments);
console.log(JSON.stringify(json, null, 2));
};
if (argv.type)
options.type = argv.type;
CLI.prototype.createWallet = function createWallet(callback) {
var options = { id: this.config.id };
if (argv.master)
options.master = argv.master;
if (this.config.type)
options.type = this.config.type;
if (argv.keys)
options.keys = argv.keys.split(/[,:]/);
if (this.config.master)
options.master = this.config.master;
if (argv.lookahead)
options.lookahead = argv.lookahead >>> 0;
if (this.config.keys)
options.keys = this.config.keys.split(/[,:]/);
if (argv.m)
options.m = argv.m >>> 0;
if (this.config.m)
options.m = this.config.m >>> 0;
if (argv.n)
options.n = argv.n >>> 0;
if (this.config.n)
options.n = this.config.n >>> 0;
if (argv.witness != null)
options.witness = !!argv.witness;
if (this.config.witness != null)
options.witness = !!this.config.witness;
if (argv.passphrase)
options.passphrase = argv.passphrase;
if (this.config.passphrase)
options.passphrase = this.config.passphrase;
client.createWallet(options, function(err, wallet) {
this.client.createWallet(options, function(err, wallet) {
if (err)
return callback(err);
utils.log(wallet);
self.log(wallet);
callback();
});
}
};
function addKey(callback) {
var id = getID();
var keys = argv.keys || argv.key;
CLI.prototype.addKey = function addKey(callback) {
var self = this;
var keys = this.config.keys || this.config.key;
if (keys)
keys = keys.split(',');
else
keys = argv.args;
client.addKey(id, argv.account, keys, function(err, wallet) {
keys = this.argv;
this.wallet.addKey(this.config.account, keys, function(err, wallet) {
if (err)
return callback(err);
utils.log('added');
self.log('added');
callback();
});
}
};
function createAccount(callback) {
var id = getID();
var account = argv.args[0] || argv.account;
client.createWalletAccount(id, account, function(err, account) {
CLI.prototype.createAccount = function createAccount(callback) {
var self = this;
var account = this.argv[0] || this.config.account;
this.wallet.createAccount(account, function(err, account) {
if (err)
return callback(err);
utils.log(account);
self.log(account);
callback();
});
}
};
function getAccounts(callback) {
var id = getID();
client.getWalletAccounts(id, function(err, accounts) {
CLI.prototype.getAccount = function getAccount(callback) {
var self = this;
var account = this.argv[0] || this.config.account;
this.wallet.getAccount(account, function(err, account) {
if (err)
return callback(err);
utils.log(accounts);
self.log(account);
callback();
});
}
};
function removeKey(callback) {
var id = getID();
var keys = argv.keys || argv.key;
CLI.prototype.getAccounts = function getAccounts(callback) {
var self = this;
this.wallet.getAccounts(function(err, accounts) {
if (err)
return callback(err);
self.log(accounts);
callback();
});
};
CLI.prototype.removeKey = function removeKey(callback) {
var self = this;
var keys = this.config.keys || this.config.key;
if (keys)
keys = keys.split(',');
else
keys = argv.args;
client.removeKey(id, argv.account, keys, function(err) {
keys = this.argv;
this.wallet.removeKey(this.config.account, keys, function(err) {
if (err)
return callback(err);
utils.log('removed');
self.log('removed');
callback();
});
}
};
function getWallet(callback) {
var id = getID();
var passphrase = argv.args[0];
client.getWallet(id, argv.account, passphrase, function(err, wallet) {
CLI.prototype.getWallet = function getWallet(callback) {
var self = this;
var passphrase = this.argv[0];
this.wallet.getInfo(this.config.account, passphrase, function(err, wallet) {
if (err)
return callback(err);
utils.log(wallet);
wallet.destroy();
self.log(wallet);
callback();
});
}
};
function getTX(callback) {
var hash = argv.args[0];
if (bcoin.address.validate(hash)) {
return client.getTXByAddress(hash, function(err, txs) {
CLI.prototype.getTX = function getTX(callback) {
var self = this;
var hash = this.argv[0];
if (utils.isBase58(hash)) {
return this.client.getTXByAddress(hash, function(err, txs) {
if (err)
return callback(err);
utils.log(txs);
self.log(txs);
callback();
});
}
hash = utils.revHex(hash);
client.getTX(hash, function(err, tx) {
this.client.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx) {
utils.log('TX not found.');
self.log('TX not found.');
return callback();
}
utils.log(tx);
self.log(tx);
callback();
});
}
};
function getBlock(callback) {
var hash = argv.args[0];
CLI.prototype.getBlock = function getBlock(callback) {
var self = this;
var hash = this.argv[0];
if (hash.length !== 64)
hash = +hash;
else
hash = utils.revHex(hash);
client.getBlock(hash, function(err, block) {
this.client.getBlock(hash, function(err, block) {
if (err)
return callback(err);
if (!block) {
utils.log('Block not found.');
self.log('Block not found.');
return callback();
}
utils.log(block);
utils.log('Coinbase Data:');
utils.log(block.txs[0].inputs[0].script.getCoinbaseFlags());
self.log(block);
callback();
});
}
};
function getCoin(callback) {
var hash = argv.args[0];
var index = argv.args[1];
if (bcoin.address.validate(hash)) {
return client.getCoinsByAddress(hash, function(err, coins) {
CLI.prototype.getCoin = function getCoin(callback) {
var self = this;
var hash = this.argv[0];
var index = this.argv[1];
if (utils.isBase58(hash)) {
return this.client.getCoinsByAddress(hash, function(err, coins) {
if (err)
return callback(err);
utils.log(coins);
self.log(coins);
callback();
});
}
hash = utils.revHex(hash);
client.getCoin(hash, index, function(err, coin) {
this.client.getCoin(hash, index, function(err, coin) {
if (err)
return callback(err);
if (!coin) {
utils.log('Coin not found.');
self.log('Coin not found.');
return callback();
}
utils.log(coin);
self.log(coin);
callback();
});
}
};
function getWalletHistory(callback) {
var id = getID();
client.getWalletHistory(id, argv.account, function(err, txs) {
CLI.prototype.getWalletHistory = function getWalletHistory(callback) {
var self = this;
this.wallet.getHistory(this.config.account, function(err, txs) {
if (err)
return callback(err);
utils.log(txs);
self.log(txs);
callback();
});
}
};
function listenWallet(callback) {
var id = getID();
client.join(id);
client.on('tx', function(tx, map) {
utils.log('TX:');
utils.log(tx);
utils.log(map);
CLI.prototype.listenWallet = function listenWallet(callback) {
var self = this;
this.wallet.on('tx', function(details) {
self.log('TX:');
self.log(details);
});
client.on('updated', function(tx, map) {
utils.log('TX updated:');
utils.log(tx);
utils.log(map);
this.wallet.on('confirmed', function(details) {
self.log('TX confirmed:');
self.log(details);
});
client.on('confirmed', function(tx, map) {
utils.log('TX updated:');
utils.log(tx);
utils.log(map);
this.wallet.on('unconfirmed', function(details) {
self.log('TX unconfirmed:');
self.log(details);
});
client.on('address', function(receive, change) {
utils.log('New addresses allocated:');
utils.log(receive);
utils.log(change);
this.wallet.on('conflict', function(details) {
self.log('TX conflict:');
self.log(details);
});
client.on('balance', function(tx, map) {
utils.log('Balance:');
utils.log(tx);
utils.log(map);
this.wallet.on('address', function(receive) {
self.log('New addresses allocated:');
self.log(receive);
});
}
this.wallet.on('balance', function(balance) {
self.log('Balance:');
self.log(balance);
});
};
function getBalance(callback) {
var id = getID();
client.getWalletBalance(id, argv.account, function(err, balance) {
CLI.prototype.getBalance = function getBalance(callback) {
var self = this;
this.wallet.getBalance(this.config.account, function(err, balance) {
if (err)
return callback(err);
utils.log('Confirmed: %s', utils.btc(balance.confirmed));
utils.log('Unconfirmed: %s', utils.btc(balance.unconfirmed));
utils.log('Total: %s', utils.btc(balance.total));
self.log(balance);
callback();
});
}
};
function getMempool(callback) {
client.getMempool(function(err, txs) {
CLI.prototype.getMempool = function getMempool(callback) {
var self = this;
this.client.getMempool(function(err, txs) {
if (err)
return callback(err);
utils.log(txs);
self.log(txs);
callback();
});
}
};
function sendTX(callback) {
var id = getID();
var options = { account: argv.account, passphrase: argv.passphrase };
CLI.prototype.sendTX = function sendTX(callback) {
var self = this;
var output = {};
if (argv.script) {
output.script = new bcoin.script(new Buffer(argv.script, 'hex'));
output.value = utils.satoshi(argv.value || argv.args[0]);
if (this.config.script) {
output.script = this.config.script;
output.value = utils.satoshi(this.config.value || this.argv[0]);
} else {
output.address = argv.address || argv.args[0];
output.value = utils.satoshi(argv.value || argv.args[1]);
output.address = this.config.address || this.argv[0];
output.value = utils.satoshi(this.config.value || this.argv[1]);
}
client.walletSend(id, {outputs:[output]}, function(err, tx) {
var options = {
account: this.config.account,
passphrase: this.config.passphrase,
outputs: [output]
};
this.wallet.send(options, function(err, tx) {
if (err)
return callback(err);
utils.log(tx);
utils.log(tx.toRaw('hex'));
self.log(tx);
callback();
});
}
};
function createTX(callback) {
var id = getID();
var options = { account: argv.account, passphrase: argv.passphrase };
CLI.prototype.createTX = function createTX(callback) {
var self = this;
var options = { account: this.config.account, passphrase: this.config.passphrase };
var output = {};
if (argv.script) {
output.script = new bcoin.script(new Buffer(argv.script, 'hex'));
output.value = utils.satoshi(argv.value || argv.args[0]);
if (this.config.script) {
output.script = this.config.script;
output.value = utils.satoshi(this.config.value || this.argv[0]);
} else {
output.address = argv.address || argv.args[0];
output.value = utils.satoshi(argv.value || argv.args[1]);
output.address = this.config.address || this.argv[0];
output.value = utils.satoshi(this.config.value || this.argv[1]);
}
client.walletCreate(id, options, [output], function(err, tx) {
options.outputs = [output];
this.wallet.createTX(options, function(err, tx) {
if (err)
return callback(err);
utils.log(tx);
utils.log(tx.toRaw('hex'));
self.log(tx);
callback();
});
}
};
function signTX(callback) {
var id = getID();
var options = { passphrase: argv.passphrase };
var tx = bcoin.tx.fromRaw(options.tx || argv.args[0], 'hex');
client.walletSign(id, tx, options, function(err, tx) {
CLI.prototype.signTX = function signTX(callback) {
var self = this;
var options = { passphrase: this.config.passphrase };
var tx = options.tx || this.argv[0];
this.wallet.sign(tx, options, function(err, tx) {
if (err)
return callback(err);
utils.log(tx);
utils.log(tx.toRaw('hex'));
self.log(tx);
callback();
});
}
};
function zap(callback) {
var id = getID();
var age = (argv.age >>> 0) || 72 * 60 * 60;
client.walletZap(id, argv.account, age, function(err) {
CLI.prototype.zap = function zap(callback) {
var self = this;
var age = (this.config.age >>> 0) || 72 * 60 * 60;
this.wallet.zap(this.config.account, age, function(err) {
if (err)
return callback(err);
utils.log('Zapped!');
self.log('Zapped!');
callback();
});
}
};
function broadcast(callback) {
var tx = bcoin.tx.fromRaw(argv.args[0] || argv.tx, 'hex');
client.broadcast(tx, function(err, tx) {
CLI.prototype.broadcast = function broadcast(callback) {
var self = this;
var tx = this.argv[0] || this.config.tx;
this.client.broadcast(tx, function(err, tx) {
if (err)
return callback(err);
utils.log('Broadcasted:');
utils.log(tx);
self.log('Broadcasted:');
self.log(tx);
callback();
});
}
};
function view(callback) {
var tx = bcoin.tx.fromRaw(argv.args[0] || argv.tx, 'hex');
client.walletFill(tx, function(err, tx) {
CLI.prototype.viewTX = function viewTX(callback) {
var self = this;
var tx = this.argv[0] || this.config.tx;
this.wallet.fill(tx, function(err, tx) {
if (err)
return callback(err);
utils.log(tx);
self.log(tx);
callback();
});
}
};
CLI.prototype.getDetails = function getDetails(callback) {
var self = this;
var hash = this.argv[0];
this.wallet.getTX(hash, function(err, tx) {
if (err)
return callback(err);
self.log(tx);
callback();
});
};
CLI.prototype.rpc = function rpc(callback) {
var self = this;
var method = this.argv.shift();
var params = [];
var i, arg, param;
for (i = 0; i < this.argv.length; i++) {
arg = this.argv[i];
try {
param = JSON.parse(arg);
} catch (e) {
param = arg;
}
params.push(param);
}
this.client.rpc.call(method, params, function(err, result) {
if (err)
return callback(err);
self.log(result);
callback();
});
};
CLI.prototype.handleWallet = function handleWallet(callback) {
var self = this;
var options = {
id: this.config.id,
token: this.config.token
};
this.wallet = new Wallet({
uri: this.config.url || this.config.uri,
apiKey: this.config.apiKey
});
this.wallet.open(options, function(err) {
if (err)
return callback(err);
switch (self.argv.shift()) {
case 'listen':
return self.listenWallet(callback);
case 'get':
return self.getWallet(callback);
case 'addkey':
return self.addKey(callback);
case 'rmkey':
return self.removeKey(callback);
case 'balance':
return self.getBalance(callback);
case 'history':
return self.getWalletHistory(callback);
case 'account':
if (self.argv[0] === 'create') {
self.argv.shift();
return self.createAccount(callback);
}
return self.getAccount(callback);
case 'accounts':
return self.getAccounts(callback);
case 'sign':
return self.signTX(callback);
case 'mktx':
return self.createTX(callback);
case 'send':
return self.sendTX(callback);
case 'zap':
return self.zap(callback);
case 'tx':
return self.getDetails(callback);
case 'view':
return self.viewTX(callback);
default:
self.log('Unrecognized command.');
self.log('Commands:');
self.log(' $ wallet [id] --keys [hdkeys]'
+ ' --type [pubkeyhash/multisig] -m [m-value]'
+ ' -n [n-value] --witness: View or create wallet by ID.');
self.log(' $ listen [id]: Listen for wallet events.');
self.log(' $ getwallet [id]: View wallet by ID.');
self.log(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
self.log(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
self.log(' $ balance [id]: Get wallet balance.');
self.log(' $ history [id]: View wallet TX history.');
self.log(' $ accounts [id]: List account names.');
self.log(' $ account [id] [acct]: Get account details.');
self.log(' $ send [id] [address] [value] --script [code]: Send transaction.');
self.log(' $ create [id] [address] [value] --script [code]: Create transaction.');
self.log(' $ sign [id] [tx-hex]: Sign transaction.');
self.log(' $ zap [id] --age [age]: Zap pending wallet TXs.');
self.log(' $ broadcast [tx-hex]: Broadcast transaction.');
self.log(' $ view [tx-hex]: View transaction.');
self.log(' $ mempool: Get mempool snapshot.');
self.log(' $ tx [hash/address]: View transactions.');
self.log(' $ coin [hash+index/address]: View coins.');
self.log(' $ block [hash/height]: View block.');
self.log('Other Options:');
self.log(' --passphrase [passphrase]: For signing and account creation.');
self.log(' --account [acctname]: Account name.');
return callback();
}
});
};
CLI.prototype.handleNode = function handleNode(callback) {
var self = this;
this.client = new Client({
uri: this.config.url || this.config.uri,
apiKey: this.config.apiKey
});
this.client.getInfo(function(err, info) {
if (err)
return callback(err);
switch (self.argv.shift()) {
case 'mkwallet':
return self.createWallet(callback);
case 'broadcast':
return self.broadcast(callback);
case 'mempool':
return self.getMempool(callback);
case 'tx':
return self.getTX(callback);
case 'coin':
return self.getCoin(callback);
case 'block':
return self.getBlock(callback);
case 'rpc':
return self.rpc(callback);
default:
self.log('Unrecognized command.');
self.log('Commands:');
self.log(' $ wallet [id] --keys [hdkeys]'
+ ' --type [pubkeyhash/multisig] -m [m-value]'
+ ' -n [n-value] --witness: View or create wallet by ID.');
self.log(' $ listen [id]: Listen for wallet events.');
self.log(' $ getwallet [id]: View wallet by ID.');
self.log(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
self.log(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
self.log(' $ balance [id]: Get wallet balance.');
self.log(' $ history [id]: View wallet TX history.');
self.log(' $ accounts [id]: List account names.');
self.log(' $ account [id] [acct]: Get account details.');
self.log(' $ send [id] [address] [value] --script [code]: Send transaction.');
self.log(' $ create [id] [address] [value] --script [code]: Create transaction.');
self.log(' $ sign [id] [tx-hex]: Sign transaction.');
self.log(' $ zap [id] --age [age]: Zap pending wallet TXs.');
self.log(' $ broadcast [tx-hex]: Broadcast transaction.');
self.log(' $ view [tx-hex]: View transaction.');
self.log(' $ mempool: Get mempool snapshot.');
self.log(' $ tx [hash/address]: View transactions.');
self.log(' $ coin [hash+index/address]: View coins.');
self.log(' $ block [hash/height]: View block.');
self.log('Other Options:');
self.log(' --passphrase [passphrase]: For signing and account creation.');
self.log(' --account [acctname]: Account name.');
return callback();
}
});
};
CLI.prototype.open = function open(callback) {
switch (this.argv[0]) {
case 'w':
case 'wallet':
this.argv.shift();
if (this.argv[0] === 'create') {
this.argv[0] = 'mkwallet';
return this.handleNode(callback);
}
return this.handleWallet(callback);
default:
return this.handleNode(callback);
}
};
CLI.prototype.destroy = function destroy(callback) {
if (this.wallet)
this.wallet.destroy();
if (this.client)
this.client.destroy();
callback();
};
function main(callback) {
switch (argv.args.shift()) {
case 'wallet':
return createWallet(callback);
case 'listen':
return listenWallet(callback);
case 'getwallet':
return getWallet(callback);
case 'addkey':
return addKey(callback);
case 'rmkey':
return removeKey(callback);
case 'balance':
return getBalance(callback);
case 'history':
return getWalletHistory(callback);
case 'account':
return createAccount(callback);
case 'accounts':
return getAccounts(callback);
case 'sign':
return signTX(callback);
case 'create':
return createTX(callback);
case 'send':
return sendTX(callback);
case 'zap':
return zap(callback);
case 'broadcast':
return broadcast(callback);
case 'view':
return view(callback);
case 'mempool':
return getMempool(callback);
case 'tx':
return getTX(callback);
case 'coin':
return getCoin(callback);
case 'block':
return getBlock(callback);
default:
utils.log('Unrecognized command.');
utils.log('Commands:');
utils.log(' $ wallet [id] --keys [hdkeys]'
+ ' --type [pubkeyhash/multisig] -m [m-value]'
+ ' -n [n-value] --witness: View or create wallet by ID.');
utils.log(' $ listen [id]: Listen for wallet events.');
utils.log(' $ getwallet [id]: View wallet by ID.');
utils.log(' $ addkey [id] --keys [hdkeys]: Add keys to wallet.');
utils.log(' $ rmkey [id] --keys [hdkeys]: Remove keys from wallet.');
utils.log(' $ balance [id]: Get wallet balance.');
utils.log(' $ history [id]: View wallet TX history.');
utils.log(' $ accounts [id]: List account names.');
utils.log(' $ account [id] [acct]: Get account details.');
utils.log(' $ send [id] [address] [value] --script [code]: Send transaction.');
utils.log(' $ create [id] [address] [value] --script [code]: Create transaction.');
utils.log(' $ sign [id] [tx-hex]: Sign transaction.');
utils.log(' $ zap [id] --age [age]: Zap pending wallet TXs.');
utils.log(' $ broadcast [tx-hex]: Broadcast transaction.');
utils.log(' $ view [tx-hex]: View transaction.');
utils.log(' $ mempool: Get mempool snapshot.');
utils.log(' $ tx [hash/address]: View transactions.');
utils.log(' $ coin [hash+index/address]: View coins.');
utils.log(' $ block [hash/height]: View block.');
utils.log('Other Options:');
utils.log(' --passphrase [passphrase]: For signing and account creation.');
utils.log(' --account [acctname]: Account name.');
return callback();
}
var cli = new CLI();
cli.open(function(err) {
if (err)
return callback(err);
cli.destroy(callback);
});
}
function parseArg(argv) {
var args = [];
var options = {};
var arg, negate;
argv = argv.slice();
function getarg() {
var arg = argv.shift();
if (arg.indexOf('--') === 0) {
// e.g. --opt
arg = arg.split('=');
if (arg.length > 1) {
// e.g. --opt=val
argv.unshift(arg.slice(1).join('='));
}
arg = arg[0];
} else if (arg[0] === '-') {
if (arg.length > 2) {
// e.g. -abc
argv = arg.substring(1).split('').map(function(ch) {
return '-' + ch;
}).concat(argv);
arg = argv.shift();
} else {
// e.g. -a
}
} else {
// e.g. foo
}
return arg;
}
while (argv.length) {
arg = getarg();
if (arg.indexOf('--') === 0) {
negate = arg.indexOf('--no-') === 0;
opt = arg.replace(/^--(no-)?/, '');
options[opt] = !argv[0] || argv[0][0] === '-'
? (negate ? false : true)
: argv.shift();
} else {
args.push(arg);
}
}
options.args = args.slice(2);
return options;
}
client.getInfo(function(err, info) {
main(function(err) {
if (err) {
console.error(err.stack + '');
//return process.exit(1);
return process.exit(1);
}
if (!argv.args[0])
utils.log(info);
main(function(err) {
if (err) {
console.error(err.stack + '');
return process.exit(1);
}
client.destroy();
});
return process.exit(0);
});

View File

@ -172,6 +172,8 @@ config.parseData = function parseData(data) {
options.walletAuth = bool(data.walletauth);
options.noAuth = bool(data.noauth);
options.data = data;
return options;
};
@ -245,13 +247,13 @@ config.parseText = function parseText(text) {
*/
config.parseArg = function parseArg(argv) {
var data = {};
var i, arg, key, value, alias;
var data = { args: [] };
var i, arg, key, value, alias, equals;
if (!argv)
argv = process.argv;
argv = argv.slice();
argv = argv.slice(2);
while (argv.length) {
arg = argv.shift();
@ -264,8 +266,10 @@ config.parseArg = function parseArg(argv) {
if (arg.length > 1) {
// e.g. --opt=val
value = arg.slice(1).join('=').trim();
equals = true;
} else {
value = 'true';
equals = false;
}
key = key.replace(/\-/g, '');
@ -292,19 +296,23 @@ config.parseArg = function parseArg(argv) {
if (alias)
key = alias;
data[key] = 'true';
equals = false;
}
continue;
}
// e.g. foo
if (key) {
value = arg.trim();
value = arg.trim();
if (value.length === 0)
continue;
if (value.length === 0)
continue;
if (key && !equals) {
data[key] = value;
key = null;
} else {
data.args.push(value);
}
}

View File

@ -7,8 +7,9 @@
'use strict';
var bcoin = require('../env');
var Network = require('../network');
var AsyncObject = require('../async');
var RPCClient = require('./rpcclient');
var utils = require('../utils');
var assert = utils.assert;
var request = require('./request');
@ -34,12 +35,13 @@ function HTTPClient(options) {
AsyncObject.call(this);
this.options = options;
this.network = bcoin.network.get(options.network);
this.network = Network.get(options.network);
this.uri = options.uri || 'http://localhost:' + this.network.rpcPort;
this.socket = null;
this.apiKey = options.apiKey;
this.auth = options.auth;
this.rpc = new RPCClient(options);
if (this.apiKey) {
if (typeof this.apiKey === 'string') {
@ -110,9 +112,6 @@ HTTPClient.prototype._open = function _open(callback) {
});
this.socket.on('wallet address', function(receive) {
receive = receive.map(function(address) {
return bcoin.keyring.fromJSON(address);
});
self.emit('address', receive);
});
@ -126,7 +125,8 @@ HTTPClient.prototype._open = function _open(callback) {
});
this.socket.on('connect', function() {
self.socket.emit('auth', self.apiKey.toString('hex'), function(err) {
var apiKey = self.apiKey ? self.apiKey.toString('hex') : null;
self.socket.emit('auth', apiKey, function(err) {
if (err)
return callback(new Error(err.error));
callback();
@ -350,23 +350,7 @@ HTTPClient.prototype.getWalletHistory = function getWalletHistory(id, account, c
options = { account: account };
return this._get('/wallet/' + id + '/tx/history', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/tx/history', options, callback);
};
/**
@ -385,23 +369,7 @@ HTTPClient.prototype.getWalletCoins = function getWalletCoins(id, account, callb
options = { account: account };
return this._get('/wallet/' + id + '/coin', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.coin.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/coin', options, callback);
};
/**
@ -420,23 +388,7 @@ HTTPClient.prototype.getWalletUnconfirmed = function getUnconfirmed(id, account,
options = { account: account };
return this._get('/wallet/' + id + '/tx/unconfirmed', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/tx/unconfirmed', options, callback);
};
/**
@ -455,20 +407,7 @@ HTTPClient.prototype.getWalletBalance = function getBalance(id, account, callbac
options = { account: account };
return this._get('/wallet/' + id + '/balance', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(new Error('Not found.'));
return callback(null, {
id: body.id,
confirmed: utils.satoshi(body.confirmed),
unconfirmed: utils.satoshi(body.unconfirmed),
total: utils.satoshi(body.total)
});
});
return this._get('/wallet/' + id + '/balance', options, callback);
};
/**
@ -488,23 +427,7 @@ HTTPClient.prototype.getWalletLast = function getWalletLast(id, account, limit,
options = { account: account, limit: limit };
return this._get('/wallet/' + id + '/tx/last', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/tx/last', options, callback);
};
/**
@ -533,23 +456,7 @@ HTTPClient.prototype.getWalletRange = function getWalletRange(id, account, optio
reverse: options.reverse
};
return this._get('/wallet/' + id + '/tx/range', options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/tx/range', options, callback);
};
/**
@ -571,23 +478,7 @@ HTTPClient.prototype.getWalletTX = function getTX(id, account, hash, callback) {
options = { account: account };
hash = utils.revHex(hash);
return this._get('/wallet/' + id + '/tx/' + hash, options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback();
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/wallet/' + id + '/tx/' + hash, options, callback);
};
/**
@ -611,24 +502,9 @@ HTTPClient.prototype.getWalletCoin = function getCoin(id, account, hash, index,
options = { account: account };
hash = utils.revHex(hash);
path = '/wallet/' + id + '/coin/' + hash + '/' + index;
return this._get(path, options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback();
try {
body = bcoin.coin.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get(path, options, callback);
};
/**
@ -641,23 +517,7 @@ HTTPClient.prototype.getWalletCoin = function getCoin(id, account, hash, index,
HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address, callback) {
var body = { addresses: address };
return this._post('/coin/address', body, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.coin.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/coin/address', body, callback);
};
/**
@ -669,23 +529,7 @@ HTTPClient.prototype.getCoinsByAddress = function getCoinsByAddress(address, cal
*/
HTTPClient.prototype.getCoin = function getCoin(hash, index, callback) {
hash = utils.revHex(hash);
return this._get('/coin/' + hash + '/' + index, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback();
try {
body = bcoin.coin.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/coin/' + hash + '/' + index, callback);
};
/**
@ -698,23 +542,7 @@ HTTPClient.prototype.getCoin = function getCoin(hash, index, callback) {
HTTPClient.prototype.getTXByAddress = function getTXByAddress(address, callback) {
var body = { addresses: address };
return this._post('/tx/address', body, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/tx/address', body, callback);
};
/**
@ -724,23 +552,7 @@ HTTPClient.prototype.getTXByAddress = function getTXByAddress(address, callback)
*/
HTTPClient.prototype.getTX = function getTX(hash, callback) {
hash = utils.revHex(hash);
return this._get('/tx/' + hash, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback();
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/tx/' + hash, callback);
};
/**
@ -750,24 +562,7 @@ HTTPClient.prototype.getTX = function getTX(hash, callback) {
*/
HTTPClient.prototype.getBlock = function getBlock(hash, callback) {
if (typeof hash !== 'number')
hash = utils.revHex(hash);
return this._get('/block/' + hash, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback();
try {
body = bcoin.block.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/block/' + hash, callback);
};
/**
@ -781,11 +576,7 @@ HTTPClient.prototype.broadcast = function broadcast(tx, callback) {
callback = utils.ensure(callback);
return this._post('/broadcast', body, function(err) {
if (err)
return callback(err);
return callback();
});
return this._post('/broadcast', body, callback);
};
/**
@ -816,18 +607,7 @@ HTTPClient.prototype.walletSend = function walletSend(id, options, callback) {
callback = utils.ensure(callback);
return this._post('/wallet/' + id + '/send', options, function(err, body) {
if (err)
return callback(err);
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/wallet/' + id + '/send', options, callback);
};
/**
@ -890,18 +670,7 @@ HTTPClient.prototype.walletCreate = function walletCreate(id, options, callback)
callback = utils.ensure(callback);
return this._post('/wallet/' + id + '/create', options, function(err, body) {
if (err)
return callback(err);
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/wallet/' + id + '/create', options, callback);
};
/**
@ -926,18 +695,7 @@ HTTPClient.prototype.walletSign = function walletCreate(id, tx, options, callbac
callback = utils.ensure(callback);
return this._post('/wallet/' + id + '/sign', body, function(err, body) {
if (err)
return callback(err);
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/wallet/' + id + '/sign', body, callback);
};
/**
@ -951,18 +709,7 @@ HTTPClient.prototype.walletFill = function walletFill(tx, callback) {
callback = utils.ensure(callback);
return this._post('/wallet/_/fill', body, function(err, body) {
if (err)
return callback(err);
try {
body = bcoin.tx.fromJSON(body);
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._post('/wallet/_/fill', body, callback);
};
/**
@ -990,11 +737,7 @@ HTTPClient.prototype.walletZap = function walletZap(id, account, age, callback)
callback = utils.ensure(callback);
return this._post('/wallet/' + id + '/zap', body, function(err) {
if (err)
return callback(err);
return callback();
});
return this._post('/wallet/' + id + '/zap', body, callback);
};
/**
@ -1020,11 +763,7 @@ HTTPClient.prototype.addKey = function addKey(id, account, key, callback) {
callback = utils.ensure(callback);
return this._put('/wallet/' + id + '/key', options, function(err) {
if (err)
return callback(err);
return callback();
});
return this._put('/wallet/' + id + '/key', options, callback);
};
/**
@ -1050,11 +789,7 @@ HTTPClient.prototype.removeKey = function removeKey(id, account, key, callback)
callback = utils.ensure(callback);
return this._del('/wallet/' + id + '/key', options, function(err) {
if (err)
return callback(err);
return callback();
});
return this._del('/wallet/' + id + '/key', options, callback);
};
/**
@ -1065,15 +800,7 @@ HTTPClient.prototype.removeKey = function removeKey(id, account, key, callback)
HTTPClient.prototype.getWalletAccounts = function getWalletAccounts(id, callback) {
var path = '/wallet/' + id + '/account';
return this._get(path, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
return callback(null, body);
});
return this._get(path, callback);
};
/**
@ -1099,15 +826,7 @@ HTTPClient.prototype.createWalletAccount = function createWalletAccount(id, opti
path = '/wallet/' + id + '/account';
return this._post(path, options, function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
return callback(null, body);
});
return this._post(path, options, callback);
};
@ -1117,23 +836,7 @@ HTTPClient.prototype.createWalletAccount = function createWalletAccount(id, opti
*/
HTTPClient.prototype.getMempool = function getMempool(callback) {
return this._get('/mempool', function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(null, []);
try {
body = body.map(function(data) {
return bcoin.tx.fromJSON(data);
});
} catch (e) {
return callback(e);
}
return callback(null, body);
});
return this._get('/mempool', callback);
};
/**
@ -1142,15 +845,7 @@ HTTPClient.prototype.getMempool = function getMempool(callback) {
*/
HTTPClient.prototype.getInfo = function getInfo(callback) {
return this._get('/', function(err, body) {
if (err)
return callback(err);
if (!body)
return callback(new Error('Info not available.'));
return callback(null, body);
});
return this._get('/', callback);
};
/*

View File

@ -2909,7 +2909,7 @@ RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) {
var self = this;
var i, det, receive, member, sent, received, json;
this.wallet.tx.toDetails(tx, function(err, details) {
this.wallet.toDetails(tx, function(err, details) {
if (err)
return callback(err);
@ -3240,7 +3240,7 @@ RPC.prototype._toListTX = function _toListTX(tx, callback) {
var i, receive, member, det, sent, received, index;
var sendMember, recMember, sendIndex, recIndex, json;
this.wallet.tx.toDetails(tx, function(err, details) {
this.wallet.toDetails(tx, function(err, details) {
if (err)
return callback(err);

View File

@ -0,0 +1,95 @@
/*!
* rpcclient.js - json rpc client for bcoin
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var Network = require('../network');
var utils = require('../utils');
var assert = utils.assert;
var request = require('./request');
/**
* BCoin RPC client.
* @exports RPCClient
* @constructor
* @param {String} uri
* @param {Object?} options
*/
function RPCClient(options) {
if (!(this instanceof RPCClient))
return new RPCClient(options);
if (!options)
options = {};
if (typeof options === 'string')
options = { uri: options };
this.options = options;
this.network = Network.get(options.network);
this.uri = options.uri || 'http://localhost:' + this.network.rpcPort;
this.apiKey = options.apiKey;
this.id = 0;
if (this.apiKey) {
if (typeof this.apiKey === 'string') {
assert(utils.isHex(this.apiKey), 'API key must be a hex string.');
this.apiKey = new Buffer(this.apiKey, 'hex');
}
assert(Buffer.isBuffer(this.apiKey));
assert(this.apiKey.length === 32, 'API key must be 32 bytes.');
}
}
/**
* Make an http request to endpoint.
* @private
* @param {String} method
* @param {String} endpoint - Path.
* @param {Object} json - Body or query depending on method.
* @param {Function} callback - Returns [Error, Object?].
*/
RPCClient.prototype.call = function call(method, params, callback) {
var self = this;
request({
method: 'POST',
uri: this.uri,
json: {
method: method,
params: params,
id: this.id++
},
auth: {
username: 'bitcoinrpc',
password: this.apiKey ? this.apiKey.toString('hex') : ''
},
expect: 'json'
}, function(err, res, body) {
if (err)
return callback(err);
if (!body)
return callback();
if (res.statusCode !== 200) {
if (body.error)
return callback(new Error(body.error.message));
return callback(new Error('Status code: ' + res.statusCode));
}
return callback(null, body.result);
});
};
/*
* Expose
*/
module.exports = RPCClient;

View File

@ -278,13 +278,22 @@ HTTPServer.prototype._init = function _init() {
if (req.path.length < 2 || req.path[0] !== 'wallet')
return next();
if (!self.options.walletAuth)
return next();
if (!self.options.walletAuth) {
return self.walletdb.get(req.options.id, function(err, wallet) {
if (err)
return next(err);
self.walletdb.auth(req.options.id, req.options.token, function(err) {
if (!wallet)
return send(404);
req.wallet = wallet;
return next();
});
}
self.walletdb.auth(req.options.id, req.options.token, function(err, wallet) {
if (err) {
if (err.message === 'Wallet not found.')
return next();
self.logger.info('Auth failure for %s: %s.',
req.options.id, err.message);
res.setHeader('WWW-Authenticate', 'Basic realm="wallet"');
@ -292,6 +301,10 @@ HTTPServer.prototype._init = function _init() {
return;
}
if (!wallet)
return send(404);
req.wallet = wallet;
self.logger.info('Successful auth for %s.', req.options.id);
next();
});
@ -525,27 +538,16 @@ HTTPServer.prototype._init = function _init() {
// Get wallet
this.get('/wallet/:id', function(req, res, next, send) {
self.walletdb.getInfo(req.options.id, function(err, wallet) {
if (err)
return next(err);
if (!wallet)
return send(404);
send(200, wallet.toJSON());
});
send(200, req.wallet.toJSON());
});
// Create/get wallet
// Create wallet
this.post('/wallet/:id?', function(req, res, next, send) {
var json;
self.walletdb.ensure(req.options, function(err, wallet) {
self.walletdb.create(req.options, function(err, wallet) {
if (err)
return next(err);
if (!wallet)
return send(404);
json = wallet.toJSON();
wallet.destroy();
@ -555,7 +557,7 @@ HTTPServer.prototype._init = function _init() {
// List accounts
this.get('/wallet/:id/account', function(req, res, next, send) {
self.walletdb.getAccounts(req.options.id, function(err, accounts) {
req.wallet.getAccounts(function(err, accounts) {
if (err)
return next(err);
@ -568,10 +570,7 @@ HTTPServer.prototype._init = function _init() {
// Create/get account
this.post('/wallet/:id/account/:account?', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
options.name = options.account || options.name;
self.walletdb.ensureAccount(id, options, function(err, account) {
req.wallet.createAccount(req.options, function(err, account) {
if (err)
return next(err);
@ -584,11 +583,10 @@ HTTPServer.prototype._init = function _init() {
// Change passphrase
this.post('/wallet/:id/passphrase', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
var old = options.old;
var new_ = options.passphrase;
self.walletdb.setPassphrase(id, old, new_, function(err) {
req.wallet.setPassphrase(old, new_, function(err) {
if (err)
return next(err);
@ -598,9 +596,8 @@ HTTPServer.prototype._init = function _init() {
// Generate new token
this.post('/wallet/:id/retoken', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
self.walletdb.retoken(id, options.passphrase, function(err, token) {
req.wallet.retoken(options.passphrase, function(err, token) {
if (err)
return next(err);
@ -610,10 +607,9 @@ HTTPServer.prototype._init = function _init() {
// Send TX
this.post('/wallet/:id/send', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
self.walletdb.send(id, options, function(err, tx) {
req.wallet.send(options, function(err, tx) {
if (err)
return next(err);
@ -623,14 +619,13 @@ HTTPServer.prototype._init = function _init() {
// Create TX
this.post('/wallet/:id/create', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
self.walletdb.createTX(id, options, function(err, tx) {
req.wallet.createTX(options, function(err, tx) {
if (err)
return next(err);
self.walletdb.sign(id, tx, options, function(err) {
req.wallet.sign(tx, options, function(err) {
if (err)
return next(err);
@ -641,11 +636,10 @@ HTTPServer.prototype._init = function _init() {
// Sign TX
this.post('/wallet/:id/sign', function(req, res, next, send) {
var id = req.options.id;
var options = req.options;
var tx = req.options.tx;
self.walletdb.sign(id, tx, options, function(err) {
req.wallet.sign(tx, options, function(err) {
if (err)
return next(err);
@ -655,10 +649,9 @@ HTTPServer.prototype._init = function _init() {
// Fill TX
this.post('/wallet/:id/fill', function(req, res, next, send) {
var id = req.options.id;
var tx = req.options.tx;
self.walletdb.fillHistory(id, tx, function(err) {
req.wallet.fillHistory(tx, function(err) {
if (err)
return next(err);
@ -668,11 +661,10 @@ HTTPServer.prototype._init = function _init() {
// Zap Wallet TXs
this.post('/wallet/:id/zap', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
var age = req.options.age;
self.walletdb.zap(id, account, age, function(err) {
req.wallet.zap(account, age, function(err) {
if (err)
return next(err);
@ -682,10 +674,9 @@ HTTPServer.prototype._init = function _init() {
// Add key
this.put('/wallet/:id/key', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
var key = req.options.key;
self.walletdb.addKey(id, account, key, function(err) {
req.wallet.addKey(account, key, function(err) {
if (err)
return next(err);
@ -695,10 +686,9 @@ HTTPServer.prototype._init = function _init() {
// Remove key
this.del('/wallet/:id/key', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
var key = req.options.key;
self.walletdb.removeKey(id, account, key, function(err) {
req.wallet.removeKey(account, key, function(err) {
if (err)
return next(err);
@ -708,9 +698,8 @@ HTTPServer.prototype._init = function _init() {
// Create address
this.post('/wallet/:id/address', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
self.walletdb.createAddress(id, account, false, function(err, address) {
req.wallet.createAddress(account, false, function(err, address) {
if (err)
return next(err);
@ -720,28 +709,22 @@ HTTPServer.prototype._init = function _init() {
// Wallet Balance
this.get('/wallet/:id/balance', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
self.walletdb.getBalance(id, account, function(err, balance) {
req.wallet.getBalance(account, function(err, balance) {
if (err)
return next(err);
if (!balance)
return send(404);
send(200, {
confirmed: utils.btc(balance.confirmed),
unconfirmed: utils.btc(balance.unconfirmed),
total: utils.btc(balance.total)
});
send(200, balance.toJSON());
});
});
// Wallet UTXOs
this.get('/wallet/:id/coin', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
self.walletdb.getCoins(id, account, function(err, coins) {
req.wallet.getCoins(account, function(err, coins) {
if (err)
return next(err);
@ -756,10 +739,9 @@ HTTPServer.prototype._init = function _init() {
// Wallet Coin
this.get('/wallet/:id/coin/:hash/:index', function(req, res, next, send) {
var id = req.options.id;
var hash = req.options.hash;
var index = req.options.index;
self.walletdb.getCoin(id, hash, index, function(err, coin) {
req.wallet.getCoin(hash, index, function(err, coin) {
if (err)
return next(err);
@ -772,18 +754,15 @@ HTTPServer.prototype._init = function _init() {
// Wallet TXs
this.get('/wallet/:id/tx/history', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
self.walletdb.getHistory(id, account, function(err, txs) {
req.wallet.getHistory(account, function(err, txs) {
if (err)
return next(err);
if (!txs.length)
return send(404);
utils.forEachSerial(txs, function(tx, next) {
self.walletdb.fillHistory(id, tx, next);
}, function(err) {
req.wallet.toDetails(txs, function(err, txs) {
if (err)
return next(err);
@ -796,18 +775,15 @@ HTTPServer.prototype._init = function _init() {
// Wallet Pending TXs
this.get('/wallet/:id/tx/unconfirmed', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
self.walletdb.getUnconfirmed(id, account, function(err, txs) {
req.wallet.getUnconfirmed(account, function(err, txs) {
if (err)
return next(err);
if (!txs.length)
return send(404);
utils.forEachSerial(txs, function(tx, next) {
self.walletdb.fillHistory(id, tx, next);
}, function(err) {
req.wallet.toDetails(txs, function(err, txs) {
if (err)
return next(err);
@ -820,19 +796,16 @@ HTTPServer.prototype._init = function _init() {
// Wallet TXs within time range
this.get('/wallet/:id/tx/range', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
var options = req.options;
self.walletdb.getRange(id, account, options, function(err, txs) {
req.walletdb.getRange(account, options, function(err, txs) {
if (err)
return next(err);
if (!txs.length)
return send(404);
utils.forEachSerial(txs, function(tx, next) {
self.walletdb.fillHistory(id, tx, next);
}, function(err) {
req.wallet.toDetails(txs, function(err, txs) {
if (err)
return next(err);
@ -845,19 +818,16 @@ HTTPServer.prototype._init = function _init() {
// Wallet TXs within time range
this.get('/wallet/:id/tx/last', function(req, res, next, send) {
var id = req.options.id;
var account = req.options.account;
var limit = req.options.limit;
self.walletdb.getRange(id, account, limit, function(err, txs) {
req.wallet.getRange(account, limit, function(err, txs) {
if (err)
return next(err);
if (!txs.length)
return send(404);
utils.forEachSerial(txs, function(tx, next) {
self.walletdb.fillHistory(id, tx, next);
}, function(err) {
req.wallet.toDetails(txs, function(err, txs) {
if (err)
return next(err);
@ -870,16 +840,15 @@ HTTPServer.prototype._init = function _init() {
// Wallet TX
this.get('/wallet/:id/tx/:hash', function(req, res, next, send) {
var id = req.options.id;
var hash = req.options.hash;
self.walletdb.getTX(id, hash, function(err, tx) {
req.wallet.getTX(hash, function(err, tx) {
if (err)
return next(err);
if (!tx)
return send(404);
self.walletdb.fillHistory(id, tx, function(err) {
req.wallet.toDetails(tx, function(err, tx) {
if (err)
return next(err);
send(200, tx.toJSON());
@ -954,11 +923,15 @@ HTTPServer.prototype._initIO = function _initIO() {
return callback();
}
self.walletdb.auth(id, token, function(err) {
self.walletdb.auth(id, token, function(err, wallet) {
if (err) {
self.logger.info('Wallet auth failure for %s: %s.', id, err.message);
return callback({ error: 'Bad token.' });
}
if (!wallet)
return callback({ error: 'Wallet does not exist.' });
self.logger.info('Successful wallet auth for %s.', id);
socket.join(id);
return callback();

View File

@ -7,7 +7,7 @@
'use strict';
var bcoin = require('../env');
var Network = require('../network');
var EventEmitter = require('events').EventEmitter;
var utils = require('../utils');
@ -33,7 +33,7 @@ function HTTPWallet(options) {
options = { uri: options };
this.options = options;
this.network = bcoin.network.get(options.network);
this.network = Network.get(options.network);
this.client = new http.client(options);
this.uri = options.uri;
@ -83,7 +83,7 @@ HTTPWallet.prototype._init = function _init() {
};
/**
* Open the client and ensure a wallet.
* Open the client and get a wallet.
* @alias HTTPWallet#open
* @param {Function} callback
*/
@ -94,6 +94,38 @@ HTTPWallet.prototype.open = function open(options, callback) {
if (Buffer.isBuffer(options.token))
options.token = options.token.toString('hex');
this.id = options.id;
if (options.token) {
this.client.auth = { username: 'x', password: options.token };
this.token = new Buffer(options.token, 'hex');
}
this.client.open(function(err) {
if (err)
return callback(err);
self.client.getWallet(self.id, function(err, wallet) {
if (err)
return callback(err);
self.client.joinWallet(self.id, wallet.token, function(err) {
if (err)
return callback(new Error(err.error));
callback(null, wallet);
});
});
});
};
/**
* Open the client and create a wallet.
* @alias HTTPWallet#open
* @param {Function} callback
*/
HTTPWallet.prototype.create = function create(options, callback) {
var self = this;
this.client.open(function(err) {
if (err)
return callback(err);
@ -101,14 +133,11 @@ HTTPWallet.prototype.open = function open(options, callback) {
self.client.createWallet(options, function(err, wallet) {
if (err)
return callback(err);
self.id = wallet.id;
self.client.auth = { username: 'x', password: wallet.token };
self.token = new Buffer(wallet.token, 'hex');
self.client.joinWallet(self.id, wallet.token, function(err) {
if (err)
return callback(new Error(err.error));
callback(null, wallet);
});
self.open({
id: wallet.id,
token: wallet.token
}, callback);
});
});
};

View File

@ -511,6 +511,30 @@ Wallet.prototype.createAccount = function createAccount(options, callback, force
});
};
/**
* Ensure an account. Requires passphrase if master key is encrypted.
* @param {Object} options - See {@link Account} options.
* @param {Function} callback - Returns [Error, {@link Account}].
*/
Wallet.prototype.ensureAccount = function ensureAccount(options, callback) {
var self = this;
var account = options.account;
if (typeof options.name === 'string')
account = options.name;
this.hasAccount(account, function(err, exists) {
if (err)
return callback(err);
if (exists)
return self.getAccount(account, callback);
self.createAccount(options, callback);
});
};
/**
* List account names and indexes from the db.
* @param {Function} callback - Returns [Error, Array].
@ -1349,6 +1373,36 @@ Wallet.prototype.fillCoins = function fillCoins(tx, callback) {
return this.tx.fillCoins(tx, callback);
};
/**
* Fill transaction with historical coins (accesses db).
* @param {TX} tx
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Wallet.prototype.fillHistory = function fillHistory(tx, callback) {
return this.tx.fillHistory(tx, callback);
};
/**
* Fill transaction with historical coins (accesses db).
* @param {TX} tx
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Wallet.prototype.toDetails = function toDetails(tx, callback) {
return this.tx.toDetails(tx, callback);
};
/**
* Fill transaction with historical coins (accesses db).
* @param {TX} tx
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Wallet.prototype.getDetails = function getDetails(tx, callback) {
return this.tx.getDetails(tx, callback);
};
/**
* Get a coin from the wallet (accesses db).
* @param {Hash} hash

View File

@ -438,12 +438,12 @@ WalletDB.prototype.save = function save(wallet) {
*/
WalletDB.prototype.auth = function auth(id, token, callback) {
this._get(id, function(err, wallet) {
this.get(id, function(err, wallet) {
if (err)
return callback(err);
if (!wallet)
return callback(new Error('Wallet not found.'));
return callback();
if (typeof token === 'string') {
if (!utils.isHex(token))
@ -455,7 +455,7 @@ WalletDB.prototype.auth = function auth(id, token, callback) {
if (!utils.ccmp(token, wallet.token))
return callback(new Error('Authentication error.'));
return callback();
return callback(null, wallet);
});
};

View File

@ -53,7 +53,7 @@ describe('HTTP', function() {
});
it('should create wallet', function(cb) {
wallet.open({ id: 'test' }, function(err, wallet) {
wallet.create({ id: 'test' }, function(err, wallet) {
assert.ifError(err);
assert.equal(wallet.id, 'test');
cb();
@ -113,9 +113,9 @@ describe('HTTP', function() {
assert.equal(receive.type, 'pubkeyhash');
assert.equal(receive.change, 0);
assert(balance);
assert.equal(balance.confirmed, 0);
assert.equal(balance.unconfirmed, 201840);
assert.equal(balance.total, 201840);
assert.equal(utils.satoshi(balance.confirmed), 0);
assert.equal(utils.satoshi(balance.unconfirmed), 201840);
assert.equal(utils.satoshi(balance.total), 201840);
assert(details);
assert.equal(details.hash, t1.rhash);
cb();
@ -126,9 +126,9 @@ describe('HTTP', function() {
it('should get balance', function(cb) {
wallet.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.confirmed, 0);
assert.equal(balance.unconfirmed, 201840);
assert.equal(balance.total, 201840);
assert.equal(utils.satoshi(balance.confirmed), 0);
assert.equal(utils.satoshi(balance.unconfirmed), 201840);
assert.equal(utils.satoshi(balance.total), 201840);
cb();
});
});
@ -147,8 +147,8 @@ describe('HTTP', function() {
assert(tx);
assert.equal(tx.inputs.length, 1);
assert.equal(tx.outputs.length, 2);
assert.equal(tx.getOutputValue(), 48190);
hash = tx.hash('hex');
assert.equal(utils.satoshi(tx.outputs[0].value) + utils.satoshi(tx.outputs[1].value), 48190);
hash = tx.hash;
cb();
});
});
@ -157,7 +157,7 @@ describe('HTTP', function() {
wallet.getTX(hash, function(err, tx) {
assert.ifError(err);
assert(tx);
assert.equal(tx.hash('hex'), hash);
assert.equal(tx.hash, hash);
cb();
});
});
@ -175,7 +175,7 @@ describe('HTTP', function() {
it('should get balance', function(cb) {
wallet.getBalance(function(err, balance) {
assert.ifError(err);
assert.equal(balance.total, 199570);
assert.equal(utils.satoshi(balance.total), 199570);
cb();
});
});