#!/usr/bin/env node 'use strict'; var config = require('../lib/node/config'); var utils = require('../lib/utils/utils'); var co = require('../lib/utils/co'); var Client = require('../lib/http/client'); var Wallet = require('../lib/http/wallet'); var main; 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.getInfo = co(function* getInfo() { var info = yield this.client.getInfo(); this.log(info); }); CLI.prototype.createWallet = co(function* createWallet() { var options = { id: this.argv[0] }; var wallet; if (this.config.type) options.type = this.config.type; if (this.config.master) options.master = this.config.master; if (this.config.mnemonic) options.master = this.config.mnemonic; 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; if (this.config.watch) { options.watchOnly = true; options.accountKey = this.config.watch; } wallet = yield this.client.createWallet(options); this.log(wallet); }); CLI.prototype.getMaster = co(function* getMaster() { var master = yield this.wallet.getMaster(); this.log(master); }); CLI.prototype.getKey = co(function* getKey() { var address = this.argv[0]; var key = yield this.wallet.getKey(address); this.log(key); }); CLI.prototype.getWIF = co(function* getWIF() { var address = this.argv[0]; var key = yield this.wallet.getWIF(address, this.config.passphrase); this.log(key.privateKey); }); CLI.prototype.addSharedKey = co(function* addSharedKey() { var key = this.argv[0]; yield this.wallet.addSharedKey(this.config.account, key); this.log('Added key.'); }); CLI.prototype.removeSharedKey = co(function* removeSharedKey() { var key = this.argv[0]; yield this.wallet.removeSharedKey(this.config.account, key); this.log('Removed key.'); }); CLI.prototype.getSharedKeys = co(function* getSharedKeys() { var acct = this.argv[0] || this.config.account; var account = yield this.wallet.getAccount(acct); this.log(account.keys); }); CLI.prototype.getAccount = co(function* getAccount() { var acct = this.argv[0] || this.config.account; var account = yield this.wallet.getAccount(acct); this.log(account); }); CLI.prototype.createAccount = co(function* createAccount() { var options = { name: this.argv[0] }; var account; if (this.config.type) options.type = this.config.type; 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.watch) options.accountKey = this.config.watch; account = yield this.wallet.createAccount(options); this.log(account); }); CLI.prototype.createAddress = co(function* createAddress() { var account = this.argv[0]; var addr = yield this.wallet.createAddress(account); this.log(addr); }); CLI.prototype.createChange = co(function* createChange() { var account = this.argv[0]; var addr = yield this.wallet.createChange(account); this.log(addr); }); CLI.prototype.createNested = co(function* createNested() { var account = this.argv[0]; var addr = yield this.wallet.createNested(account); this.log(addr); }); CLI.prototype.getAccounts = co(function* getAccounts() { var accounts = yield this.wallet.getAccounts(); this.log(accounts); }); CLI.prototype.getWallet = co(function* getWallet() { var info = yield this.wallet.getInfo(); this.log(info); }); CLI.prototype.getTX = co(function* getTX() { var hash = this.argv[0]; var txs, tx; if (utils.isBase58(hash)) { txs = yield this.client.getTXByAddress(hash); this.log(txs); return; } tx = yield this.client.getTX(hash); if (!tx) { this.log('TX not found.'); return; } this.log(tx); }); CLI.prototype.getBlock = co(function* getBlock() { var hash = this.argv[0]; var block; if (hash.length !== 64) hash = +hash; block = yield this.client.getBlock(hash); if (!block) { this.log('Block not found.'); return; } this.log(block); }); CLI.prototype.getCoin = co(function* getCoin() { var hash = this.argv[0]; var index = this.argv[1]; var coins, coin; if (utils.isBase58(hash)) { coins = yield this.client.getCoinsByAddress(hash); this.log(coins); return; } coin = yield this.client.getCoin(hash, index); if (!coin) { this.log('Coin not found.'); return; } this.log(coin); }); CLI.prototype.getWalletHistory = co(function* getWalletHistory() { var txs = yield this.wallet.getHistory(this.config.account); this.log(txs); }); CLI.prototype.getWalletPending = co(function* getWalletPending() { var txs = yield this.wallet.getPending(this.config.account); this.log(txs); }); CLI.prototype.getWalletCoins = co(function* getWalletCoins() { var coins = yield this.wallet.getCoins(this.config.account); this.log(coins); }); CLI.prototype.listenWallet = co(function* listenWallet() { var self = this; yield 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 yield this.wallet.onDisconnect(); }); CLI.prototype.getBalance = co(function* getBalance() { var balance = yield this.wallet.getBalance(this.config.account); this.log(balance); }); CLI.prototype.getMempool = co(function* getMempool() { var txs = yield this.client.getMempool(); this.log(txs); }); CLI.prototype.sendTX = co(function* sendTX() { var output = {}; var options, tx; 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] }; tx = yield this.wallet.send(options); this.log(tx); }); CLI.prototype.createTX = co(function* createTX() { var output = {}; var options, tx; 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] }; tx = yield this.wallet.createTX(options); this.log(tx); }); CLI.prototype.signTX = co(function* signTX() { var options = { passphrase: this.config.passphrase }; var raw = options.tx || this.argv[0]; var tx = yield this.wallet.sign(raw, options); this.log(tx); }); CLI.prototype.zapWallet = co(function* zapWallet() { var age = (this.config.age >>> 0) || 72 * 60 * 60; yield this.wallet.zap(this.config.account, age); this.log('Zapped!'); }); CLI.prototype.broadcast = co(function* broadcast() { var raw = this.argv[0] || this.config.tx; var tx = yield this.client.broadcast(raw); this.log('Broadcasted:'); this.log(tx); }); CLI.prototype.viewTX = co(function* viewTX() { var raw = this.argv[0] || this.config.tx; var tx = yield this.wallet.fill(raw); this.log(tx); }); CLI.prototype.getDetails = co(function* getDetails() { var hash = this.argv[0]; var details = yield this.wallet.getTX(hash); this.log(details); }); CLI.prototype.getWalletBlocks = co(function* getWalletBlocks() { var blocks = yield this.wallet.getBlocks(); this.log(blocks); }); CLI.prototype.getWalletBlock = co(function* getWalletBlock() { var height = this.argv[0] | 0; var block = yield this.wallet.getBlock(height); this.log(block); }); CLI.prototype.retoken = co(function* retoken() { var result = yield this.wallet.retoken(); this.log(result); }); CLI.prototype.rescan = co(function* rescan() { var height = this.argv[0]; if (!utils.isUInt32(height)) height = null; yield this.client.rescan(height); this.log('Rescanning...'); }); CLI.prototype.reset = co(function* reset() { var hash = this.argv[0]; if (hash.length !== 64) hash = +hash; yield this.client.reset(hash); this.log('Chain has been reset.'); }); CLI.prototype.resend = co(function* resend() { yield this.client.resend(); this.log('Resending...'); }); CLI.prototype.resendWallet = co(function* resendWallet() { yield this.wallet.resend(); this.log('Resending...'); }); CLI.prototype.backup = co(function* backup() { var path = this.argv[0]; yield this.client.backup(path); this.log('Backup complete.'); }); CLI.prototype.importKey = co(function* importKey() { var key = this.argv[0]; if (!key) throw new Error('No key for import.'); if (utils.isBase58(key)) { yield this.wallet.importPrivate(key); this.log('Imported private key.'); return; } if (utils.isHex(key)) { yield this.wallet.importPublic(key); this.log('Imported public key.'); return; } throw new Error('Bad key for import.'); }); CLI.prototype.importAddress = co(function* importKey() { var address = this.argv[0]; yield this.wallet.importAddress(address); this.log('Imported address.'); }); CLI.prototype.lock = co(function* lock() { yield this.wallet.lock(); this.log('Locked.'); }); CLI.prototype.unlock = co(function* unlock() { var passphrase = this.argv[0]; var timeout = +this.argv[1] || null; yield this.wallet.unlock(passphrase, timeout); this.log('Unlocked.'); }); CLI.prototype.rpc = co(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); } result = yield this.client.rpc.call(method, params); this.log(result); }); CLI.prototype.handleWallet = co(function* handleWallet() { this.wallet = new Wallet({ uri: this.config.url || this.config.uri, apiKey: this.config.apikey, network: this.config.network, id: this.config.id || 'primary', token: this.config.token }); switch (this.argv.shift()) { case 'listen': return yield this.listenWallet(); case 'get': return yield this.getWallet(); case 'master': return yield this.getMaster(); case 'shared': if (this.argv[0] === 'add') { this.argv.shift(); return yield this.addSharedKey(); } if (this.argv[0] === 'remove') { this.argv.shift(); return yield this.removeSharedKey(); } if (this.argv[0] === 'list') this.argv.shift(); return yield this.getSharedKeys(); case 'balance': return yield this.getBalance(); case 'history': return yield this.getWalletHistory(); case 'pending': return yield this.getWalletPending(); case 'coins': return yield this.getWalletCoins(); case 'account': if (this.argv[0] === 'list') { this.argv.shift(); return yield this.getAccounts(); } if (this.argv[0] === 'create') { this.argv.shift(); return yield this.createAccount(); } if (this.argv[0] === 'get') this.argv.shift(); return yield this.getAccount(); case 'address': return yield this.createAddress(); case 'change': return yield this.createChange(); case 'nested': return yield this.createNested(); case 'retoken': return yield this.retoken(); case 'sign': return yield this.signTX(); case 'mktx': return yield this.createTX(); case 'send': return yield this.sendTX(); case 'zap': return yield this.zapWallet(); case 'tx': return yield this.getDetails(); case 'blocks': return yield this.getWalletBlocks(); case 'block': return yield this.getWalletBlock(); case 'view': return yield this.viewTX(); case 'import': return yield this.importKey(); case 'watch': return yield this.importAddress(); case 'key': return yield this.getKey(); case 'dump': return yield this.getWIF(); case 'lock': return yield this.lock(); case 'unlock': return yield this.unlock(); case 'resend': return yield 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 = co(function* handleNode() { this.client = new Client({ uri: this.config.url || this.config.uri, apiKey: this.config.apikey, network: this.config.network }); switch (this.argv.shift()) { case 'info': return yield this.getInfo(); case 'mkwallet': return yield this.createWallet(); case 'broadcast': return yield this.broadcast(); case 'mempool': return yield this.getMempool(); case 'tx': return yield this.getTX(); case 'coin': return yield this.getCoin(); case 'block': return yield this.getBlock(); case 'rescan': return yield this.rescan(); case 'reset': return yield this.reset(); case 'resend': return yield this.resend(); case 'backup': return yield this.backup(); case 'rpc': return yield this.rpc(); default: this.log('Unrecognized command.'); this.log('Commands:'); this.log(' $ info: Get server info.'); 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 = co(function* open() { switch (this.argv[0]) { case 'w': case 'wallet': this.argv.shift(); if (this.argv[0] === 'create') { this.argv[0] = 'mkwallet'; return yield this.handleNode(); } return yield this.handleWallet(); default: return yield this.handleNode(); } }); CLI.prototype.destroy = function destroy() { if (this.wallet) this.wallet.client.destroy(); if (this.client) this.client.destroy(); return Promise.resolve(); }; main = co(function* main() { var cli = new CLI(); yield cli.open(); yield cli.destroy(); }); main().then(process.exit).catch(function(err) { console.error(err.stack + ''); return process.exit(1); });