From df23810b0c67ca04182cc713c2c9f5f319b93259 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 21 Sep 2016 10:46:07 -0700 Subject: [PATCH] refactor: fix cli. finish rpc. --- bin/cli | 835 +++++++++----------- lib/http/rpc.js | 1711 +++++++++++++++++++--------------------- lib/http/rpcclient.js | 50 +- lib/http/server.js | 6 +- lib/node/fullnode.js | 1 - lib/utils/locker.js | 52 +- lib/wallet/walletdb.js | 1 + 7 files changed, 1255 insertions(+), 1401 deletions(-) diff --git a/bin/cli b/bin/cli index 20631252..5292354b 100755 --- a/bin/cli +++ b/bin/cli @@ -4,6 +4,7 @@ var config = require('../lib/node/config'); var utils = require('../lib/utils/utils'); +var spawn = require('../lib/utils/spawn'); var Client = require('../lib/http/client'); var Wallet = require('../lib/http/wallet'); var assert = utils.assert; @@ -26,195 +27,161 @@ CLI.prototype.log = function log(json) { console.log(JSON.stringify(json, null, 2)); }; -CLI.prototype.createWallet = function createWallet(callback) { - var self = this; - var options = { id: this.argv[0] }; +CLI.prototype.createWallet = function createWallet() { + return spawn(function *() { + var options = { id: this.argv[0] }; + var wallet; - if (this.config.type) - options.type = this.config.type; + if (this.config.type) + options.type = this.config.type; - if (this.config.master) - options.master = this.config.master; + if (this.config.master) + options.master = this.config.master; - if (this.config.key) - options.key = this.config.key; + if (this.config.key) + options.key = this.config.key; - if (this.config.m) - options.m = this.config.m >>> 0; + if (this.config.m) + options.m = this.config.m >>> 0; - if (this.config.n) - options.n = this.config.n >>> 0; + if (this.config.n) + options.n = this.config.n >>> 0; - if (this.config.witness != null) - options.witness = !!this.config.witness; + if (this.config.witness != null) + options.witness = !!this.config.witness; - if (this.config.passphrase) - options.passphrase = this.config.passphrase; + 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(); - }); + wallet = yield this.client.createWallet(options); + this.log(wallet); + }, this); }; -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.addKey = function addKey() { + return spawn(function *() { + var key = this.argv[0]; + yield this.wallet.addKey(this.config.account, key); + this.log('added'); + }, this); }; -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.removeKey = function removeKey() { + return spawn(function *() { + var key = this.argv[0]; + yield this.wallet.removeKey(this.config.account, key); + this.log('removed'); + }, this); }; -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.getAccount = function getAccount() { + return spawn(function *() { + var account = this.argv[0] || this.config.account; + yield this.wallet.getAccount(account); + this.log(account); + }, this); }; -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.createAccount = function createAccount() { + return spawn(function *() { + var name = this.argv[0]; + var account = yield this.wallet.createAccount(name); + this.log(account); + }, this); }; -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.createAddress = function createAddress() { + return spawn(function *() { + var account = this.argv[0]; + var addr = yield this.wallet.createAddress(account); + this.log(addr); + }, this); }; -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.getAccounts = function getAccounts() { + return spawn(function *() { + var accounts = yield this.wallet.getAccounts(); + this.log(accounts); + }, this); }; -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.getWallet = function getWallet() { + return spawn(function *() { + var info = yield this.wallet.getInfo(); + this.log(wallet); + }, this); }; -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); +CLI.prototype.getTX = function getTX() { + return spawn(function *() { + 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) { - self.log('TX not found.'); - return callback(); + this.log('TX not found.'); + return; } - self.log(tx); - callback(); - }); + this.log(tx); + }, this); }; -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); +CLI.prototype.getBlock = function getBlock() { + return spawn(function *() { + var hash = this.argv[0]; + if (hash.length !== 64) + hash = +hash; + + block = yield this.client.getBlock(hash); if (!block) { - self.log('Block not found.'); - return callback(); + this.log('Block not found.'); + return } - self.log(block); - callback(); - }); + this.log(block); + }, this); }; -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); +CLI.prototype.getCoin = function getCoin() { + return spawn(function *() { + 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) { - self.log('Coin not found.'); - return callback(); + this.log('Coin not found.'); + return; } - self.log(coin); - callback(); - }); + this.log(coin); + }, this); }; -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.getWalletHistory = function getWalletHistory() { + return spawn(function *() { + var txs = yield this.wallet.getHistory(this.config.account); + this.log(txs); + }, this); }; -CLI.prototype.listenWallet = function listenWallet(callback) { +CLI.prototype.listenWallet = function listenWallet() { var self = this; this.wallet.on('tx', function(details) { self.log('TX:'); @@ -240,338 +207,304 @@ CLI.prototype.listenWallet = function listenWallet(callback) { self.log('Balance:'); self.log(balance); }); + return new Promise(function() {}); }; -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.getBalance = function getBalance() { + return spawn(function *() { + var balance = yield this.wallet.getBalance(this.config.account); + this.log(balance); + }, this); }; -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.getMempool = function getMempool() { + return spawn(function *() { + var txs = yield this.client.getMempool(); + this.log(txs); + }, this); }; -CLI.prototype.sendTX = function sendTX(callback) { - var self = this; - var output = {}; - var options; +CLI.prototype.sendTX = function sendTX() { + return spawn(function *() { + 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] - }; - - 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; + 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]); } - params.push(param); - } - this.client.rpc.call(method, params, function(err, result) { - if (err) - return callback(err); - self.log(result); - callback(); - }); + options = { + account: this.config.account, + passphrase: this.config.passphrase, + outputs: [output] + }; + + tx = yield this.wallet.send(options); + + this.log(tx); + }, this); }; -CLI.prototype.handleWallet = function handleWallet(callback) { - var self = this; +CLI.prototype.createTX = function createTX() { + return spawn(function *() { + var output = {}; + var options, tx; - 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(); + 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); + }, this); }; -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.signTX = function signTX() { + return spawn(function *() { + var options = { passphrase: this.config.passphrase }; + var raw = options.tx || this.argv[0]; + var tx = yield this.wallet.sign(raw, options); + this.log(tx); + }, this); }; -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); +CLI.prototype.zap = function zap() { + return spawn(function *() { + var age = (this.config.age >>> 0) || 72 * 60 * 60; + yield this.wallet.zap(this.config.account, age); + this.log('Zapped!'); + }, this); +}; + +CLI.prototype.broadcast = function broadcast() { + return spawn(function *() { + var self = this; + var raw = this.argv[0] || this.config.tx; + var tx = yield this.client.broadcast(raw); + this.log('Broadcasted:'); + this.log(tx); + }, this); +}; + +CLI.prototype.viewTX = function viewTX() { + return spawn(function *() { + var raw = this.argv[0] || this.config.tx; + var tx = yield this.wallet.fill(raw); + this.log(tx); + }, this); +}; + +CLI.prototype.getDetails = function getDetails() { + return spawn(function *() { + var hash = this.argv[0]; + var details = yield this.wallet.getTX(hash); + this.log(details); + }, this); +}; + +CLI.prototype.retoken = function retoken() { + return spawn(function *() { + var result = yield this.wallet.retoken(); + this.log(result); + }, this); +}; + +CLI.prototype.rpc = function rpc() { + return spawn(function *() { + 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; } - return this.handleWallet(callback); - default: - return this.handleNode(callback); - } + params.push(param); + } + + result = yield this.client.rpc.call(method, params); + + this.log(result); + }, this); }; -CLI.prototype.destroy = function destroy(callback) { +CLI.prototype.handleWallet = function handleWallet() { + return spawn(function *() { + 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 + }); + + yield this.wallet.open(options); + + switch (this.argv.shift()) { + case 'listen': + return yield this.listenWallet(); + case 'get': + return yield this.getWallet(); + case 'addkey': + return yield this.addKey(); + case 'rmkey': + return yield this.removeKey(); + case 'balance': + return yield this.getBalance(); + case 'history': + return yield this.getWalletHistory(); + 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 '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.zap(); + case 'tx': + return yield this.getDetails(); + case 'view': + return yield this.viewTX(); + default: + this.log('Unrecognized command.'); + this.log('Commands:'); + this.log(' $ listen: Listen for events.'); + this.log(' $ get: View wallet.'); + this.log(' $ addkey [xpubkey]: Add key to wallet.'); + this.log(' $ rmkey [xpubkey]: Remove key from wallet.'); + this.log(' $ balance: Get wallet balance.'); + this.log(' $ history: View wallet TX history.'); + 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(' $ 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 [age]: Zap pending wallet TXs.'); + this.log(' $ tx [hash]: View transaction details.'); + this.log(' $ view [tx-hex]: Parse and view transaction.'); + this.log('Other Options:'); + this.log(' --passphrase [passphrase]: For signing and account creation.'); + this.log(' --account [account-name]: Account name.'); + return; + } + }, this); +}; + +CLI.prototype.handleNode = function handleNode() { + return spawn(function *() { + var info; + + this.client = new Client({ + uri: this.config.url || this.config.uri, + apiKey: this.config.apikey, + network: this.config.network + }); + + info = yield this.client.getInfo(); + + switch (this.argv.shift()) { + 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 'rpc': + return yield this.rpc(); + default: + this.log('Unrecognized command.'); + this.log('Commands:'); + 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.'); + return; + } + }, this); +}; + +CLI.prototype.open = function open() { + return spawn(function *() { + 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(); + } + }, this); +}; + +CLI.prototype.destroy = function destroy() { if (this.wallet && !this.wallet.client.loading) this.wallet.client.destroy(); if (this.client && !this.client.loading) this.client.destroy(); - callback(); + return Promise.resolve(null); }; -function main(callback) { - var cli = new CLI(); - cli.open(function(err) { - if (err) - return callback(err); - cli.destroy(callback); - }); +function main() { + return spawn(function *() { + var cli = new CLI(); + yield cli.open(); + yield cli.destroy(); + }, this); } -main(function(err) { - if (err) { - console.error(err.stack + ''); - return process.exit(1); - } - return process.exit(0); +main().then(process.exit).catch(function(err) { + console.error(err.stack + ''); + return process.exit(1); }); diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 322766a6..663b78a8 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -356,7 +356,7 @@ RPC.prototype.getnetworkinfo = function getnetworkinfo(args) { if (args.help || args.length !== 0) return Promise.reject(new RPCError('getnetworkinfo')); - return { + return Promise.resolve({ version: constants.USER_VERSION, subversion: constants.USER_AGENT, protocolversion: constants.VERSION, @@ -367,7 +367,7 @@ RPC.prototype.getnetworkinfo = function getnetworkinfo(args) { relayfee: +utils.btc(this.network.getMinRelay()), localaddresses: [], warnings: '' - }; + }); }; RPC.prototype.addnode = function addnode(args) { @@ -1188,7 +1188,7 @@ RPC.prototype.gettxout = function gettxout(args) { mempool = toBool(args[2], true); if (!hash || index < 0) - throw new RPCError('Invalid parameter.')); + throw new RPCError('Invalid parameter.'); if (mempool) coin = yield this.node.getCoin(hash, index); @@ -1216,7 +1216,7 @@ RPC.prototype.gettxoutproof = function gettxoutproof(args) { var i, txids, block, hash, last, tx, coins; if (args.help || (args.length !== 1 && args.length !== 2)) - throw new RPCError('gettxoutproof ["txid",...] ( blockhash )')); + throw new RPCError('gettxoutproof ["txid",...] ( blockhash )'); if (this.chain.db.options.spv) throw new RPCError('Cannot get coins in SPV mode.'); @@ -2005,7 +2005,7 @@ RPC.prototype.generatetoaddress = function generatetoaddress(args) { if (args.help || args.length < 2 || args.length > 3) { unlock(); - throw new RPCError('generatetoaddress numblocks address ( maxtries )')); + throw new RPCError('generatetoaddress numblocks address ( maxtries )'); } numblocks = toNumber(args[0], 1); @@ -2158,33 +2158,35 @@ RPC.prototype.decodescript = function decodescript(args) { }; RPC.prototype.getrawtransaction = function getrawtransaction(args) { - var hash, verbose, json, tx; + return spawn(function *() { + var hash, verbose, json, tx; - if (args.help || args.length < 1 || args.length > 2) - return Promise.reject(new RPCError('getrawtransaction "txid" ( verbose )')); + if (args.help || args.length < 1 || args.length > 2) + throw new RPCError('getrawtransaction "txid" ( verbose )'); - hash = toHash(args[0]); + hash = toHash(args[0]); - if (!hash) - return Promise.reject(new RPCError('Invalid parameter')); + if (!hash) + throw new RPCError('Invalid parameter'); - verbose = false; + verbose = false; - if (args.length > 1) - verbose = Boolean(args[1]); + if (args.length > 1) + verbose = Boolean(args[1]); - var tx = yield this.node.getTX(hash); + tx = yield this.node.getTX(hash); - if (!tx) - return Promise.reject(new RPCError('Transaction not found.')); + if (!tx) + throw new RPCError('Transaction not found.'); - if (!verbose) - return Promise.resolve(tx.toRaw().toString('hex')); + if (!verbose) + throw tx.toRaw().toString('hex'); - json = this._txToJSON(tx); - json.hex = tx.toRaw().toString('hex'); + json = this._txToJSON(tx); + json.hex = tx.toRaw().toString('hex'); - return Promise.resolve(json); + return json; + }, this); }; RPC.prototype.sendrawtransaction = function sendrawtransaction(args) { @@ -2364,7 +2366,7 @@ RPC.prototype.fundrawtransaction = function fundrawtransaction(args) { tx = bcoin.mtx.fromRaw(toString(args[0]), 'hex'); if (tx.outputs.length === 0) - throw new RPCError('TX must have at least one output.')); + throw new RPCError('TX must have at least one output.'); if (args.length === 2 && args[1]) { options = args[1]; @@ -2734,391 +2736,363 @@ RPC.prototype.setmocktime = function setmocktime(args) { * Wallet */ -RPC.prototype.resendwallettransactions = function resendwallettransactions(args, callback) { - var hashes = []; - var i, tx; +RPC.prototype.resendwallettransactions = function resendwallettransactions(args) { + return spawn(function *() { + var hashes = []; + var i, tx, txs; - if (args.help || args.length !== 0) - return callback(new RPCError('resendwallettransactions')); + if (args.help || args.length !== 0) + throw new RPCError('resendwallettransactions'); - this.wallet.resend(function(err, txs) { - if (err) - return callback(err); + txs = yield this.wallet.resend(); for (i = 0; i < txs.length; i++) { tx = txs[i]; hashes.push(tx.rhash); } - callback(null, hashes); - }); + return hashes; + }, this); }; -RPC.prototype.addmultisigaddress = function addmultisigaddress(args, callback) { +RPC.prototype.addmultisigaddress = function addmultisigaddress(args) { if (args.help || args.length < 2 || args.length > 3) { - return callback(new RPCError('addmultisigaddress' + return Promise.reject(new RPCError('addmultisigaddress' + ' nrequired ["key",...] ( "account" )')); } // Impossible to implement in bcoin (no address book). - callback(new Error('Not implemented.')); + Promise.reject(new Error('Not implemented.')); }; RPC.prototype.addwitnessaddress = function addwitnessaddress(args, callback) { if (args.help || args.length < 1 || args.length > 1) - return callback(new RPCError('addwitnessaddress "address"')); + return Promise.reject(new RPCError('addwitnessaddress "address"')); // Unlikely to be implemented. - callback(new Error('Not implemented.')); + Promise.reject(new Error('Not implemented.')); }; -RPC.prototype.backupwallet = function backupwallet(args, callback) { - var dest; +RPC.prototype.backupwallet = function backupwallet(args) { + return spawn(function *() { + var dest; - if (args.help || args.length !== 1) - return callback(new RPCError('backupwallet "destination"')); + if (args.help || args.length !== 1) + throw new RPCError('backupwallet "destination"'); - dest = toString(args[0]); + dest = toString(args[0]); - this.walletdb.backup(dest, function(err) { - if (err) - return callback(err); - callback(null, null); - }); + yield this.walletdb.backup(dest); + return null; + }, this); }; -RPC.prototype.dumpprivkey = function dumpprivkey(args, callback) { - var self = this; - var hash; +RPC.prototype.dumpprivkey = function dumpprivkey(args) { + return spawn(function *() { + var hash, ring; - if (args.help || args.length !== 1) - return callback(new RPCError('dumpprivkey "bitcoinaddress"')); + if (args.help || args.length !== 1) + throw new RPCError('dumpprivkey "bitcoinaddress"'); - hash = bcoin.address.getHash(toString(args[0]), 'hex'); + hash = bcoin.address.getHash(toString(args[0]), 'hex'); - if (!hash) - return callback(new RPCError('Invalid address.')); + if (!hash) + throw new RPCError('Invalid address.'); - this.wallet.getKeyRing(hash, function(err, ring) { - if (err) - return callback(err); + ring = yield this.wallet.getKeyRing(hash); if (!ring) - return callback(new RPCError('Key not found.')); + throw new RPCError('Key not found.'); - if (!self.wallet.master.key) - return callback(new RPCError('Wallet is locked.')); + if (!this.wallet.master.key) + throw new RPCError('Wallet is locked.'); - callback(null, ring.toSecret()); - }); + return ring.toSecret(); + }, this); }; -RPC.prototype.dumpwallet = function dumpwallet(args, callback) { - var self = this; - var file, time, address, fmt, str, out; +RPC.prototype.dumpwallet = function dumpwallet(args) { + return spawn(function *() { + var i, file, time, address, fmt, str, out, hash, hashes, ring; - if (args.help || args.length !== 1) - return callback(new RPCError('dumpwallet "filename"')); + if (args.help || args.length !== 1) + throw new RPCError('dumpwallet "filename"'); - if (!args[0] || typeof args[0] !== 'string') - return callback(new RPCError('Invalid parameter.')); + if (!args[0] || typeof args[0] !== 'string') + throw new RPCError('Invalid parameter.'); - file = toString(args[0]); - time = utils.date(); - out = [ - utils.fmt('# Wallet Dump created by BCoin %s', constants.USER_VERSION), - utils.fmt('# * Created on %s', time), - utils.fmt('# * Best block at time of backup was %d (%s),', - this.chain.height, this.chain.tip.rhash), - utils.fmt('# mined on %s', utils.date(this.chain.tip.ts)), - utils.fmt('# * File: %s', file), - '' - ]; + file = toString(args[0]); + time = utils.date(); + out = [ + utils.fmt('# Wallet Dump created by BCoin %s', constants.USER_VERSION), + utils.fmt('# * Created on %s', time), + utils.fmt('# * Best block at time of backup was %d (%s),', + this.chain.height, this.chain.tip.rhash), + utils.fmt('# mined on %s', utils.date(this.chain.tip.ts)), + utils.fmt('# * File: %s', file), + '' + ]; - this.wallet.getAddressHashes(function(err, hashes) { - if (err) - return callback(err); + hashes = yield this.wallet.getAddressHashes(); - utils.forEachSerial(hashes, function(hash, next) { - self.wallet.getKeyRing(hash, function(err, ring) { - if (err) - return next(err); + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + ring = yield this.wallet.getKeyRing(hash); - if (!ring) - return next(); + if (!ring) + continue; - if (!self.wallet.master.key) - return next(new RPCError('Wallet is locked.')); + if (!this.wallet.master.key) + throw new RPCError('Wallet is locked.'); - address = ring.getAddress('base58'); - fmt = '%s %s label= addr=%s'; + address = ring.getAddress('base58'); + fmt = '%s %s label= addr=%s'; - if (ring.change) - fmt = '%s %s change=1 addr=%s'; + if (ring.change) + fmt = '%s %s change=1 addr=%s'; - str = utils.fmt(fmt, ring.toSecret(), time, address); + str = utils.fmt(fmt, ring.toSecret(), time, address); - out.push(str); + out.push(str); + } - next(); - }); - }, function(err) { - if (err) - return callback(err); + out.push(''); + out.push('# End of dump'); + out.push(''); - out.push(''); - out.push('# End of dump'); - out.push(''); + out = out.join('\n'); - out = out.join('\n'); + if (!fs) + return out; - if (!fs) - return callback(null, out); + yield writeFile(file, out); - fs.writeFile(file, out, function(err) { - if (err) - return callback(err); - callback(null, out); - }); - }); - }); + return out; + }, this); }; -RPC.prototype.encryptwallet = function encryptwallet(args, callback) { - var passphrase; +RPC.prototype.encryptwallet = function encryptwallet(args) { + return spawn(function *() { + var passphrase; - if (!this.wallet.master.encrypted && (args.help || args.help !== 1)) - return callback(new RPCError('encryptwallet "passphrase"')); + if (!this.wallet.master.encrypted && (args.help || args.help !== 1)) + throw new RPCError('encryptwallet "passphrase"'); - if (this.wallet.master.encrypted) - return callback(new RPCError('Already running with an encrypted wallet')); + if (this.wallet.master.encrypted) + throw new RPCError('Already running with an encrypted wallet'); - passphrase = toString(args[0]); + passphrase = toString(args[0]); - if (passphrase.length < 1) - return callback(new RPCError('encryptwallet "passphrase"')); + if (passphrase.length < 1) + throw new RPCError('encryptwallet "passphrase"'); - this.wallet.setPassphrase(passphrase, function(err) { - if (err) - return callback(err); - callback(null, 'wallet encrypted; we do not need to stop!'); - }); + yield this.wallet.setPassphrase(passphrase); + + return 'wallet encrypted; we do not need to stop!'; + }, this); }; -RPC.prototype.getaccountaddress = function getaccountaddress(args, callback) { - var account; +RPC.prototype.getaccountaddress = function getaccountaddress(args) { + return spawn(function *() { + var account; - if (args.help || args.length !== 1) - return callback(new RPCError('getaccountaddress "account"')); + if (args.help || args.length !== 1) + throw new RPCError('getaccountaddress "account"'); - account = toString(args[0]); - - if (!account) - account = 'default'; - - this.wallet.getAccount(account, function(err, account) { - if (err) - return callback(err); + account = toString(args[0]); if (!account) - return callback(null, ''); + account = 'default'; - callback(null, account.receiveAddress.getAddress('base58')); - }); + account = yield this.wallet.getAccount(account); + + if (!account) + return ''; + + return account.receiveAddress.getAddress('base58'); + }, this); }; -RPC.prototype.getaccount = function getaccount(args, callback) { - var hash; +RPC.prototype.getaccount = function getaccount(args) { + return spawn(function *() { + var hash, path; - if (args.help || args.length !== 1) - return callback(new RPCError('getaccount "bitcoinaddress"')); + if (args.help || args.length !== 1) + throw new RPCError('getaccount "bitcoinaddress"'); - hash = bcoin.address.getHash(args[0], 'hex'); + hash = bcoin.address.getHash(args[0], 'hex'); - if (!hash) - return callback(new RPCError('Invalid address.')); + if (!hash) + throw new RPCError('Invalid address.'); - this.wallet.getPath(hash, function(err, path) { - if (err) - return callback(err); + path = yield this.wallet.getPath(hash); if (!path) - return callback(null, ''); + return ''; - callback(null, path.name); - }); + return path.name; + }, this); }; -RPC.prototype.getaddressesbyaccount = function getaddressesbyaccount(args, callback) { - var self = this; - var i, path, account, addrs; +RPC.prototype.getaddressesbyaccount = function getaddressesbyaccount(args) { + return spawn(function *() { + var i, path, account, addrs, paths; - if (args.help || args.length !== 1) - return callback(new RPCError('getaddressesbyaccount "account"')); + if (args.help || args.length !== 1) + throw new RPCError('getaddressesbyaccount "account"'); - account = toString(args[0]); + account = toString(args[0]); - if (!account) - account = 'default'; + if (!account) + account = 'default'; - addrs = []; + addrs = []; - this.wallet.getPaths(account, function(err, paths) { - if (err) - return callback(err); + paths = yield this.wallet.getPaths(account); for (i = 0; i < paths.length; i++) { path = paths[i]; addrs.push(path.toAddress().toBase58(self.network)); } - callback(null, addrs); - }); + return addrs; + }, this); }; -RPC.prototype.getbalance = function getbalance(args, callback) { - var minconf = 0; - var account, value; +RPC.prototype.getbalance = function getbalance(args) { + return spawn(function *() { + var minconf = 0; + var account, value, balance; - if (args.help || args.length > 3) { - return callback(new RPCError('getbalance' - + ' ( "account" minconf includeWatchonly )')); - } + if (args.help || args.length > 3) + throw new RPCError('getbalance ( "account" minconf includeWatchonly )'); - if (args.length >= 1) { - account = toString(args[0]); - if (!account) - account = 'default'; - if (account === '*') - account = null; - } + if (args.length >= 1) { + account = toString(args[0]); + if (!account) + account = 'default'; + if (account === '*') + account = null; + } - if (args.length >= 2) - minconf = toNumber(args[1], 0); + if (args.length >= 2) + minconf = toNumber(args[1], 0); - this.wallet.getBalance(account, function(err, balance) { - if (err) - return callback(err); + balance = yield this.wallet.getBalance(account); if (minconf) value = balance.confirmed; else value = balance.total; - callback(null, +utils.btc(value)); - }); + return +utils.btc(value); + }, this); }; -RPC.prototype.getnewaddress = function getnewaddress(args, callback) { - var account; +RPC.prototype.getnewaddress = function getnewaddress(args) { + return spawn(function *() { + var account, address; - if (args.help || args.length > 1) - return callback(new RPCError('getnewaddress ( "account" )')); + if (args.help || args.length > 1) + throw new RPCError('getnewaddress ( "account" )'); + + if (args.length === 1) + account = toString(args[0]); + + if (!account) + account = 'default'; + + address = yield this.wallet.createReceive(account); + + return address.getAddress('base58'); + }, this); +}; + +RPC.prototype.getrawchangeaddress = function getrawchangeaddress(args) { + return spawn(function *() { + var address; + + if (args.help || args.length > 1) + throw new RPCError('getrawchangeaddress'); + + address = yield this.wallet.createChange(); + + return address.getAddress('base58'); + }, this); +}; + +RPC.prototype.getreceivedbyaccount = function getreceivedbyaccount(args) { + return spawn(function *() { + var minconf = 0; + var total = 0; + var filter = {}; + var lastConf = -1; + var i, j, path, tx, output, conf, hash, account, paths, txs; + + if (args.help || args.length < 1 || args.length > 2) + throw new RPCError('getreceivedbyaccount "account" ( minconf )'); - if (args.length === 1) account = toString(args[0]); - if (!account) - account = 'default'; + if (!account) + account = 'default'; - this.wallet.createReceive(account, function(err, address) { - if (err) - return callback(err); - callback(null, address.getAddress('base58')); - }); -}; + if (args.length === 2) + minconf = toNumber(args[1], 0); -RPC.prototype.getrawchangeaddress = function getrawchangeaddress(args, callback) { - if (args.help || args.length > 1) - return callback(new RPCError('getrawchangeaddress')); - - this.wallet.createChange(function(err, address) { - if (err) - return callback(err); - callback(null, address.getAddress('base58')); - }); -}; - -RPC.prototype.getreceivedbyaccount = function getreceivedbyaccount(args, callback) { - var self = this; - var minconf = 0; - var total = 0; - var filter = {}; - var lastConf = -1; - var i, j, path, tx, output, conf, hash, account; - - if (args.help || args.length < 1 || args.length > 2) - return callback(new RPCError('getreceivedbyaccount "account" ( minconf )')); - - account = toString(args[0]); - - if (!account) - account = 'default'; - - if (args.length === 2) - minconf = toNumber(args[1], 0); - - this.wallet.getPaths(account, function(err, paths) { - if (err) - return callback(err); + paths = yield this.wallet.getPaths(account); for (i = 0; i < paths.length; i++) { path = paths[i]; filter[path.hash] = true; } - self.wallet.getHistory(account, function(err, txs) { - if (err) - return callback(err); + txs = yield this.wallet.getHistory(account); - for (i = 0; i < txs.length; i++) { - tx = txs[i]; + for (i = 0; i < txs.length; i++) { + tx = txs[i]; - if (minconf) { - if (tx.height === -1) - continue; - if (!(self.chain.height - tx.height + 1 >= minconf)) - continue; - } - - conf = tx.getConfirmations(self.chain.height); - - if (lastConf === -1 || conf < lastConf) - lastConf = conf; - - for (j = 0; j < tx.outputs.length; j++) { - output = tx.outputs[j]; - hash = output.getHash('hex'); - if (filter[hash]) - total += output.value; - } + if (minconf) { + if (tx.height === -1) + continue; + if (!(this.chain.height - tx.height + 1 >= minconf)) + continue; } - callback(null, +utils.btc(total)); - }); - }); + conf = tx.getConfirmations(this.chain.height); + + if (lastConf === -1 || conf < lastConf) + lastConf = conf; + + for (j = 0; j < tx.outputs.length; j++) { + output = tx.outputs[j]; + hash = output.getHash('hex'); + if (filter[hash]) + total += output.value; + } + } + + return +utils.btc(total); + }, this); }; -RPC.prototype.getreceivedbyaddress = function getreceivedbyaddress(args, callback) { - var self = this; - var minconf = 0; - var total = 0; - var i, j, hash, tx, output; +RPC.prototype.getreceivedbyaddress = function getreceivedbyaddress(args) { + return spawn(function *() { + var self = this; + var minconf = 0; + var total = 0; + var i, j, hash, tx, output, txs; - if (args.help || args.length < 1 || args.length > 2) { - return callback(new RPCError('getreceivedbyaddress' - + ' "bitcoinaddress" ( minconf )')); - } + if (args.help || args.length < 1 || args.length > 2) + throw new RPCError('getreceivedbyaddress "bitcoinaddress" ( minconf )'); - hash = bcoin.address.getHash(toString(args[0]), 'hex'); + hash = bcoin.address.getHash(toString(args[0]), 'hex'); - if (!hash) - return callback(new RPCError('Invalid address')); + if (!hash) + throw new RPCError('Invalid address'); - if (args.length === 2) - minconf = toNumber(args[1], 0); + if (args.length === 2) + minconf = toNumber(args[1], 0); - this.wallet.getHistory(function(err, txs) { - if (err) - return callback(err); + txs = yield this.wallet.getHistory(); for (i = 0; i < txs.length; i++) { tx = txs[i]; @@ -3135,20 +3109,18 @@ RPC.prototype.getreceivedbyaddress = function getreceivedbyaddress(args, callbac } } - callback(null, +utils.btc(total)); - }); + return +utils.btc(total); + }, this); }; RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) { - var self = this; - var i, det, receive, member, sent, received, json; + return spawn(function *() { + var i, det, receive, member, sent, received, json, details; - this.wallet.toDetails(tx, function(err, details) { - if (err) - return callback(err); + details = yield this.wallet.toDetails(tx); if (!details) - return callback(new RPCError('TX not found.')); + throw new RPCError('TX not found.'); det = []; sent = 0; @@ -3172,7 +3144,7 @@ RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) { det.push({ account: member.path.name, - address: member.address.toBase58(self.network), + address: member.address.toBase58(this.network), category: 'receive', amount: +utils.btc(member.value), label: member.path.name, @@ -3190,7 +3162,7 @@ RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) { det.push({ account: '', address: member.address - ? member.address.toBase58(self.network) + ? member.address.toBase58(this.network) : null, category: 'send', amount: -(+utils.btc(member.value)), @@ -3216,150 +3188,137 @@ RPC.prototype._toWalletTX = function _toWalletTX(tx, callback) { hex: details.tx.toRaw().toString('hex') }; - callback(null, json); - }); + return json; + }, this); }; RPC.prototype.gettransaction = function gettransaction(args, callback) { - var self = this; - var hash; + return spawn(function *() { + var hash, tx; - if (args.help || args.length < 1 || args.length > 2) - return callback(new RPCError('gettransaction "txid" ( includeWatchonly )')); + if (args.help || args.length < 1 || args.length > 2) + throw new RPCError('gettransaction "txid" ( includeWatchonly )'); - hash = toHash(args[0]); + hash = toHash(args[0]); - if (!hash) - return callback(new RPCError('Invalid parameter')); + if (!hash) + throw new RPCError('Invalid parameter'); - this.wallet.getTX(hash, function(err, tx) { - if (err) - return callback(err); + tx = yield this.wallet.getTX(hash); if (!tx) - return callback(new RPCError('TX not found.')); + throw new RPCError('TX not found.'); - self._toWalletTX(tx, callback); - }); + return yield this._toWalletTX(tx); + }, this); }; -RPC.prototype.abandontransaction = function abandontransaction(args, callback) { - var hash; +RPC.prototype.abandontransaction = function abandontransaction(args) { + return spawn(function *() { + var hash, result; - if (args.help || args.length !== 1) - return callback(new RPCError('abandontransaction "txid"')); + if (args.help || args.length !== 1) + throw new RPCError('abandontransaction "txid"'); - hash = toHash(args[0]); + hash = toHash(args[0]); - if (!hash) - return callback(new RPCError('Invalid parameter.')); + if (!hash) + throw new RPCError('Invalid parameter.'); - this.wallet.abandon(hash, function(err, result) { - if (err) - return callback(err); + result = yield this.wallet.abandon(hash); if (!result) - return callback(new RPCError('Transaction not in wallet.')); + throw new RPCError('Transaction not in wallet.'); - callback(null, null); - }); + return null; + }, this); }; -RPC.prototype.getunconfirmedbalance = function getunconfirmedbalance(args, callback) { - if (args.help || args.length > 0) - return callback(new RPCError('getunconfirmedbalance')); +RPC.prototype.getunconfirmedbalance = function getunconfirmedbalance(args) { + return spawn(function *() { + var balance; - this.wallet.getBalance(function(err, balance) { - if (err) - return callback(err); + if (args.help || args.length > 0) + throw new RPCError('getunconfirmedbalance'); - callback(null, +utils.btc(balance.unconfirmed)); - }); + balance = yield this.wallet.getBalance(); + + return +utils.btc(balance.unconfirmed); + }, this); }; -RPC.prototype.getwalletinfo = function getwalletinfo(args, callback) { - var self = this; +RPC.prototype.getwalletinfo = function getwalletinfo(args) { + return spawn(function *() { + var balance, hashes; - if (args.help || args.length !== 0) - return callback(new RPCError('getwalletinfo')); + if (args.help || args.length !== 0) + throw new RPCError('getwalletinfo'); - this.wallet.getBalance(function(err, balance) { - if (err) - return callback(err); + balance = yield this.wallet.getBalance(); - self.wallet.tx.getHistoryHashes(self.wallet.id, function(err, hashes) { - if (err) - return callback(err); + hashes = yield this.wallet.tx.getHistoryHashes(this.wallet.id); - callback(null, { - walletversion: 0, - balance: +utils.btc(balance.total), - unconfirmed_balance: +utils.btc(balance.unconfirmed), - txcount: hashes.length, - keypoololdest: 0, - keypoolsize: 0, - unlocked_until: self.wallet.master.until, - paytxfee: self.feeRate != null - ? +utils.btc(self.feeRate) - : +utils.btc(0) - }); - }); - }); + return { + walletversion: 0, + balance: +utils.btc(balance.total), + unconfirmed_balance: +utils.btc(balance.unconfirmed), + txcount: hashes.length, + keypoololdest: 0, + keypoolsize: 0, + unlocked_until: this.wallet.master.until, + paytxfee: this.feeRate != null + ? +utils.btc(this.feeRate) + : +utils.btc(0) + }; + }, this); }; -RPC.prototype.importprivkey = function importprivkey(args, callback) { - var self = this; - var secret, label, rescan, key; +RPC.prototype.importprivkey = function importprivkey(args) { + return spawn(function *() { + var secret, label, rescan, key; - if (args.help || args.length < 1 || args.length > 3) { - return callback(new RPCError('importprivkey' - + ' "bitcoinprivkey" ( "label" rescan )')); - } + if (args.help || args.length < 1 || args.length > 3) + throw new RPCError('importprivkey "bitcoinprivkey" ( "label" rescan )'); - secret = toString(args[0]); + secret = toString(args[0]); - if (args.length > 1) - label = toString(args[1]); + if (args.length > 1) + label = toString(args[1]); - if (args.length > 2) - rescan = toBool(args[2]); + if (args.length > 2) + rescan = toBool(args[2]); - if (rescan && this.chain.db.options.prune) - return callback(new RPCError('Cannot rescan when pruned.')); + if (rescan && this.chain.db.options.prune) + throw new RPCError('Cannot rescan when pruned.'); - key = bcoin.keyring.fromSecret(secret); + key = bcoin.keyring.fromSecret(secret); - this.wallet.importKey(0, key, null, function(err) { - if (err) - return callback(err); + yield this.wallet.importKey(0, key, null); if (!rescan) - return callback(null, null); + return null; - self.walletdb.rescan(self.chain.db, 0, function(err) { - if (err) - return callback(err); - callback(null, null); - }); - }); + yield this.walletdb.rescan(this.chain.db, 0); + + return null; + }, this); }; -RPC.prototype.importwallet = function importwallet(args, callback) { - var self = this; - var file, keys, lines, line, parts; - var i, secret, time, label, addr; +RPC.prototype.importwallet = function importwallet(args) { + return spawn(function *() { + var file, keys, lines, line, parts; + var i, secret, time, label, addr; + var data, key; - if (args.help || args.length !== 1) - return callback(new RPCError('importwallet "filename"')); + if (args.help || args.length !== 1) + throw new RPCError('importwallet "filename"'); - file = toString(args[0]); + file = toString(args[0]); - if (!fs) - return callback(new RPCError('FS not available.')); + if (!fs) + throw new RPCError('FS not available.'); - fs.readFile(file, 'utf8', function(err, data) { - if (err) - return callback(err); + data = yield readFile(file, 'utf8'); lines = data.split(/\n+/); keys = []; @@ -3376,7 +3335,7 @@ RPC.prototype.importwallet = function importwallet(args, callback) { parts = line.split(/\s+/); if (parts.length < 4) - return callback(new RPCError('Malformed wallet.')); + throw new RPCError('Malformed wallet.'); try { secret = bcoin.keyring.fromSecret(parts[0]); @@ -3391,117 +3350,99 @@ RPC.prototype.importwallet = function importwallet(args, callback) { keys.push(secret); } - utils.forEachSerial(keys, function(key, next) { - self.wallet.importKey(0, key, null, next); - }, function(err) { - if (err) - return callback(err); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + yield this.wallet.importKey(0, key, null); + } - self.walletdb.rescan(self.chain.db, 0, function(err) { - if (err) - return callback(err); - callback(null, null); - }); - }); - }); + yield this.walletdb.rescan(this.chain.db, 0); + + return null; + }, this); }; -RPC.prototype.importaddress = function importaddress(args, callback) { +RPC.prototype.importaddress = function importaddress(args) { if (args.help || args.length < 1 || args.length > 4) { - return callback(new RPCError('importaddress' - + ' "address" ( "label" rescan p2sh )')); + return Promise.reject(new RPCError( + 'importaddress "address" ( "label" rescan p2sh )')); } // Impossible to implement in bcoin. - callback(new Error('Not implemented.')); + return Promise.reject(new Error('Not implemented.')); }; -RPC.prototype.importpubkey = function importpubkey(args, callback) { - var self = this; - var pubkey, label, rescan, key; +RPC.prototype.importpubkey = function importpubkey(args) { + return spawn(function *() { + var pubkey, label, rescan, key; - if (args.help || args.length < 1 || args.length > 4) - return callback(new RPCError('importpubkey "pubkey" ( "label" rescan )')); + if (args.help || args.length < 1 || args.length > 4) + throw new RPCError('importpubkey "pubkey" ( "label" rescan )'); - pubkey = toString(args[0]); + pubkey = toString(args[0]); - if (!utils.isHex(pubkey)) - return callback(new RPCError('Invalid paremeter.')); + if (!utils.isHex(pubkey)) + throw new RPCError('Invalid paremeter.'); - if (args.length > 1) - label = toString(args[1]); + if (args.length > 1) + label = toString(args[1]); - if (args.length > 2) - rescan = toBool(args[2]); + if (args.length > 2) + rescan = toBool(args[2]); - if (rescan && this.chain.db.options.prune) - return callback(new RPCError('Cannot rescan when pruned.')); + if (rescan && this.chain.db.options.prune) + throw new RPCError('Cannot rescan when pruned.'); - pubkey = new Buffer(pubkey, 'hex'); + pubkey = new Buffer(pubkey, 'hex'); - key = bcoin.keyring.fromPublic(pubkey, this.network); + key = bcoin.keyring.fromPublic(pubkey, this.network); - this.wallet.importKey(0, key, null, function(err) { - if (err) - return callback(err); + yield this.wallet.importKey(0, key, null); if (!rescan) - return callback(null, null); + return null; - self.walletdb.rescan(self.chain.db, 0, function(err) { - if (err) - return callback(err); - callback(null, null); - }); - }); + yield this.walletdb.rescan(this.chain.db, 0); + + return null; + }, this); }; -RPC.prototype.keypoolrefill = function keypoolrefill(args, callback) { +RPC.prototype.keypoolrefill = function keypoolrefill(args) { if (args.help || args.length > 1) - return callback(new RPCError('keypoolrefill ( newsize )')); - callback(null, null); + return Promise.reject(new RPCError('keypoolrefill ( newsize )')); + return Promise.resolve(null); }; -RPC.prototype.listaccounts = function listaccounts(args, callback) { - var self = this; - var map; +RPC.prototype.listaccounts = function listaccounts(args) { + return spawn(function *() { + var i, map, accounts, account, balance; - if (args.help || args.length > 2) - return callback(new RPCError('listaccounts ( minconf includeWatchonly)')); + if (args.help || args.length > 2) + throw new RPCError('listaccounts ( minconf includeWatchonly)'); - map = {}; + map = {}; + accounts = yield this.wallet.getAccounts(); - this.wallet.getAccounts(function(err, accounts) { - if (err) - return callback(err); + for (i = 0; i < accounts.length; i++) { + account = accounts[i]; + balance = yield this.wallet.getBalance(account); + map[account] = +utils.btc(balance.total); + } - utils.forEachSerial(accounts, function(account, next) { - self.wallet.getBalance(account, function(err, balance) { - if (err) - return next(err); - - map[account] = +utils.btc(balance.total); - next(); - }); - }, function(err) { - if (err) - return callback(err); - - callback(null, map); - }); - }); + return map; + }, this); }; -RPC.prototype.listaddressgroupings = function listaddressgroupings(args, callback) { +RPC.prototype.listaddressgroupings = function listaddressgroupings(args) { if (args.help) - return callback(new RPCError('listaddressgroupings')); - callback(new Error('Not implemented.')); + return Promise.reject(new RPCError('listaddressgroupings')); + return Promise.resolve(new Error('Not implemented.')); }; -RPC.prototype.listlockunspent = function listlockunspent(args, callback) { +RPC.prototype.listlockunspent = function listlockunspent(args) { var i, outpoints, outpoint, out; if (args.help || args.length > 0) - return callback(new RPCError('listlockunspent')); + return Promise.reject(new RPCError('listlockunspent')); outpoints = this.wallet.tx.getLocked(); out = []; @@ -3514,16 +3455,16 @@ RPC.prototype.listlockunspent = function listlockunspent(args, callback) { }); } - callback(null, out); + return Promise.resolve(out); }; -RPC.prototype.listreceivedbyaccount = function listreceivedbyaccount(args, callback) { +RPC.prototype.listreceivedbyaccount = function listreceivedbyaccount(args) { var minconf = 0; var includeEmpty = false; if (args.help || args.length > 3) { - return callback(new RPCError('listreceivedbyaccount' - + ' ( minconf includeempty includeWatchonly)')); + return Promise.reject(new RPCError( + 'listreceivedbyaccount ( minconf includeempty includeWatchonly )')); } if (args.length > 0) @@ -3532,16 +3473,16 @@ RPC.prototype.listreceivedbyaccount = function listreceivedbyaccount(args, callb if (args.length > 1) includeEmpty = toBool(args[1], false); - this._listReceived(minconf, includeEmpty, true, callback); + return this._listReceived(minconf, includeEmpty, true); }; -RPC.prototype.listreceivedbyaddress = function listreceivedbyaddress(args, callback) { +RPC.prototype.listreceivedbyaddress = function listreceivedbyaddress(args) { var minconf = 0; var includeEmpty = false; if (args.help || args.length > 3) { - return callback(new RPCError('listreceivedbyaddress' - + ' ( minconf includeempty includeWatchonly)')); + return Promise.reject(new RPCError( + 'listreceivedbyaddress ( minconf includeempty includeWatchonly )')); } if (args.length > 0) @@ -3550,26 +3491,24 @@ RPC.prototype.listreceivedbyaddress = function listreceivedbyaddress(args, callb if (args.length > 1) includeEmpty = toBool(args[1], false); - this._listReceived(minconf, includeEmpty, false, callback); + return this._listReceived(minconf, includeEmpty, false); }; -RPC.prototype._listReceived = function _listReceived(minconf, empty, account, callback) { - var self = this; - var out = []; - var result = []; - var map = {}; - var i, j, path, tx, output, conf, hash; - var entry, address, keys, key, item; +RPC.prototype._listReceived = function _listReceived(minconf, empty, account) { + return spawn(function *() { + var out = []; + var result = []; + var map = {}; + var i, j, path, tx, output, conf, hash; + var entry, address, keys, key, item, paths, txs; - this.wallet.getPaths(function(err, paths) { - if (err) - return callback(err); + paths = yield this.wallet.getPaths(); for (i = 0; i < paths.length; i++) { path = paths[i]; map[path.hash] = { involvesWatchonly: false, - address: path.toAddress().toBase58(self.network), + address: path.toAddress().toBase58(this.network), account: path.name, amount: 0, confirmations: -1, @@ -3577,156 +3516,144 @@ RPC.prototype._listReceived = function _listReceived(minconf, empty, account, ca }; } - self.wallet.getHistory(function(err, txs) { - if (err) - return callback(err); + txs = yield this.wallet.getHistory(); - for (i = 0; i < txs.length; i++) { - tx = txs[i]; + for (i = 0; i < txs.length; i++) { + tx = txs[i]; - if (minconf) { - if (tx.height === -1) - continue; - if (!(self.chain.height - tx.height + 1 >= minconf)) - continue; - } - - conf = tx.getConfirmations(self.chain.height); - - for (j = 0; j < tx.outputs.length; j++) { - output = tx.outputs[j]; - address = output.getAddress(); - if (!address) - continue; - hash = address.getHash('hex'); - entry = map[hash]; - if (entry) { - if (entry.confirmations === -1 || conf < entry.confirmations) - entry.confirmations = conf; - entry.address = address.toBase58(self.network); - entry.amount += output.value; - } - } + if (minconf) { + if (tx.height === -1) + continue; + if (!(this.chain.height - tx.height + 1 >= minconf)) + continue; } + conf = tx.getConfirmations(this.chain.height); + + for (j = 0; j < tx.outputs.length; j++) { + output = tx.outputs[j]; + address = output.getAddress(); + if (!address) + continue; + hash = address.getHash('hex'); + entry = map[hash]; + if (entry) { + if (entry.confirmations === -1 || conf < entry.confirmations) + entry.confirmations = conf; + entry.address = address.toBase58(this.network); + entry.amount += output.value; + } + } + } + + keys = Object.keys(map); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + entry = map[key]; + out.push(entry); + } + + if (account) { + map = {}; + for (i = 0; i < out.length; i++) { + entry = out[i]; + item = map[entry.account]; + if (!item) { + map[entry.account] = entry; + entry.address = undefined; + continue; + } + item.amount += entry.amount; + } + out = []; keys = Object.keys(map); for (i = 0; i < keys.length; i++) { key = keys[i]; entry = map[key]; out.push(entry); } + } - if (account) { - map = {}; - for (i = 0; i < out.length; i++) { - entry = out[i]; - item = map[entry.account]; - if (!item) { - map[entry.account] = entry; - entry.address = undefined; - continue; - } - item.amount += entry.amount; - } - out = []; - keys = Object.keys(map); - for (i = 0; i < keys.length; i++) { - key = keys[i]; - entry = map[key]; - out.push(entry); - } - } + for (i = 0; i < out.length; i++) { + entry = out[i]; + if (!empty && entry.amount === 0) + continue; + if (entry.confirmations === -1) + entry.confirmations = 0; + entry.amount = +utils.btc(entry.amount); + result.push(entry); + } - for (i = 0; i < out.length; i++) { - entry = out[i]; - if (!empty && entry.amount === 0) - continue; - if (entry.confirmations === -1) - entry.confirmations = 0; - entry.amount = +utils.btc(entry.amount); - result.push(entry); - } - - callback(null, result); - }); - }); + return result; + }, this); }; -RPC.prototype.listsinceblock = function listsinceblock(args, callback) { - var self = this; - var block, conf, out, highest; +RPC.prototype.listsinceblock = function listsinceblock(args) { + return spawn(function *() { + var block, conf, out, highest; + var i, height, txs, tx; - if (args.help) { - return callback(new RPCError('listsinceblock' - + ' ( "blockhash" target-confirmations includeWatchonly)')); - } + if (args.help) { + throw new RPCError('listsinceblock' + + ' ( "blockhash" target-confirmations includeWatchonly)'); + } - if (args.length > 0) { - block = toHash(args[0]); - if (!block) - return callback(new RPCError('Invalid parameter.')); - } + if (args.length > 0) { + block = toHash(args[0]); + if (!block) + throw new RPCError('Invalid parameter.'); + } - conf = 0; + conf = 0; - if (args.length > 1) - conf = toNumber(args[1], 0); + if (args.length > 1) + conf = toNumber(args[1], 0); - out = []; + out = []; - this.chain.db.getHeight(block, function(err, height) { - if (err) - return callback(err); + height = yield this.chain.db.getHeight(block); if (height === -1) - height = self.chain.height; + height = this.chain.height; - self.wallet.getHistory(function(err, txs) { - if (err) - return callback(err); + txs = yield this.wallet.getHistory(); - utils.forEachSerial(txs, function(tx, next, i) { - if (tx.height < height) - return next(); + for (i = 0; i < txs.length; i++) { + tx = txs[i]; - if (tx.getConfirmations(self.chain.height) < conf) - return next(); + if (tx.height < height) + continue; - if (!highest || tx.height > highest) - highest = tx; + if (tx.getConfirmations(this.chain.height) < conf) + continue; - self._toListTX(tx, function(err, json) { - if (err) - return next(err); - out.push(json); - next(); - }); - }, function(err) { - if (err) - return callback(err); + if (!highest || tx.height > highest) + highest = tx; - callback(null, { - transactions: out, - lastblock: highest && highest.block - ? utils.revHex(highest.block) - : constants.NULL_HASH - }); - }); - }); - }); + json = yield this._toListTX(tx); + + out.push(json); + } + + return { + transactions: out, + lastblock: highest && highest.block + ? utils.revHex(highest.block) + : constants.NULL_HASH + }; + }, this); }; -RPC.prototype._toListTX = function _toListTX(tx, callback) { - var self = this; - var i, receive, member, det, sent, received, index; - var sendMember, recMember, sendIndex, recIndex, json; +RPC.prototype._toListTX = function _toListTX(tx) { + return spawn(function *() { + var i, receive, member, det, sent, received, index; + var sendMember, recMember, sendIndex, recIndex, json; + var details; - this.wallet.toDetails(tx, function(err, details) { - if (err) - return callback(err); + details = yield this.wallet.toDetails(tx); if (!details) - return callback(new RPCError('TX not found.')); + throw new RPCError('TX not found.'); det = []; sent = 0; @@ -3776,7 +3703,7 @@ RPC.prototype._toListTX = function _toListTX(tx, callback) { json = { account: member.path ? member.path.name : '', address: member.address - ? member.address.toBase58(self.network) + ? member.address.toBase58(this.network) : null, category: receive ? 'receive' : 'send', amount: +utils.btc(receive ? received : -sent), @@ -3793,149 +3720,135 @@ RPC.prototype._toListTX = function _toListTX(tx, callback) { 'bip125-replaceable': 'no' }; - callback(null, json); - }); + return json; + }, this); }; -RPC.prototype.listtransactions = function listtransactions(args, callback) { - var self = this; - var account, count; +RPC.prototype.listtransactions = function listtransactions(args) { + return spawn(function *() { + var i, account, count, txs, tx, json; - if (args.help || args.length > 4) { - return callback(new RPCError('listtransactions' - + ' ( "account" count from includeWatchonly)')); - } + if (args.help || args.length > 4) { + throw new RPCError( + 'listtransactions ( "account" count from includeWatchonly)'); + } - account = null; + account = null; - if (args.length > 0) { - account = toString(args[0]); - if (!account) - account = 'default'; - } + if (args.length > 0) { + account = toString(args[0]); + if (!account) + account = 'default'; + } - count = 10; - - if (args.length > 1) - count = toNumber(args[1], 10); - - if (count < 0) count = 10; - this.wallet.getHistory(account, function(err, txs) { - if (err) - return callback(err); + if (args.length > 1) + count = toNumber(args[1], 10); - utils.forEachSerial(txs, function(tx, next, i) { - self._toListTX(tx, function(err, json) { - if (err) - return next(err); - txs[i] = json; - next(); - }); - }, function(err) { - if (err) - return callback(err); - callback(null, txs); - }); - }); + if (count < 0) + count = 10; + + txs = yield this.wallet.getHistory(); + + for (i = 0; i < txs.length; i++) { + tx = txs[i]; + json = yield this._toListTX(tx); + txs[i] = json; + } + + return txs; + }, this); }; -RPC.prototype.listunspent = function listunspent(args, callback) { - var self = this; - var minDepth = 1; - var maxDepth = 9999999; - var out = []; - var i, addresses, addrs, depth, address, hash; +RPC.prototype.listunspent = function listunspent(args) { + return spawn(function *() { + var minDepth = 1; + var maxDepth = 9999999; + var out = []; + var i, addresses, addrs, depth, address, hash, coins, coin, ring; - if (args.help || args.length > 3) { - return callback(new RPCError('listunspent' - + ' ( minconf maxconf ["address",...] )')); - } - - if (args.length > 0) - minDepth = toNumber(args[0], 1); - - if (args.length > 1) - maxDepth = toNumber(args[1], maxDepth); - - if (args.length > 2) - addrs = toArray(args[2]); - - if (addrs) { - addresses = {}; - for (i = 0; i < addrs.length; i++) { - address = toString(addrs[i]); - hash = bcoin.address.getHash(address, 'hex'); - - if (!hash) - return callback(new RPCError('Invalid address.')); - - if (addresses[hash]) - return callback(new RPCError('Duplicate address.')); - - addresses[hash] = true; + if (args.help || args.length > 3) { + throw new RPCError('listunspent' + + ' ( minconf maxconf ["address",...] )'); } - } - this.wallet.getCoins(function(err, coins) { - if (err) - return callback(err); + if (args.length > 0) + minDepth = toNumber(args[0], 1); + + if (args.length > 1) + maxDepth = toNumber(args[1], maxDepth); + + if (args.length > 2) + addrs = toArray(args[2]); + + if (addrs) { + addresses = {}; + for (i = 0; i < addrs.length; i++) { + address = toString(addrs[i]); + hash = bcoin.address.getHash(address, 'hex'); + + if (!hash) + return callback(new RPCError('Invalid address.')); + + if (addresses[hash]) + return callback(new RPCError('Duplicate address.')); + + addresses[hash] = true; + } + } + + coins = yield this.wallet.getCoins(); + + for (i = 0; i < coins.length; i++ ) { + coin = coins[i]; - utils.forEachSerial(coins, function(coin, next) { depth = coin.height !== -1 - ? self.chain.height - coin.height + 1 + ? this.chain.height - coin.height + 1 : 0; if (!(depth >= minDepth && depth <= maxDepth)) - return next(); + continue; address = coin.getAddress(); if (!address) - return next(); + continue; hash = coin.getHash('hex'); if (addresses) { if (!hash || !addresses[hash]) - return next(); + continue; } - self.wallet.getKeyRing(hash, function(err, ring) { - if (err) - return next(err); + ring = yield this.wallet.getKeyRing(hash); - out.push({ - txid: utils.revHex(coin.hash), - vout: coin.index, - address: address ? address.toBase58(self.network) : null, - account: ring ? ring.path.name : undefined, - redeemScript: ring && ring.script - ? ring.script.toJSON() - : undefined, - scriptPubKey: coin.script.toJSON(), - amount: +utils.btc(coin.value), - confirmations: depth, - spendable: !self.wallet.tx.isLocked(coin), - solvable: true - }); - - next(); + out.push({ + txid: utils.revHex(coin.hash), + vout: coin.index, + address: address ? address.toBase58(this.network) : null, + account: ring ? ring.path.name : undefined, + redeemScript: ring && ring.script + ? ring.script.toJSON() + : undefined, + scriptPubKey: coin.script.toJSON(), + amount: +utils.btc(coin.value), + confirmations: depth, + spendable: !this.wallet.tx.isLocked(coin), + solvable: true }); - }, function(err) { - if (err) - return callback(err); - callback(null, out); - }); - }); + } + + return out; + }, this); }; -RPC.prototype.lockunspent = function lockunspent(args, callback) { +RPC.prototype.lockunspent = function lockunspent(args) { var i, unlock, outputs, output, outpoint; if (args.help || args.length < 1 || args.length > 2) { - return callback(new RPCError('lockunspent' + return Promise.reject(new RPCError('lockunspent' + ' unlock ([{"txid":"txid","vout":n},...])')); } @@ -3950,23 +3863,23 @@ RPC.prototype.lockunspent = function lockunspent(args, callback) { outputs = toArray(args[1]); if (!outputs) - return callback(new RPCError('Invalid paremeter.')); + return Promise.reject(new RPCError('Invalid paremeter.')); for (i = 0; i < outputs.length; i++) { output = outputs[i]; if (!output || typeof output !== 'object') - return callback(new RPCError('Invalid paremeter.')); + return Promise.reject(new RPCError('Invalid paremeter.')); outpoint = new bcoin.outpoint(); outpoint.hash = toHash(output.txid); outpoint.index = toNumber(output.vout); if (!outpoint.txid) - return callback(new RPCError('Invalid paremeter.')); + return Promise.reject(new RPCError('Invalid paremeter.')); if (outpoint.index < 0) - return callback(new RPCError('Invalid paremeter.')); + return Promise.reject(new RPCError('Invalid paremeter.')); if (unlock) this.wallet.unlockCoin(outpoint); @@ -3974,33 +3887,39 @@ RPC.prototype.lockunspent = function lockunspent(args, callback) { this.wallet.lockCoin(outpoint); } - callback(null, true); + return Promise.resolve(true); }; -RPC.prototype.move = function move(args, callback) { +RPC.prototype.move = function move(args) { // Not implementing: stupid and deprecated. - callback(new Error('Not implemented.')); + Promise.reject(new Error('Not implemented.')); }; -RPC.prototype._send = function _send(account, address, amount, subtractFee, callback) { - var options = { - account: account, - subtractFee: subtractFee, - rate: this.feeRate, - outputs: [{ - address: address, - value: amount - }] - }; +RPC.prototype._send = function _send(account, address, amount, subtractFee) { + return spawn(function *() { + var tx, options; - this.wallet.send(options, callback); + options = { + account: account, + subtractFee: subtractFee, + rate: this.feeRate, + outputs: [{ + address: address, + value: amount + }] + }; + + tx = yield this.wallet.send(options); + + return tx.rhash; + }, this); }; -RPC.prototype.sendfrom = function sendfrom(args, callback) { +RPC.prototype.sendfrom = function sendfrom(args) { var account, address, amount; if (args.help || args.length < 3 || args.length > 6) { - return callback(new RPCError('sendfrom' + return Promise.reject(new RPCError('sendfrom' + ' "fromaccount" "tobitcoinaddress"' + ' amount ( minconf "comment" "comment-to" )')); } @@ -4012,83 +3931,79 @@ RPC.prototype.sendfrom = function sendfrom(args, callback) { if (!account) account = 'default'; - this._send(account, address, amount, false, function(err, tx) { - if (err) - return callback(err); - callback(null, tx.rhash); - }); + return this._send(account, address, amount, false); }; -RPC.prototype.sendmany = function sendmany(args, callback) { - var account, sendTo, minDepth, comment, subtractFee; - var i, outputs, keys, uniq; - var key, value, address, hash, output, options; +RPC.prototype.sendmany = function sendmany(args) { + return spawn(function *() { + var account, sendTo, minDepth, comment, subtractFee; + var i, outputs, keys, uniq, tx; + var key, value, address, hash, output, options; - if (args.help || args.length < 2 || args.length > 5) { - return callback(new RPCError('sendmany' - + ' "fromaccount" {"address":amount,...}' - + ' ( minconf "comment" ["address",...] )')); - } + if (args.help || args.length < 2 || args.length > 5) { + return Promise.reject(new RPCError('sendmany' + + ' "fromaccount" {"address":amount,...}' + + ' ( minconf "comment" ["address",...] )')); + } - account = toString(args[0]); - sendTo = toObject(args[1]); - minDepth = 1; + account = toString(args[0]); + sendTo = toObject(args[1]); + minDepth = 1; - if (!account) - account = 'default'; + if (!account) + account = 'default'; - if (!sendTo) - return callback(new RPCError('Invalid parameter.')); + if (!sendTo) + throw new RPCError('Invalid parameter.'); - if (args.length > 2) - minDepth = toNumber(args[2], 1); + if (args.length > 2) + minDepth = toNumber(args[2], 1); - if (args.length > 3) - comment = toString(args[3]); + if (args.length > 3) + comment = toString(args[3]); - if (args.length > 4) - subtractFee = toArray(args[4]); + if (args.length > 4) + subtractFee = toArray(args[4]); - outputs = []; - keys = Object.keys(sendTo); - uniq = {}; + outputs = []; + keys = Object.keys(sendTo); + uniq = {}; - for (i = 0; i < keys.length; i++) { - key = keys[i]; - value = toSatoshi(sendTo[key]); - address = bcoin.address.fromBase58(key); - hash = address.getHash('hex'); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + value = toSatoshi(sendTo[key]); + address = bcoin.address.fromBase58(key); + hash = address.getHash('hex'); - if (uniq[hash]) - return callback(new RPCError('Invalid parameter.')); + if (uniq[hash]) + throw new RPCError('Invalid parameter.'); - uniq[hash] = true; + uniq[hash] = true; - output = new bcoin.output(); - output.value = value; - output.script.fromAddress(address); - outputs.push(output); - } + output = new bcoin.output(); + output.value = value; + output.script.fromAddress(address); + outputs.push(output); + } - options = { - outputs: outputs, - subtractFee: subtractFee, - account: account, - confirmations: minDepth - }; + options = { + outputs: outputs, + subtractFee: subtractFee, + account: account, + confirmations: minDepth + }; - this.wallet.send(options, function(err, tx) { - if (err) - return callback(err); - callback(null, tx.rhash); - }); + tx = yield this.wallet.send(options); + + return tx.rhash; + }, this); }; -RPC.prototype.sendtoaddress = function sendtoaddress(args, callback) { +RPC.prototype.sendtoaddress = function sendtoaddress(args) { var address, amount, subtractFee; if (args.help || args.length < 2 || args.length > 5) { - return callback(new RPCError('sendtoaddress' + return Promise.reject(new RPCError('sendtoaddress' + ' "bitcoinaddress" amount' + ' ( "comment" "comment-to"' + ' subtractfeefromamount )')); @@ -4098,206 +4013,196 @@ RPC.prototype.sendtoaddress = function sendtoaddress(args, callback) { amount = toSatoshi(args[1]); subtractFee = toBool(args[4]); - this._send(null, address, amount, subtractFee, function(err, tx) { - if (err) - return callback(err); - callback(null, tx.rhash); - }); + return this._send(null, address, amount, subtractFee); }; -RPC.prototype.setaccount = function setaccount(args, callback) { - if (args.help || args.length < 1 || args.length > 2) - return callback(new RPCError('setaccount "bitcoinaddress" "account"')); +RPC.prototype.setaccount = function setaccount(args) { + if (args.help || args.length < 1 || args.length > 2) { + return Promise.reject(new RPCError( + 'setaccount "bitcoinaddress" "account"')); + } // Impossible to implement in bcoin: - callback(new Error('Not implemented.')); + return Promise.reject(new Error('Not implemented.')); }; -RPC.prototype.settxfee = function settxfee(args, callback) { +RPC.prototype.settxfee = function settxfee(args) { if (args.help || args.length < 1 || args.length > 1) - return callback(new RPCError('settxfee amount')); + return Promise.reject(new RPCError('settxfee amount')); this.feeRate = toSatoshi(args[0]); - callback(null, true); + return Promise.resolve(true); }; -RPC.prototype.signmessage = function signmessage(args, callback) { - var self = this; - var address, msg, sig; +RPC.prototype.signmessage = function signmessage(args) { + return spawn(function *() { + var address, msg, sig; - if (args.help || args.length !== 2) - return callback(new RPCError('signmessage "bitcoinaddress" "message"')); + if (args.help || args.length !== 2) + throw new RPCError('signmessage "bitcoinaddress" "message"'); - address = toString(args[0]); - msg = toString(args[1]); + address = toString(args[0]); + msg = toString(args[1]); - address = bcoin.address.getHash(address, 'hex'); + address = bcoin.address.getHash(address, 'hex'); - if (!address) - return callback(new RPCError('Invalid address.')); + if (!address) + throw new RPCError('Invalid address.'); - this.wallet.getKeyRing(address, function(err, ring) { - if (err) - return callback(err); + ring = yield this.wallet.getKeyRing(address); if (!ring) - return callback(new RPCError('Address not found.')); + throw new RPCError('Address not found.'); - if (!self.wallet.master.key) - return callback(new RPCError('Wallet is locked.')); + if (!this.wallet.master.key) + throw new RPCError('Wallet is locked.'); msg = new Buffer(RPC.magic + msg, 'utf8'); msg = crypto.hash256(msg); sig = ring.sign(msg); - callback(null, sig.toString('base64')); - }); + return sig.toString('base64'); + }, this); }; -RPC.prototype.walletlock = function walletlock(args, callback) { +RPC.prototype.walletlock = function walletlock(args) { if (args.help || (this.wallet.master.encrypted && args.length !== 0)) - return callback(new RPCError('walletlock')); + throw new RPCError('walletlock'); if (!this.wallet.master.encrypted) - return callback(new RPCError('Wallet is not encrypted.')); + throw new RPCError('Wallet is not encrypted.'); this.wallet.lock(); - callback(null, null); + + return null; }; -RPC.prototype.walletpassphrasechange = function walletpassphrasechange(args, callback) { - var old, new_; +RPC.prototype.walletpassphrasechange = function walletpassphrasechange(args) { + return spawn(function *() { + var old, new_; - if (args.help || (this.wallet.master.encrypted && args.length !== 2)) { - return callback(new RPCError('walletpassphrasechange' - + ' "oldpassphrase" "newpassphrase"')); - } + if (args.help || (this.wallet.master.encrypted && args.length !== 2)) { + throw new RPCError('walletpassphrasechange' + + ' "oldpassphrase" "newpassphrase"'); + } - if (!this.wallet.master.encrypted) - return callback(new RPCError('Wallet is not encrypted.')); + if (!this.wallet.master.encrypted) + throw new RPCError('Wallet is not encrypted.'); - old = toString(args[0]); - new_ = toString(args[1]); + old = toString(args[0]); + new_ = toString(args[1]); - if (old.length < 1 || new_.length < 1) - return callback(new RPCError('Invalid parameter')); + if (old.length < 1 || new_.length < 1) + throw new RPCError('Invalid parameter'); - this.wallet.setPassphrase(old, new_, function(err) { - if (err) - return callback(err); + yield this.wallet.setPassphrase(old, new_); - callback(null, null); - }); + return null; + }, this); }; -RPC.prototype.walletpassphrase = function walletpassphrase(args, callback) { - var passphrase, timeout; +RPC.prototype.walletpassphrase = function walletpassphrase(args) { + return spawn(function *() { + var passphrase, timeout; - if (args.help || (this.wallet.master.encrypted && args.length !== 2)) - return callback(new RPCError('walletpassphrase "passphrase" timeout')); + if (args.help || (this.wallet.master.encrypted && args.length !== 2)) + throw new RPCError('walletpassphrase "passphrase" timeout'); - if (!this.wallet.master.encrypted) - return callback(new RPCError('Wallet is not encrypted.')); + if (!this.wallet.master.encrypted) + throw new RPCError('Wallet is not encrypted.'); - passphrase = toString(args[0]); - timeout = toNumber(args[1]); + passphrase = toString(args[0]); + timeout = toNumber(args[1]); - if (passphrase.length < 1) - return callback(new RPCError('Invalid parameter')); + if (passphrase.length < 1) + throw new RPCError('Invalid parameter'); - if (timeout < 0) - return callback(new RPCError('Invalid parameter')); + if (timeout < 0) + throw new RPCError('Invalid parameter'); - this.wallet.unlock(passphrase, timeout, function(err) { - if (err) - return callback(err); - callback(null, null); - }); + yield this.wallet.unlock(passphrase, timeout); + + return null; + }, this); }; -RPC.prototype.importprunedfunds = function importprunedfunds(args, callback) { - var self = this; - var tx, block, label; +RPC.prototype.importprunedfunds = function importprunedfunds(args) { + return spawn(function *() { + var tx, block, label, height, added; - if (args.help || args.length < 2 || args.length > 3) { - return callback(new RPCError('importprunedfunds' - + ' "rawtransaction" "txoutproof" ( "label" )')); - } + if (args.help || args.length < 2 || args.length > 3) { + throw new RPCError('importprunedfunds' + + ' "rawtransaction" "txoutproof" ( "label" )'); + } - tx = args[0]; - block = args[1]; + tx = args[0]; + block = args[1]; - if (!utils.isHex(tx) || !utils.isHex(block)) - return callback(new RPCError('Invalid parameter.')); + if (!utils.isHex(tx) || !utils.isHex(block)) + throw new RPCError('Invalid parameter.'); - tx = bcoin.tx.fromRaw(tx, 'hex'); - block = bcoin.merkleblock.fromRaw(block, 'hex'); + tx = bcoin.tx.fromRaw(tx, 'hex'); + block = bcoin.merkleblock.fromRaw(block, 'hex'); - if (args.length === 3) - label = toString(args[2]); + if (args.length === 3) + label = toString(args[2]); - if (!block.verify()) - return callback(new RPCError('Invalid proof.')); + if (!block.verify()) + throw new RPCError('Invalid proof.'); - if (!block.hasTX(tx)) - return callback(new RPCError('Invalid proof.')); + if (!block.hasTX(tx)) + throw new RPCError('Invalid proof.'); - this.chain.db.getHeight(block.hash('hex'), function(err, height) { - if (err) - return callback(err); + height = yield this.chain.db.getHeight(block.hash('hex')); if (height === -1) - return callback(new RPCError('Invalid proof.')); + throw new RPCError('Invalid proof.'); tx.index = block.indexOf(tx); tx.block = block.hash('hex'); tx.ts = block.ts; tx.height = height; - self.wallet.addTX(tx, function(err, added) { - if (err) - return callback(err); + added = yield this.wallet.addTX(tx); - if (!added) - return callback(new RPCError('No tracked address for TX.')); + if (!added) + throw new RPCError('No tracked address for TX.'); - callback(null, null); - }); - }); + return null; + }, this); }; -RPC.prototype.removeprunedfunds = function removeprunedfunds(args, callback) { - var hash; +RPC.prototype.removeprunedfunds = function removeprunedfunds(args) { + return spawn(function *() { + var hash, removed; - if (args.help || args.length !== 1) - return callback(new RPCError('removeprunedfunds "txid"')); + if (args.help || args.length !== 1) + throw new RPCError('removeprunedfunds "txid"'); - hash = toHash(args[0]); + hash = toHash(args[0]); - if (!hash) - return callback(new RPCError('Invalid parameter.')); + if (!hash) + throw new RPCError('Invalid parameter.'); - this.wallet.tx.remove(hash, function(err, removed) { - if (err) - return callback(err); + removed = yield this.wallet.tx.remove(hash); if (!removed) - return callback(new RPCError('Transaction not in wallet.')); + throw new RPCError('Transaction not in wallet.'); - callback(null, null); - }); + return null; + }, this); }; -RPC.prototype.getmemory = function getmemory(args, callback) { +RPC.prototype.getmemory = function getmemory(args) { var mem; if (args.help || args.length !== 0) - return callback(new RPCError('getmemory')); + return Promise.reject(new RPCError('getmemory')); mem = process.memoryUsage(); - callback(null, { + return Promise.resolve({ rss: utils.mb(mem.rss), jsheap: utils.mb(mem.heapUsed), jsheaptotal: utils.mb(mem.heapTotal), @@ -4375,6 +4280,12 @@ function reverseEndian(data) { } } +function writeFile(file, data) { + return new Promise(function(resolve, reject) { + fs.writeFile(file, out, utils.P(resolve, reject)); + }); +} + /* * Expose */ diff --git a/lib/http/rpcclient.js b/lib/http/rpcclient.js index dd1b2688..f8b2952c 100644 --- a/lib/http/rpcclient.js +++ b/lib/http/rpcclient.js @@ -8,6 +8,7 @@ var Network = require('../protocol/network'); var request = require('./request'); +var spawn = require('../utils/spawn'); /** * BCoin RPC client. @@ -43,38 +44,37 @@ function RPCClient(options) { * @param {Function} callback - Returns [Error, Object?]. */ -RPCClient.prototype.call = function call(method, params, callback) { - request({ - method: 'POST', - uri: this.uri, - json: { - method: method, - params: params, - id: this.id++ - }, - auth: { - username: 'bitcoinrpc', - password: this.apiKey || '' - }, - expect: 'json' - }, function(err, res, body) { - if (err) - return callback(err); +RPCClient.prototype.call = function call(method, params) { + return spawn(function *() { + var res = yield request.promise({ + method: 'POST', + uri: this.uri, + json: { + method: method, + params: params, + id: this.id++ + }, + auth: { + username: 'bitcoinrpc', + password: this.apiKey || '' + }, + expect: 'json' + }); - if (!body) - return callback(); + if (!res.body) + return; if (res.statusCode === 400) - return callback(null, body.result); + return res.body.result; if (res.statusCode !== 200) { - if (body.error) - return callback(new Error(body.error.message)); - return callback(new Error('Status code: ' + res.statusCode)); + if (res.body.error) + throw new Error(res.body.error.message); + throw new Error('Status code: ' + res.statusCode); } - return callback(null, body.result); - }); + return res.body.result; + }, this); }; /* diff --git a/lib/http/server.js b/lib/http/server.js index bfbaf8b4..3cddad5a 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -377,8 +377,10 @@ HTTPServer.prototype._init = function _init() { spawn(function *() { var wallet; - if (req.path.length < 2 || req.path[0] !== 'wallet') - return next(); + if (req.path.length < 2 || req.path[0] !== 'wallet') { + next(); + return; + } if (!self.options.walletAuth) { wallet = yield self.walletdb.get(req.options.id); diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 2fe82ea5..ffb678c0 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -194,7 +194,6 @@ Fullnode.prototype._init = function _init() { }); this.chain.on('connect', function(entry, block) { - return; self.walletdb.addBlock(entry, block.txs).catch(onError); if (self.chain.synced) diff --git a/lib/utils/locker.js b/lib/utils/locker.js index 6efc4c28..d8491913 100644 --- a/lib/utils/locker.js +++ b/lib/utils/locker.js @@ -32,6 +32,8 @@ function Locker(parent, add) { this.pending = []; this.pendingMap = {}; this.add = add; + this._unlock = this.unlock.bind(this); + this._unlocker = this.unlocker.bind(this); } utils.inherits(Locker, EventEmitter); @@ -83,9 +85,7 @@ Locker.prototype.lock = function lock(arg1, arg2) { if (force) { assert(this.busy); - return new Promise(function(resolve, reject) { - resolve(function unlock() {}); - }); + return new Promise(this._force); } if (this.busy) { @@ -100,31 +100,39 @@ Locker.prototype.lock = function lock(arg1, arg2) { this.busy = true; - return new Promise(function(resolve, reject) { - resolve(function unlock() { - var item, res, obj; + return new Promise(this._unlock); +}; - self.busy = false; +Locker.prototype._force = function force(resolve, reject) { + resolve(utils.nop); +}; - if (self.pending.length === 0) - self.emit('drain'); +Locker.prototype.unlock = function unlock(resolve, reject) { + resolve(this._unlocker); +}; - if (self.jobs.length === 0) - return; +Locker.prototype.unlocker = function unlocker() { + var item, resolve, obj; - item = self.jobs.shift(); - res = item[0]; - obj = item[1]; + this.busy = false; - if (obj) { - assert(obj === self.pending.shift()); - delete self.pendingMap[obj.hash('hex')]; - } + if (this.pending.length === 0) + this.emit('drain'); - self.busy = true; - res(unlock); - }); - }); + if (this.jobs.length === 0) + return; + + item = this.jobs.shift(); + resolve = item[0]; + obj = item[1]; + + if (obj) { + assert(obj === this.pending.shift()); + delete this.pendingMap[obj.hash('hex')]; + } + + this.busy = true; + resolve(this._unlocker); }; /** diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 57c83ce3..85572636 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -1297,6 +1297,7 @@ WalletDB.prototype.addBlock = function addBlock(entry, txs, force) { unlock(); throw e; } + unlock(); return; } }