#!/usr/bin/env node 'use strict'; var argv = parseArg(process.argv); var bcoin = require('../').set(argv.network); var network = bcoin.network.get(); var utils = bcoin.utils; 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) { bcoin.error(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 createWallet(callback) { var options = { id: getID() }; if (argv.type) options.type = argv.type; if (argv.master) options.master = argv.master; if (argv.keys) options.keys = argv.keys.split(/[,:]/); if (argv.lookahead) options.lookahead = argv.lookahead >>> 0; if (argv.m) options.m = argv.m >>> 0; if (argv.n) options.n = argv.n >>> 0; if (argv.witness != null) options.witness = !!argv.witness; if (argv.passphrase) options.passphrase = argv.passphrase; client.createWallet(options, function(err, wallet) { if (err) return callback(err); utils.log(wallet); callback(); }); } function addKey(callback) { var id = getID(); var keys = argv.keys || argv.key; if (keys) keys = keys.split(','); else keys = argv.args; client.addKey(id, argv.account, keys, function(err, wallet) { if (err) return callback(err); utils.log('added'); callback(); }); } function createAccount(callback) { var id = getID(); var account = argv.args[0] || argv.account; client.createWalletAccount(id, account, function(err, account) { if (err) return callback(err); utils.log(account); callback(); }); } function getAccounts(callback) { var id = getID(); client.getWalletAccounts(id, function(err, accounts) { if (err) return callback(err); utils.log(accounts); callback(); }); } function removeKey(callback) { var id = getID(); var keys = argv.keys || argv.key; if (keys) keys = keys.split(','); else keys = argv.args; client.removeKey(id, argv.account, keys, function(err) { if (err) return callback(err); utils.log('removed'); callback(); }); } function getWallet(callback) { var id = getID(); var passphrase = argv.args[0]; client.getWallet(id, argv.account, passphrase, function(err, wallet) { if (err) return callback(err); utils.log(wallet); wallet.destroy(); callback(); }); } function getTX(callback) { var hash = argv.args[0]; if (bcoin.address.validate(hash)) { return client.getTXByAddress(hash, function(err, txs) { if (err) return callback(err); utils.log(txs); callback(); }); } hash = utils.revHex(hash); client.getTX(hash, function(err, tx) { if (err) return callback(err); if (!tx) { utils.log('TX not found.'); return callback(); } utils.log(tx); callback(); }); } function getBlock(callback) { var hash = argv.args[0]; if (hash.length !== 64) hash = +hash; else hash = utils.revHex(hash); client.getBlock(hash, function(err, block) { if (err) return callback(err); if (!block) { utils.log('Block not found.'); return callback(); } utils.log(block); utils.log('Coinbase Data:'); utils.log(block.txs[0].inputs[0].script.getCoinbaseFlags()); 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) { if (err) return callback(err); utils.log(coins); callback(); }); } hash = utils.revHex(hash); client.getCoin(hash, index, function(err, coin) { if (err) return callback(err); if (!coin) { utils.log('Coin not found.'); return callback(); } utils.log(coin); callback(); }); } function getWalletHistory(callback) { var id = getID(); client.getWalletHistory(id, argv.account, function(err, txs) { if (err) return callback(err); utils.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); }); client.on('updated', function(tx, map) { utils.log('TX updated:'); utils.log(tx); utils.log(map); }); client.on('confirmed', function(tx, map) { utils.log('TX updated:'); utils.log(tx); utils.log(map); }); client.on('address', function(receive, change) { utils.log('New addresses allocated:'); utils.log(receive); utils.log(change); }); client.on('balance', function(tx, map) { utils.log('Balance:'); utils.log(tx); utils.log(map); }); } function getBalance(callback) { var id = getID(); client.getWalletBalance(id, argv.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)); callback(); }); } function getMempool(callback) { client.getMempool(function(err, txs) { if (err) return callback(err); utils.log(txs); callback(); }); } function sendTX(callback) { var id = getID(); var options = { account: argv.account, passphrase: argv.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]); } else { output.address = argv.address || argv.args[0]; output.value = utils.satoshi(argv.value || argv.args[1]); } client.walletSend(id, {outputs:[output]}, function(err, tx) { if (err) return callback(err); utils.log(tx); utils.log(tx.toRaw('hex')); callback(); }); } function createTX(callback) { var id = getID(); var options = { account: argv.account, passphrase: argv.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]); } else { output.address = argv.address || argv.args[0]; output.value = utils.satoshi(argv.value || argv.args[1]); } client.walletCreate(id, options, [output], function(err, tx) { if (err) return callback(err); utils.log(tx); utils.log(tx.toRaw('hex')); 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) { if (err) return callback(err); utils.log(tx); utils.log(tx.toRaw('hex')); callback(); }); } function zap(callback) { var id = getID(); var age = (argv.age >>> 0) || 72 * 60 * 60; client.walletZap(id, argv.account, age, function(err) { if (err) return callback(err); utils.log('Zapped!'); callback(); }); } function broadcast(callback) { var tx = bcoin.tx.fromRaw(argv.args[0] || argv.tx, 'hex'); client.broadcast(tx, function(err, tx) { if (err) return callback(err); utils.log('Broadcasted:'); utils.log(tx); callback(); }); } function view(callback) { var tx = bcoin.tx.fromRaw(argv.args[0] || argv.tx, 'hex'); client.walletFill(tx, function(err, tx) { if (err) return callback(err); utils.log(tx); 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(); } } 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) { if (err) { console.error(err.stack + ''); //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(); }); });