#!/usr/bin/env node 'use strict'; var config = require('../lib/node/config'); var utils = require('../lib/utils/utils'); var Client = require('../lib/http/client'); var Wallet = require('../lib/http/wallet'); var assert = utils.assert; 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; } 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.createWallet = function createWallet(callback) { var self = this; var options = { id: this.argv[0] }; if (this.config.type) options.type = this.config.type; if (this.config.master) options.master = this.config.master; if (this.config.key) options.key = this.config.key; if (this.config.m) options.m = this.config.m >>> 0; if (this.config.n) options.n = this.config.n >>> 0; if (this.config.witness != null) options.witness = !!this.config.witness; if (this.config.passphrase) options.passphrase = this.config.passphrase; this.client.createWallet(options, function(err, wallet) { if (err) return callback(err); self.log(wallet); callback(); }); }; CLI.prototype.addKey = function addKey(callback) { var self = this; var key = this.argv[0]; this.wallet.addKey(this.config.account, key, function(err, wallet) { if (err) return callback(err); self.log('added'); callback(); }); }; CLI.prototype.removeKey = function removeKey(callback) { var self = this; var key = this.argv[0]; this.wallet.removeKey(this.config.account, key, function(err) { if (err) return callback(err); self.log('removed'); callback(); }); }; 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); self.log(account); callback(); }); }; CLI.prototype.createAccount = function createAccount(callback) { var self = this; var account = this.argv[0]; this.wallet.createAccount(account, function(err, account) { if (err) return callback(err); self.log(account); callback(); }); }; CLI.prototype.createAddress = function createAddress(callback) { var self = this; var account = this.argv[0]; this.wallet.createAddress(account, function(err, account) { if (err) return callback(err); self.log(account); callback(); }); }; 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.getWallet = function getWallet(callback) { var self = this; this.wallet.getInfo(function(err, wallet) { if (err) return callback(err); self.log(wallet); callback(); }); }; 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); self.log(txs); callback(); }); } this.client.getTX(hash, function(err, tx) { if (err) return callback(err); if (!tx) { self.log('TX not found.'); return callback(); } self.log(tx); callback(); }); }; CLI.prototype.getBlock = function getBlock(callback) { var self = this; var hash = this.argv[0]; if (hash.length !== 64) hash = +hash; this.client.getBlock(hash, function(err, block) { if (err) return callback(err); if (!block) { self.log('Block not found.'); return callback(); } self.log(block); callback(); }); }; 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); self.log(coins); callback(); }); } this.client.getCoin(hash, index, function(err, coin) { if (err) return callback(err); if (!coin) { self.log('Coin not found.'); return callback(); } self.log(coin); callback(); }); }; CLI.prototype.getWalletHistory = function getWalletHistory(callback) { var self = this; this.wallet.getHistory(this.config.account, function(err, txs) { if (err) return callback(err); self.log(txs); callback(); }); }; CLI.prototype.listenWallet = function listenWallet(callback) { var self = this; 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); }); }; CLI.prototype.getBalance = function getBalance(callback) { var self = this; this.wallet.getBalance(this.config.account, function(err, balance) { if (err) return callback(err); self.log(balance); callback(); }); }; CLI.prototype.getMempool = function getMempool(callback) { var self = this; this.client.getMempool(function(err, txs) { if (err) return callback(err); self.log(txs); callback(); }); }; CLI.prototype.sendTX = function sendTX(callback) { var self = this; var output = {}; var options; if (this.config.script) { output.script = this.config.script; output.value = utils.satoshi(this.config.value || this.argv[0]); } else { output.address = this.config.address || this.argv[0]; output.value = utils.satoshi(this.config.value || this.argv[1]); } options = { account: this.config.account, passphrase: this.config.passphrase, outputs: [output] }; this.wallet.send(options, function(err, tx) { if (err) return callback(err); self.log(tx); callback(); }); }; CLI.prototype.createTX = function createTX(callback) { var self = this; var output = {}; var options; if (this.config.script) { output.script = this.config.script; output.value = utils.satoshi(this.config.value || this.argv[0]); } else { output.address = this.config.address || this.argv[0]; output.value = utils.satoshi(this.config.value || this.argv[1]); } options = { account: this.config.account, passphrase: this.config.passphrase, outputs: [output] }; this.wallet.createTX(options, function(err, tx) { if (err) return callback(err); self.log(tx); callback(); }); }; 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); self.log(tx); callback(); }); }; 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); self.log('Zapped!'); callback(); }); }; 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); self.log('Broadcasted:'); self.log(tx); callback(); }); }; 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); 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.retoken = function retoken(callback) { var self = this; this.wallet.retoken(function(err, result) { if (err) return callback(err); self.log(result); 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 || 'primary', token: this.config.token }; this.wallet = new Wallet({ uri: this.config.url || this.config.uri, apiKey: this.config.apikey, network: this.config.network }); 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] === 'list') { self.argv.shift(); return self.getAccounts(callback); } if (self.argv[0] === 'create') { self.argv.shift(); return self.createAccount(callback); } if (self.argv[0] === 'get') self.argv.shift(); return self.getAccount(callback); case 'address': return self.createAddress(callback); case 'retoken': return self.retoken(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(' $ listen: Listen for events.'); self.log(' $ get: View wallet.'); self.log(' $ addkey [xpubkey]: Add key to wallet.'); self.log(' $ rmkey [xpubkey]: Remove key from wallet.'); self.log(' $ balance: Get wallet balance.'); self.log(' $ history: View wallet TX history.'); self.log(' $ account list: List account names.'); self.log(' $ account create [account-name]: Create account.'); self.log(' $ account get [account-name]: Get account details.'); self.log(' $ address: Derive new address.'); self.log(' $ retoken: Create new api key.'); self.log(' $ send [address] [value]: Send transaction.'); self.log(' $ mktx [address] [value]: Create transaction.'); self.log(' $ sign [tx-hex]: Sign transaction.'); self.log(' $ zap --age [age]: Zap pending wallet TXs.'); self.log(' $ tx [hash]: View transaction details.'); self.log(' $ view [tx-hex]: Parse and view transaction.'); self.log('Other Options:'); self.log(' --passphrase [passphrase]: For signing and account creation.'); self.log(' --account [account-name]: 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, network: this.config.network }); 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 create [id]: Create wallet.'); self.log(' $ broadcast [tx-hex]: Broadcast 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.'); 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.client.loading) this.wallet.client.destroy(); if (this.client && !this.client.loading) this.client.destroy(); callback(); }; function main(callback) { var cli = new CLI(); cli.open(function(err) { if (err) return callback(err); cli.destroy(callback); }); } main(function(err) { if (err) { console.error(err.stack + ''); return process.exit(1); } return process.exit(0); });