698 lines
18 KiB
JavaScript
Executable File
698 lines
18 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
var Config = require('../lib/node/config');
|
|
var util = require('../lib/utils/util');
|
|
var co = require('../lib/utils/co');
|
|
var Client = require('../lib/http/client');
|
|
var Wallet = require('../lib/http/wallet');
|
|
var Amount = require('../lib/btc/amount');
|
|
var main;
|
|
|
|
function CLI() {
|
|
this.config = new Config('bcoin');
|
|
|
|
this.config.load({
|
|
argv: true,
|
|
env: true
|
|
});
|
|
|
|
this.config.open('bcoin.conf');
|
|
|
|
this.argv = this.config.argv;
|
|
this.client = null;
|
|
this.wallet = null;
|
|
}
|
|
|
|
CLI.prototype.log = function log(json) {
|
|
if (typeof json === 'string')
|
|
return console.log.apply(console, arguments);
|
|
console.log(JSON.stringify(json, null, 2));
|
|
};
|
|
|
|
CLI.prototype.getInfo = async function getInfo() {
|
|
var info = await this.client.getInfo();
|
|
this.log(info);
|
|
};
|
|
|
|
CLI.prototype.getWallets = async function getWallets() {
|
|
var wallets = await this.client.getWallets();
|
|
this.log(wallets);
|
|
};
|
|
|
|
CLI.prototype.createWallet = async function createWallet() {
|
|
var options = { id: this.config.str(0) };
|
|
var wallet;
|
|
|
|
options.type = this.config.str('type');
|
|
options.master = this.config.str('master');
|
|
options.mnemonic = this.config.str('mnemonic');
|
|
options.m = this.config.num('m');
|
|
options.n = this.config.num('n');
|
|
options.witness = this.config.bool('witness');
|
|
options.passphrase = this.config.str('passphrase');
|
|
|
|
if (this.config.has('watch')) {
|
|
options.watchOnly = true;
|
|
options.accountKey = this.config.str('watch');
|
|
}
|
|
|
|
wallet = await this.client.createWallet(options);
|
|
|
|
this.log(wallet);
|
|
};
|
|
|
|
CLI.prototype.getMaster = async function getMaster() {
|
|
var master = await this.wallet.getMaster();
|
|
this.log(master);
|
|
};
|
|
|
|
CLI.prototype.getKey = async function getKey() {
|
|
var address = this.config.str(0);
|
|
var key = await this.wallet.getKey(address);
|
|
this.log(key);
|
|
};
|
|
|
|
CLI.prototype.getWIF = async function getWIF() {
|
|
var address = this.config.str(0);
|
|
var key = await this.wallet.getWIF(address, this.config.str('passphrase'));
|
|
this.log(key.privateKey);
|
|
};
|
|
|
|
CLI.prototype.addSharedKey = async function addSharedKey() {
|
|
var key = this.config.str(0);
|
|
await this.wallet.addSharedKey(this.config.str('account'), key);
|
|
this.log('Added key.');
|
|
};
|
|
|
|
CLI.prototype.removeSharedKey = async function removeSharedKey() {
|
|
var key = this.config.str(0);
|
|
await this.wallet.removeSharedKey(this.config.str('account'), key);
|
|
this.log('Removed key.');
|
|
};
|
|
|
|
CLI.prototype.getSharedKeys = async function getSharedKeys() {
|
|
var acct = this.config.str([0, 'account']);
|
|
var account = await this.wallet.getAccount(acct);
|
|
this.log(account.keys);
|
|
};
|
|
|
|
CLI.prototype.getAccount = async function getAccount() {
|
|
var acct = this.config.str([0, 'account']);
|
|
var account = await this.wallet.getAccount(acct);
|
|
this.log(account);
|
|
};
|
|
|
|
CLI.prototype.createAccount = async function createAccount() {
|
|
var name = this.config.str(0);
|
|
var options = {};
|
|
var account;
|
|
|
|
options.type = this.config.str('type');
|
|
options.m = this.config.num('m');
|
|
options.n = this.config.num('n');
|
|
options.witness = this.config.bool('witness');
|
|
options.accountKey = this.config.str('watch');
|
|
|
|
account = await this.wallet.createAccount(name, options);
|
|
|
|
this.log(account);
|
|
};
|
|
|
|
CLI.prototype.createAddress = async function createAddress() {
|
|
var account = this.config.str([0, 'account']);
|
|
var addr = await this.wallet.createAddress(account);
|
|
this.log(addr);
|
|
};
|
|
|
|
CLI.prototype.createChange = async function createChange() {
|
|
var account = this.config.str([0, 'account']);
|
|
var addr = await this.wallet.createChange(account);
|
|
this.log(addr);
|
|
};
|
|
|
|
CLI.prototype.createNested = async function createNested() {
|
|
var account = this.config.str([0, 'account']);
|
|
var addr = await this.wallet.createNested(account);
|
|
this.log(addr);
|
|
};
|
|
|
|
CLI.prototype.getAccounts = async function getAccounts() {
|
|
var accounts = await this.wallet.getAccounts();
|
|
this.log(accounts);
|
|
};
|
|
|
|
CLI.prototype.getWallet = async function getWallet() {
|
|
var info = await this.wallet.getInfo();
|
|
this.log(info);
|
|
};
|
|
|
|
CLI.prototype.getTX = async function getTX() {
|
|
var hash = this.config.str(0);
|
|
var txs, tx;
|
|
|
|
if (util.isBase58(hash)) {
|
|
txs = await this.client.getTXByAddress(hash);
|
|
this.log(txs);
|
|
return;
|
|
}
|
|
|
|
tx = await this.client.getTX(hash);
|
|
|
|
if (!tx) {
|
|
this.log('TX not found.');
|
|
return;
|
|
}
|
|
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.getBlock = async function getBlock() {
|
|
var hash = this.config.str(0);
|
|
var block;
|
|
|
|
if (hash.length !== 64)
|
|
hash = +hash;
|
|
|
|
block = await this.client.getBlock(hash);
|
|
|
|
if (!block) {
|
|
this.log('Block not found.');
|
|
return;
|
|
}
|
|
|
|
this.log(block);
|
|
};
|
|
|
|
CLI.prototype.getCoin = async function getCoin() {
|
|
var hash = this.config.str(0);
|
|
var index = this.config.num(1);
|
|
var coins, coin;
|
|
|
|
if (util.isBase58(hash)) {
|
|
coins = await this.client.getCoinsByAddress(hash);
|
|
this.log(coins);
|
|
return;
|
|
}
|
|
|
|
coin = await this.client.getCoin(hash, index);
|
|
|
|
if (!coin) {
|
|
this.log('Coin not found.');
|
|
return;
|
|
}
|
|
|
|
this.log(coin);
|
|
};
|
|
|
|
CLI.prototype.getWalletHistory = async function getWalletHistory() {
|
|
var txs = await this.wallet.getHistory(this.config.str('account'));
|
|
this.log(txs);
|
|
};
|
|
|
|
CLI.prototype.getWalletPending = async function getWalletPending() {
|
|
var txs = await this.wallet.getPending(this.config.str('account'));
|
|
this.log(txs);
|
|
};
|
|
|
|
CLI.prototype.getWalletCoins = async function getWalletCoins() {
|
|
var coins = await this.wallet.getCoins(this.config.str('account'));
|
|
this.log(coins);
|
|
};
|
|
|
|
CLI.prototype.listenWallet = async function listenWallet() {
|
|
var self = this;
|
|
|
|
await this.wallet.open();
|
|
|
|
this.wallet.on('tx', function(details) {
|
|
self.log('TX:');
|
|
self.log(details);
|
|
});
|
|
|
|
this.wallet.on('confirmed', function(details) {
|
|
self.log('TX confirmed:');
|
|
self.log(details);
|
|
});
|
|
|
|
this.wallet.on('unconfirmed', function(details) {
|
|
self.log('TX unconfirmed:');
|
|
self.log(details);
|
|
});
|
|
|
|
this.wallet.on('conflict', function(details) {
|
|
self.log('TX conflict:');
|
|
self.log(details);
|
|
});
|
|
|
|
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);
|
|
});
|
|
|
|
return await this.wallet.onDisconnect();
|
|
};
|
|
|
|
CLI.prototype.getBalance = async function getBalance() {
|
|
var balance = await this.wallet.getBalance(this.config.str('account'));
|
|
this.log(balance);
|
|
};
|
|
|
|
CLI.prototype.getMempool = async function getMempool() {
|
|
var txs = await this.client.getMempool();
|
|
this.log(txs);
|
|
};
|
|
|
|
CLI.prototype.sendTX = async function sendTX() {
|
|
var output = {};
|
|
var options, tx;
|
|
|
|
if (this.config.has('script')) {
|
|
output.script = this.config.str('script');
|
|
output.value = this.config.str(['value', 0]);
|
|
} else {
|
|
output.address = this.config.str(['address', 0]);
|
|
output.value = this.config.str(['value', 1]);
|
|
}
|
|
|
|
options = {
|
|
account: this.config.str('account'),
|
|
passphrase: this.config.str('passphrase'),
|
|
outputs: [output],
|
|
smart: this.config.bool('smart'),
|
|
rate: this.config.str('rate'),
|
|
subtractFee: this.config.bool('subtract-fee')
|
|
};
|
|
|
|
tx = await this.wallet.send(options);
|
|
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.createTX = async function createTX() {
|
|
var output = {};
|
|
var options, tx;
|
|
|
|
if (this.config.has('script')) {
|
|
output.script = this.config.str('script');
|
|
output.value = this.config.str(['value', 0]);
|
|
} else {
|
|
output.address = this.config.str(['address', 0]);
|
|
output.value = this.config.str(['value', 1]);
|
|
}
|
|
|
|
options = {
|
|
account: this.config.str('account'),
|
|
passphrase: this.config.str('passphrase'),
|
|
outputs: [output],
|
|
smart: this.config.bool('smart'),
|
|
rate: this.config.str('rate')
|
|
};
|
|
|
|
tx = await this.wallet.createTX(options);
|
|
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.signTX = async function signTX() {
|
|
var options = {};
|
|
var raw = this.config.str(['tx', 0]);
|
|
|
|
options.passphrase = this.config.str('passphrase');
|
|
|
|
tx = await this.wallet.sign(raw, options);
|
|
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.zapWallet = async function zapWallet() {
|
|
var age = this.config.num('age', 72 * 60 * 60);
|
|
await this.wallet.zap(this.config.str('account'), age);
|
|
this.log('Zapped!');
|
|
};
|
|
|
|
CLI.prototype.broadcast = async function broadcast() {
|
|
var raw = this.config.str([0, 'tx']);
|
|
var tx = await this.client.broadcast(raw);
|
|
this.log('Broadcasted:');
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.viewTX = async function viewTX() {
|
|
var raw = this.config.str([0, 'tx']);
|
|
var tx = await this.wallet.fill(raw);
|
|
this.log(tx);
|
|
};
|
|
|
|
CLI.prototype.getDetails = async function getDetails() {
|
|
var hash = this.config.str(0);
|
|
var details = await this.wallet.getTX(hash);
|
|
this.log(details);
|
|
};
|
|
|
|
CLI.prototype.getWalletBlocks = async function getWalletBlocks() {
|
|
var blocks = await this.wallet.getBlocks();
|
|
this.log(blocks);
|
|
};
|
|
|
|
CLI.prototype.getWalletBlock = async function getWalletBlock() {
|
|
var height = this.config.num(0);
|
|
var block = await this.wallet.getBlock(height);
|
|
this.log(block);
|
|
};
|
|
|
|
CLI.prototype.retoken = async function retoken() {
|
|
var result = await this.wallet.retoken();
|
|
this.log(result);
|
|
};
|
|
|
|
CLI.prototype.rescan = async function rescan() {
|
|
var height = this.config.num(0);
|
|
|
|
if (!util.isUInt32(height))
|
|
height = null;
|
|
|
|
await this.client.rescan(height);
|
|
|
|
this.log('Rescanning...');
|
|
};
|
|
|
|
CLI.prototype.reset = async function reset() {
|
|
var hash = this.config.str(0);
|
|
|
|
if (hash.length !== 64)
|
|
hash = +hash;
|
|
|
|
await this.client.reset(hash);
|
|
|
|
this.log('Chain has been reset.');
|
|
};
|
|
|
|
CLI.prototype.resend = async function resend() {
|
|
await this.client.resend();
|
|
this.log('Resending...');
|
|
};
|
|
|
|
CLI.prototype.resendWallet = async function resendWallet() {
|
|
await this.wallet.resend();
|
|
this.log('Resending...');
|
|
};
|
|
|
|
CLI.prototype.backup = async function backup() {
|
|
var path = this.config.str(0);
|
|
|
|
await this.client.backup(path);
|
|
|
|
this.log('Backup complete.');
|
|
};
|
|
|
|
CLI.prototype.importKey = async function importKey() {
|
|
var key = this.config.str(0);
|
|
var account = this.config.str('account');
|
|
|
|
if (!key)
|
|
throw new Error('No key for import.');
|
|
|
|
if (util.isBase58(key)) {
|
|
await this.wallet.importPrivate(account, key);
|
|
this.log('Imported private key.');
|
|
return;
|
|
}
|
|
|
|
if (util.isHex(key)) {
|
|
await this.wallet.importPublic(account, key);
|
|
this.log('Imported public key.');
|
|
return;
|
|
}
|
|
|
|
throw new Error('Bad key for import.');
|
|
};
|
|
|
|
CLI.prototype.importAddress = async function importKey() {
|
|
var address = this.config.str(0);
|
|
var account = this.config.str('account');
|
|
await this.wallet.importAddress(account, address);
|
|
this.log('Imported address.');
|
|
};
|
|
|
|
CLI.prototype.lock = async function lock() {
|
|
await this.wallet.lock();
|
|
this.log('Locked.');
|
|
};
|
|
|
|
CLI.prototype.unlock = async function unlock() {
|
|
var passphrase = this.config.str(0);
|
|
var timeout = this.config.num(1);
|
|
await this.wallet.unlock(passphrase, timeout);
|
|
this.log('Unlocked.');
|
|
};
|
|
|
|
CLI.prototype.rpc = async function rpc() {
|
|
var method = this.argv.shift();
|
|
var params = [];
|
|
var i, arg, param, result;
|
|
|
|
for (i = 0; i < this.argv.length; i++) {
|
|
arg = this.argv[i];
|
|
try {
|
|
param = JSON.parse(arg);
|
|
} catch (e) {
|
|
param = arg;
|
|
}
|
|
params.push(param);
|
|
}
|
|
|
|
try {
|
|
result = await this.client.rpc.execute(method, params);
|
|
} catch (e) {
|
|
if (e.type === 'RPCError') {
|
|
this.log(e.message);
|
|
return;
|
|
}
|
|
throw e;
|
|
}
|
|
|
|
this.log(result);
|
|
};
|
|
|
|
CLI.prototype.handleWallet = async function handleWallet() {
|
|
this.wallet = new Wallet({
|
|
uri: this.config.str(['url', 'uri']),
|
|
apiKey: this.config.str('api-key'),
|
|
network: this.config.str('network'),
|
|
id: this.config.str('id', 'primary'),
|
|
token: this.config.str('token')
|
|
});
|
|
|
|
switch (this.argv.shift()) {
|
|
case 'listen':
|
|
return await this.listenWallet();
|
|
case 'get':
|
|
return await this.getWallet();
|
|
case 'master':
|
|
return await this.getMaster();
|
|
case 'shared':
|
|
if (this.argv[0] === 'add') {
|
|
this.argv.shift();
|
|
return await this.addSharedKey();
|
|
}
|
|
if (this.argv[0] === 'remove') {
|
|
this.argv.shift();
|
|
return await this.removeSharedKey();
|
|
}
|
|
if (this.argv[0] === 'list')
|
|
this.argv.shift();
|
|
return await this.getSharedKeys();
|
|
case 'balance':
|
|
return await this.getBalance();
|
|
case 'history':
|
|
return await this.getWalletHistory();
|
|
case 'pending':
|
|
return await this.getWalletPending();
|
|
case 'coins':
|
|
return await this.getWalletCoins();
|
|
case 'account':
|
|
if (this.argv[0] === 'list') {
|
|
this.argv.shift();
|
|
return await this.getAccounts();
|
|
}
|
|
if (this.argv[0] === 'create') {
|
|
this.argv.shift();
|
|
return await this.createAccount();
|
|
}
|
|
if (this.argv[0] === 'get')
|
|
this.argv.shift();
|
|
return await this.getAccount();
|
|
case 'address':
|
|
return await this.createAddress();
|
|
case 'change':
|
|
return await this.createChange();
|
|
case 'nested':
|
|
return await this.createNested();
|
|
case 'retoken':
|
|
return await this.retoken();
|
|
case 'sign':
|
|
return await this.signTX();
|
|
case 'mktx':
|
|
return await this.createTX();
|
|
case 'send':
|
|
return await this.sendTX();
|
|
case 'zap':
|
|
return await this.zapWallet();
|
|
case 'tx':
|
|
return await this.getDetails();
|
|
case 'blocks':
|
|
return await this.getWalletBlocks();
|
|
case 'block':
|
|
return await this.getWalletBlock();
|
|
case 'view':
|
|
return await this.viewTX();
|
|
case 'import':
|
|
return await this.importKey();
|
|
case 'watch':
|
|
return await this.importAddress();
|
|
case 'key':
|
|
return await this.getKey();
|
|
case 'dump':
|
|
return await this.getWIF();
|
|
case 'lock':
|
|
return await this.lock();
|
|
case 'unlock':
|
|
return await this.unlock();
|
|
case 'resend':
|
|
return await this.resendWallet();
|
|
default:
|
|
this.log('Unrecognized command.');
|
|
this.log('Commands:');
|
|
this.log(' $ listen: Listen for events.');
|
|
this.log(' $ get: View wallet.');
|
|
this.log(' $ master: View wallet master key.');
|
|
this.log(' $ shared add [xpubkey]: Add key to wallet.');
|
|
this.log(' $ shared remove [xpubkey]: Remove key from wallet.');
|
|
this.log(' $ balance: Get wallet balance.');
|
|
this.log(' $ history: View TX history.');
|
|
this.log(' $ pending: View pending TXs.');
|
|
this.log(' $ coins: View wallet coins.');
|
|
this.log(' $ account list: List account names.');
|
|
this.log(' $ account create [account-name]: Create account.');
|
|
this.log(' $ account get [account-name]: Get account details.');
|
|
this.log(' $ address: Derive new address.');
|
|
this.log(' $ change: Derive new change address.');
|
|
this.log(' $ nested: Derive new nested address.');
|
|
this.log(' $ retoken: Create new api key.');
|
|
this.log(' $ send [address] [value]: Send transaction.');
|
|
this.log(' $ mktx [address] [value]: Create transaction.');
|
|
this.log(' $ sign [tx-hex]: Sign transaction.');
|
|
this.log(' $ zap [age?]: Zap pending wallet TXs.');
|
|
this.log(' $ tx [hash]: View transaction details.');
|
|
this.log(' $ blocks: List wallet blocks.');
|
|
this.log(' $ block [height]: View wallet block.');
|
|
this.log(' $ view [tx-hex]: Parse and view transaction.');
|
|
this.log(' $ import [wif|hex]: Import private or public key.');
|
|
this.log(' $ watch [address]: Import an address.');
|
|
this.log(' $ key [address]: Get wallet key by address.');
|
|
this.log(' $ dump [address]: Get wallet key WIF by address.');
|
|
this.log(' $ lock: Lock wallet.');
|
|
this.log(' $ unlock [passphrase] [timeout?]: Unlock wallet.');
|
|
this.log(' $ resend: Resend pending transactions.');
|
|
this.log('Other Options:');
|
|
this.log(' --passphrase [passphrase]: For signing and account creation.');
|
|
this.log(' --account [account-name]: Account name.');
|
|
return;
|
|
}
|
|
};
|
|
|
|
CLI.prototype.handleNode = async function handleNode() {
|
|
this.client = new Client({
|
|
uri: this.config.str(['url', 'uri']),
|
|
apiKey: this.config.str('api-key'),
|
|
network: this.config.str('network')
|
|
});
|
|
|
|
switch (this.argv.shift()) {
|
|
case 'info':
|
|
return await this.getInfo();
|
|
case 'wallets':
|
|
return await this.getWallets();
|
|
case 'mkwallet':
|
|
return await this.createWallet();
|
|
case 'broadcast':
|
|
return await this.broadcast();
|
|
case 'mempool':
|
|
return await this.getMempool();
|
|
case 'tx':
|
|
return await this.getTX();
|
|
case 'coin':
|
|
return await this.getCoin();
|
|
case 'block':
|
|
return await this.getBlock();
|
|
case 'rescan':
|
|
return await this.rescan();
|
|
case 'reset':
|
|
return await this.reset();
|
|
case 'resend':
|
|
return await this.resend();
|
|
case 'backup':
|
|
return await this.backup();
|
|
case 'rpc':
|
|
return await this.rpc();
|
|
default:
|
|
this.log('Unrecognized command.');
|
|
this.log('Commands:');
|
|
this.log(' $ info: Get server info.');
|
|
this.log(' $ wallets: List all wallets.');
|
|
this.log(' $ wallet create [id]: Create wallet.');
|
|
this.log(' $ broadcast [tx-hex]: Broadcast transaction.');
|
|
this.log(' $ mempool: Get mempool snapshot.');
|
|
this.log(' $ tx [hash/address]: View transactions.');
|
|
this.log(' $ coin [hash+index/address]: View coins.');
|
|
this.log(' $ block [hash/height]: View block.');
|
|
this.log(' $ rescan [height]: Rescan for transactions.');
|
|
this.log(' $ reset [height/hash]: Reset chain to desired block.');
|
|
this.log(' $ resend: Resend pending transactions.');
|
|
this.log(' $ backup [path]: Backup the wallet db.');
|
|
this.log(' $ rpc [command] [args]: Execute RPC command.');
|
|
return;
|
|
}
|
|
};
|
|
|
|
CLI.prototype.open = async function open() {
|
|
switch (this.argv[0]) {
|
|
case 'w':
|
|
case 'wallet':
|
|
this.argv.shift();
|
|
if (this.argv[0] === 'create') {
|
|
this.argv[0] = 'mkwallet';
|
|
return await this.handleNode();
|
|
}
|
|
return await this.handleWallet();
|
|
default:
|
|
return await this.handleNode();
|
|
}
|
|
};
|
|
|
|
CLI.prototype.destroy = function destroy() {
|
|
if (this.wallet)
|
|
this.wallet.client.destroy();
|
|
if (this.client)
|
|
this.client.destroy();
|
|
return Promise.resolve();
|
|
};
|
|
|
|
async function main() {
|
|
var cli = new CLI();
|
|
await cli.open();
|
|
await cli.destroy();
|
|
}
|
|
|
|
main().then(process.exit).catch(function(err) {
|
|
console.error(err.stack + '');
|
|
return process.exit(1);
|
|
});
|