http: more methods and better validation.

This commit is contained in:
Christopher Jeffrey 2016-10-05 04:49:37 -07:00
parent 6f18d890f1
commit 80aa8f8c4c
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 572 additions and 104 deletions

79
bin/cli
View File

@ -7,7 +7,6 @@ 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 assert = require('assert');
var main;
function CLI() {
@ -38,8 +37,8 @@ CLI.prototype.createWallet = co(function* createWallet() {
if (this.config.master)
options.master = this.config.master;
if (this.config.key)
options.key = this.config.key;
if (this.config.mnemonic)
options.master = this.config.mnemonic;
if (this.config.m)
options.m = this.config.m >>> 0;
@ -65,13 +64,13 @@ CLI.prototype.createWallet = co(function* createWallet() {
CLI.prototype.addKey = co(function* addKey() {
var key = this.argv[0];
yield this.wallet.addKey(this.config.account, key);
this.log('added');
this.log('Added key.');
});
CLI.prototype.removeKey = co(function* removeKey() {
var key = this.argv[0];
yield this.wallet.removeKey(this.config.account, key);
this.log('removed');
this.log('Removed key.');
});
CLI.prototype.getAccount = co(function* getAccount() {
@ -130,6 +129,8 @@ CLI.prototype.getTX = co(function* getTX() {
CLI.prototype.getBlock = co(function* getBlock() {
var hash = this.argv[0];
var block;
if (hash.length !== 64)
hash = +hash;
@ -137,7 +138,7 @@ CLI.prototype.getBlock = co(function* getBlock() {
if (!block) {
this.log('Block not found.');
return
return;
}
this.log(block);
@ -268,7 +269,6 @@ CLI.prototype.zap = co(function* zap() {
});
CLI.prototype.broadcast = co(function* broadcast() {
var self = this;
var raw = this.argv[0] || this.config.tx;
var tx = yield this.client.broadcast(raw);
this.log('Broadcasted:');
@ -292,6 +292,56 @@ CLI.prototype.retoken = co(function* retoken() {
this.log(result);
});
CLI.prototype.rescan = co(function* rescan() {
var hash = this.argv[0];
if (hash.length !== 64)
hash = +hash;
yield this.client.rescan(hash);
this.log('Rescanning...');
});
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 = [];
@ -369,6 +419,14 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
return yield this.getDetails();
case 'view':
return yield this.viewTX();
case 'import':
return yield this.importKey();
case 'watch':
return yield this.importAddress();
case 'lock':
return yield this.lock();
case 'unlock':
return yield this.unlock();
default:
this.log('Unrecognized command.');
this.log('Commands:');
@ -390,6 +448,10 @@ CLI.prototype.handleWallet = co(function* handleWallet() {
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(' $ import [wif|hex]: Import private or public key.');
this.log(' $ watch [address]: Import an address.');
this.log(' $ lock: Lock wallet.');
this.log(' $ unlock [passphrase] [timeout]: Unlock wallet.');
this.log('Other Options:');
this.log(' --passphrase [passphrase]: For signing and account creation.');
this.log(' --account [account-name]: Account name.');
@ -421,6 +483,8 @@ CLI.prototype.handleNode = co(function* handleNode() {
return yield this.getCoin();
case 'block':
return yield this.getBlock();
case 'rescan':
return yield this.rescan();
case 'rpc':
return yield this.rpc();
default:
@ -432,6 +496,7 @@ CLI.prototype.handleNode = co(function* handleNode() {
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/hash]: Rescan for transactions.');
this.log(' $ rpc [command] [args]: Execute RPC command.');
return;
}

View File

@ -12,7 +12,6 @@ var AsyncObject = require('../utils/async');
var RPCClient = require('./rpcclient');
var utils = require('../utils/utils');
var co = require('../utils/co');
var assert = require('assert');
var request = require('./request').promise;
/**
@ -348,6 +347,17 @@ HTTPClient.prototype.broadcast = function broadcast(tx) {
return this._post('/broadcast', body);
};
/**
* Rescan the chain.
* @param {Hash|Number} hash
* @returns {Promise}
*/
HTTPClient.prototype.rescan = function rescan(hash) {
var options = { hash: hash };
return this._post('/rescan', options);
};
/**
* Listen for events on wallet id.
* @param {WalletID} id
@ -650,7 +660,6 @@ HTTPClient.prototype.zap = function zap(id, account, age) {
account: account,
age: age
};
assert(utils.isNumber(age));
return this._post('/wallet/' + id + '/zap', body);
};
@ -664,11 +673,7 @@ HTTPClient.prototype.zap = function zap(id, account, age) {
*/
HTTPClient.prototype.addKey = function addKey(id, account, key) {
var options;
key = key.xpubkey || key;
options = { account: account, key: key };
var options = { account: account, accountKey: key };
return this._put('/wallet/' + id + '/key', options);
};
@ -682,14 +687,108 @@ HTTPClient.prototype.addKey = function addKey(id, account, key) {
*/
HTTPClient.prototype.removeKey = function removeKey(id, account, key) {
var options;
key = key.xpubkey || key;
options = { account: account, key: key };
var options = { account: account, accountKey: key };
return this._del('/wallet/' + id + '/key', options);
};
/**
* Import private key.
* @param {String} id
* @param {Number|String} account
* @param {String} key
* @returns {Promise}
*/
HTTPClient.prototype.importPrivate = function importPrivate(id, account, key) {
var options = { privateKey: key };
return this._post('/wallet/' + id + '/import', options);
};
/**
* Import public key.
* @param {String} id
* @param {Number|String} account
* @param {String} key
* @returns {Promise}
*/
HTTPClient.prototype.importPublic = function importPublic(id, account, key) {
var options = { publicKey: key };
return this._post('/wallet/' + id + '/import', options);
};
/**
* Import address.
* @param {String} id
* @param {Number|String} account
* @param {String} address
* @returns {Promise}
*/
HTTPClient.prototype.importAddress = function importAddress(id, account, address) {
var options = { address: address };
return this._post('/wallet/' + id + '/import', options);
};
/**
* Lock a coin.
* @param {String} id
* @param {String} hash
* @param {Number} index
* @returns {Promise}
*/
HTTPClient.prototype.lockCoin = function lockCoin(id, hash, index) {
var options = { hash: hash, index: index };
return this._put('/wallet/' + id + '/coin/locked', options);
};
/**
* Unlock a coin.
* @param {String} id
* @param {String} hash
* @param {Number} index
* @returns {Promise}
*/
HTTPClient.prototype.unlockCoin = function unlockCoin(id, hash, index) {
var options = { hash: hash, index: index };
return this._del('/wallet/' + id + '/coin/locked', options);
};
/**
* Get locked coins.
* @param {String} id
* @returns {Promise}
*/
HTTPClient.prototype.getLocked = function getLocked(id) {
return this._get('/wallet/' + id + '/coin/locked');
};
/**
* Lock wallet.
* @param {String} id
* @returns {Promise}
*/
HTTPClient.prototype.lock = function lock(id) {
return this._post('/wallet/' + id + '/lock', {});
};
/**
* Unlock wallet.
* @param {String} id
* @param {String} passphrase
* @param {Number} timeout
* @returns {Promise}
*/
HTTPClient.prototype.unlock = function unlock(id, passphrase, timeout) {
var options = { passphrase: passphrase, timeout: timeout };
return this._post('/wallet/' + id + '/unlock', options);
};
/**
* Get wallet accounts.
* @param {WalletID} id

View File

@ -8,17 +8,21 @@
'use strict';
/* jshint -W069 */
/* jshint noyield: true */
var EventEmitter = require('events').EventEmitter;
var assert = require('assert');
var constants = require('../protocol/constants');
var HTTPBase = require('./base');
var utils = require('../utils/utils');
var co = require('../utils/co');
var Address = require('../primitives/address');
var TX = require('../primitives/tx');
var KeyRing = require('../primitives/keyring');
var Outpoint = require('../primitives/outpoint');
var HD = require('../hd/hd');
var Script = require('../script/script');
var crypto = require('../crypto/crypto');
var assert = require('assert');
var con = co.con;
var RPC;
@ -176,8 +180,8 @@ HTTPServer.prototype._init = function _init() {
var i, params, options, output, address;
if (req.method === 'POST' && req.pathname === '/') {
assert(typeof req.body.method === 'string', 'Method must be a string.');
assert(Array.isArray(req.body.params), 'Params must be an array.');
enforce(typeof req.body.method === 'string', 'Method must be a string.');
enforce(Array.isArray(req.body.params), 'Params must be an array.');
req.options = {};
return next();
}
@ -193,15 +197,15 @@ HTTPServer.prototype._init = function _init() {
this.logger.debug(params);
if (params.id) {
assert(typeof params.id === 'string', 'ID must be a string.');
enforce(typeof params.id === 'string', 'ID must be a string.');
options.id = params.id;
}
if (params.hash) {
assert(typeof params.hash === 'string', 'Hash must be a string.');
enforce(typeof params.hash === 'string', 'Hash must be a string.');
if (params.hash.length !== 64) {
options.height = Number(params.hash);
assert(utils.isUInt32(options.height), 'Height must be a number.');
enforce(utils.isUInt32(options.height), 'Height must be a number.');
} else {
options.hash = utils.revHex(params.hash);
}
@ -209,37 +213,37 @@ HTTPServer.prototype._init = function _init() {
if (params.index != null) {
options.index = Number(params.index);
assert(utils.isUInt32(options.index), 'Index must be a number.');
enforce(utils.isUInt32(options.index), 'Index must be a number.');
}
if (params.height != null) {
options.height = Number(params.height);
assert(utils.isUInt32(options.height), 'Height must be a number.');
enforce(utils.isUInt32(options.height), 'Height must be a number.');
}
if (params.start != null) {
options.start = Number(params.start);
assert(utils.isUInt32(options.start), 'Start must be a number.');
enforce(utils.isUInt32(options.start), 'Start must be a number.');
}
if (params.end != null) {
options.end = Number(params.end);
assert(utils.isUInt32(options.end), 'End must be a number.');
enforce(utils.isUInt32(options.end), 'End must be a number.');
}
if (params.limit != null) {
options.limit = Number(params.limit);
assert(utils.isUInt32(options.limit), 'Limit must be a number.');
enforce(utils.isUInt32(options.limit), 'Limit must be a number.');
}
if (params.age != null) {
options.age = Number(params.age);
assert(utils.isUInt32(options.age), 'Age must be a number.');
enforce(utils.isUInt32(options.age), 'Age must be a number.');
}
if (params.confirmations != null) {
options.confirmations = Number(params.confirmations);
assert(utils.isNumber(options.confirmations),
enforce(utils.isNumber(options.confirmations),
'Confirmations must be a number.');
}
@ -257,53 +261,63 @@ HTTPServer.prototype._init = function _init() {
if (params.m != null) {
options.m = Number(params.m);
assert(utils.isUInt32(options.m), 'm must be a number.');
enforce(utils.isUInt32(options.m), 'm must be a number.');
}
if (params.n != null) {
options.n = Number(params.n);
assert(utils.isUInt32(options.n), 'n must be a number.');
enforce(utils.isUInt32(options.n), 'n must be a number.');
}
if (params.blocks != null) {
options.blocks = Number(params.blocks);
assert(utils.isUInt32(options.blocks), 'Blocks must be a number.');
enforce(utils.isUInt32(options.blocks), 'Blocks must be a number.');
}
if (params.subtractFee != null) {
if (typeof params.subtractFee === 'number') {
options.subtractFee = params.subtractFee;
assert(utils.isUInt32(options.subtractFee), 'subtractFee must be a number.');
enforce(utils.isUInt32(options.subtractFee), 'subtractFee must be a number.');
} else {
options.subtractFee = params.subtractFee;
assert(typeof options.subtractFee === 'boolean', 'subtractFee must be a boolean.');
enforce(typeof options.subtractFee === 'boolean', 'subtractFee must be a boolean.');
}
}
if (params.watchOnly != null) {
assert(typeof params.watchOnly === 'boolean', 'watchOnly must be a boolean.');
enforce(typeof params.watchOnly === 'boolean', 'watchOnly must be a boolean.');
options.watchOnly = params.watchOnly;
}
if (params.accountKey) {
assert(typeof params.accountKey === 'string', 'accountKey must be a string.');
options.accountKey = params.accountKey;
enforce(typeof params.accountKey === 'string', 'accountKey must be a string.');
options.accountKey = HD.fromExtended(params.accountKey);
}
if (params.timeout != null) {
options.timeout = Number(params.timeout);
enforce(utils.isNumber(options.timeout), 'Timeout must be a number.');
}
if (params.witness != null) {
enforce(typeof params.witness === 'boolean', 'witness must be a boolean.');
options.witness = params.witness;
}
if (params.outputs) {
assert(Array.isArray(params.outputs), 'Outputs must be an array.');
enforce(Array.isArray(params.outputs), 'Outputs must be an array.');
options.outputs = [];
for (i = 0; i < params.outputs.length; i++) {
output = params.outputs[i];
assert(output && typeof output === 'object', 'Output must be an object.');
enforce(output && typeof output === 'object', 'Output must be an object.');
if (output.address)
assert(typeof output.address === 'string', 'Address must be a string.');
enforce(typeof output.address === 'string', 'Address must be a string.');
else if (output.script)
assert(typeof output.script === 'string', 'Script must be a string.');
enforce(typeof output.script === 'string', 'Script must be a string.');
else
assert(false, 'No address or script present.');
enforce(false, 'No address or script present.');
options.outputs.push({
address: output.address
@ -322,11 +336,11 @@ HTTPServer.prototype._init = function _init() {
options.address = [];
for (i = 0; i < params.address.length; i++) {
address = params.address[i];
assert(typeof address === 'string', 'Address must be a string.');
enforce(typeof address === 'string', 'Address must be a string.');
address = Address.fromBase58(address);
}
} else {
assert(typeof params.address === 'string', 'Address must be a string.');
enforce(typeof params.address === 'string', 'Address must be a string.');
options.address = Address.fromBase58(params.address);
}
}
@ -335,7 +349,7 @@ HTTPServer.prototype._init = function _init() {
if (typeof params.tx === 'object') {
options.tx = TX.fromJSON(params.tx);
} else {
assert(typeof params.tx === 'string', 'TX must be a hex string.');
enforce(typeof params.tx === 'string', 'TX must be a hex string.');
options.tx = TX.fromRaw(params.tx, 'hex');
}
}
@ -343,43 +357,59 @@ HTTPServer.prototype._init = function _init() {
if (params.account != null) {
if (typeof params.account === 'number') {
options.account = params.account;
assert(utils.isUInt32(options.account), 'Account must be a number.');
enforce(utils.isUInt32(options.account), 'Account must be a number.');
} else {
assert(typeof params.account === 'string', 'Account must be a string.');
enforce(typeof params.account === 'string', 'Account must be a string.');
options.account = params.account;
}
}
if (params.type) {
assert(typeof params.type === 'string', 'Type must be a string.');
enforce(typeof params.type === 'string', 'Type must be a string.');
options.type = params.type;
}
if (params.name) {
assert(typeof params.name === 'string', 'Name must be a string.');
enforce(typeof params.name === 'string', 'Name must be a string.');
options.name = params.name;
}
if (params.key) {
assert(typeof params.key === 'string', 'Key must be a string.');
options.key = params.key;
if (params.privateKey) {
enforce(typeof params.privateKey === 'string', 'Key must be a string.');
options.privateKey = KeyRing.fromSecret(params.privateKey);
}
if (params.publicKey) {
enforce(typeof params.publicKey === 'string', 'Key must be a string.');
options.publicKey = new Buffer(params.publicKey, 'hex');
options.publicKey = KeyRing.fromKey(options.publicKey, this.network);
}
if (params.master) {
enforce(typeof params.key === 'string', 'Key must be a string.');
options.master = HD.fromExtended(params.master);
}
if (params.mnemonic) {
enforce(typeof params.mnemonic === 'string', 'Key must be a string.');
options.master = HD.fromMnemonic(params.mnemonic, this.network);
}
if (params.old) {
assert(typeof params.old === 'string', 'Passphrase must be a string.');
assert(params.old.length > 0, 'Passphrase must be a string.');
enforce(typeof params.old === 'string', 'Passphrase must be a string.');
enforce(params.old.length > 0, 'Passphrase must be a string.');
options.old = params.old;
}
if (params.passphrase) {
assert(typeof params.passphrase === 'string', 'Passphrase must be a string.');
assert(params.passphrase.length > 0, 'Passphrase must be a string.');
enforce(typeof params.passphrase === 'string', 'Passphrase must be a string.');
enforce(params.passphrase.length > 0, 'Passphrase must be a string.');
options.passphrase = params.passphrase;
}
if (params.token) {
assert(utils.isHex(params.token), 'Wallet token must be a hex string.');
assert(params.token.length === 64, 'Wallet token must be 32 bytes.');
enforce(utils.isHex(params.token), 'Wallet token must be a hex string.');
enforce(params.token.length === 64, 'Wallet token must be 32 bytes.');
options.token = new Buffer(params.token, 'hex');
}
@ -425,7 +455,9 @@ HTTPServer.prototype._init = function _init() {
}
req.wallet = wallet;
this.logger.info('Successful auth for %s.', req.options.id);
next();
}));
@ -489,7 +521,12 @@ HTTPServer.prototype._init = function _init() {
// UTXO by address
this.get('/coin/address/:address', con(function* (req, res, send, next) {
var coins = yield this.node.getCoinsByAddress(req.options.address);
var coins;
enforce(req.options.address, 'Address is required.');
coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
@ -497,7 +534,12 @@ HTTPServer.prototype._init = function _init() {
// UTXO by id
this.get('/coin/:hash/:index', con(function* (req, res, send, next) {
var coin = yield this.node.getCoin(req.options.hash, req.options.index);
var coin;
enforce(req.options.hash, 'Hash is required.');
enforce(req.options.index != null, 'Index is required.');
coin = yield this.node.getCoin(req.options.hash, req.options.index);
if (!coin)
return send(404);
@ -507,7 +549,12 @@ HTTPServer.prototype._init = function _init() {
// Bulk read UTXOs
this.post('/coin/address', con(function* (req, res, send, next) {
var coins = yield this.node.getCoinsByAddress(req.options.address);
var coins;
enforce(req.options.address, 'Address is required.');
coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
@ -515,7 +562,11 @@ HTTPServer.prototype._init = function _init() {
// TX by hash
this.get('/tx/:hash', con(function* (req, res, send, next) {
var tx = yield this.node.getTX(req.options.hash);
var tx;
enforce(req.options.hash, 'Hash is required.');
tx = yield this.node.getTX(req.options.hash);
if (!tx)
return send(404);
@ -527,8 +578,11 @@ HTTPServer.prototype._init = function _init() {
// TX by address
this.get('/tx/address/:address', con(function* (req, res, send, next) {
var txs = yield this.node.getTXByAddress(req.options.address);
var i, tx;
var i, txs, tx;
enforce(req.options.address, 'Address is required.');
txs = yield this.node.getTXByAddress(req.options.address);
for (i = 0; i < txs.length; i++) {
tx = txs[i];
@ -542,8 +596,11 @@ HTTPServer.prototype._init = function _init() {
// Bulk read TXs
this.post('/tx/address', con(function* (req, res, send, next) {
var txs = yield this.node.getTXByAddress(req.options.address);
var i, tx;
var i, txs, tx;
enforce(req.options.address, 'Address is required.');
txs = yield this.node.getTXByAddress(req.options.address);
for (i = 0; i < txs.length; i++) {
tx = txs[i];
@ -558,7 +615,11 @@ HTTPServer.prototype._init = function _init() {
// Block by hash/height
this.get('/block/:hash', con(function* (req, res, send, next) {
var hash = req.options.hash || req.options.height;
var block = yield this.node.getFullBlock(hash);
var block;
enforce(hash != null, 'Hash or height required.');
block = yield this.node.getFullBlock(hash);
if (!block)
return send(404);
@ -587,6 +648,7 @@ HTTPServer.prototype._init = function _init() {
// Broadcast TX
this.post('/broadcast', con(function* (req, res, send, next) {
enforce(req.options.tx, 'TX is required.');
yield this.node.sendTX(req.options.tx);
send(200, { success: true });
}));
@ -603,6 +665,15 @@ HTTPServer.prototype._init = function _init() {
send(200, { rate: utils.btc(fee) });
});
// Rescan
this.post('/rescan', con(function* (req, res, send, next) {
var options = req.options;
var height = options.hash || options.height;
enforce(height != null, 'Hash or height is required.');
send(200, { success: true });
yield this.node.scan(height);
}));
// Get wallet
this.get('/wallet/:id', function(req, res, send, next) {
send(200, req.wallet.toJSON());
@ -622,7 +693,13 @@ HTTPServer.prototype._init = function _init() {
// Get account
this.get('/wallet/:id/account/:account', con(function* (req, res, send, next) {
var account = yield req.wallet.getAccount(req.options.account);
var options = req.options;
var acct = options.name || options.account;
var account;
enforce(acct != null, 'Account is required.');
account = yield req.wallet.getAccount(acct);
if (!account)
return send(404);
@ -630,9 +707,17 @@ HTTPServer.prototype._init = function _init() {
send(200, account.toJSON());
}));
// Create/get account
// Create account
this.post('/wallet/:id/account/:account?', con(function* (req, res, send, next) {
var account = yield req.wallet.createAccount(req.options);
var options = req.options;
var account;
if (typeof options.account === 'string') {
options.name = options.account;
options.account = null;
}
account = yield req.wallet.createAccount(req.options);
if (!account)
return send(404);
@ -645,10 +730,49 @@ HTTPServer.prototype._init = function _init() {
var options = req.options;
var old = options.old;
var new_ = options.passphrase;
enforce(old || new_, 'Passphrase is required.');
yield req.wallet.setPassphrase(old, new_);
send(200, { success: true });
}));
// Unlock wallet
this.post('/wallet/:id/unlock', con(function* (req, res, send, next) {
var options = req.options;
var passphrase = options.passphrase;
var timeout = options.timeout;
enforce(passphrase, 'Passphrase is required.');
yield req.wallet.unlock(passphrase, timeout);
send(200, { success: true });
}));
// Lock wallet
this.post('/wallet/:id/lock', con(function* (req, res, send, next) {
yield req.wallet.lock();
send(200, { success: true });
}));
// Import key
this.post('/wallet/:id/import', con(function* (req, res, send, next) {
var options = req.options;
var acct = req.options.name || req.options.account;
var key = options.privateKey || options.publicKey;
if (key) {
yield req.wallet.importKey(acct, key);
send(200, { success: true });
return;
}
if (options.address) {
enforce(options.address instanceof Address, 'Address is required.');
yield req.wallet.importAddress(acct, options.address);
send(200, { success: true });
return;
}
enforce(false, 'Key or address is required.');
}));
// Generate new token
this.post('/wallet/:id/retoken', con(function* (req, res, send, next) {
var options = req.options;
@ -675,6 +799,7 @@ HTTPServer.prototype._init = function _init() {
this.post('/wallet/:id/sign', con(function* (req, res, send, next) {
var options = req.options;
var tx = req.options.tx;
enforce(tx, 'TX is required.');
yield req.wallet.sign(tx, options);
send(200, tx.toJSON());
}));
@ -682,59 +807,70 @@ HTTPServer.prototype._init = function _init() {
// Fill TX
this.post('/wallet/:id/fill', con(function* (req, res, send, next) {
var tx = req.options.tx;
enforce(tx, 'TX is required.');
yield req.wallet.fillHistory(tx);
send(200, tx.toJSON());
}));
// Zap Wallet TXs
this.post('/wallet/:id/zap', con(function* (req, res, send, next) {
var account = req.options.account;
var age = req.options.age;
yield req.wallet.zap(account, age);
var options = req.options;
var acct = options.name || options.account;
var age = options.age;
enforce(age, 'Age is required.');
yield req.wallet.zap(acct, age);
send(200, { success: true });
}));
// Abandon Wallet TX
this.del('/wallet/:id/tx/:hash', con(function* (req, res, send, next) {
var hash = req.options.hash;
enforce(hash, 'Hash is required.');
yield req.wallet.abandon(hash);
send(200, { success: true });
}));
// Add key
this.put('/wallet/:id/key', con(function* (req, res, send, next) {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.addKey(account, key);
var options = req.options;
var acct = options.name || options.account;
var key = options.accountKey;
enforce(key, 'Key is required.');
yield req.wallet.addKey(acct, key);
send(200, { success: true });
}));
// Remove key
this.del('/wallet/:id/key', con(function* (req, res, send, next) {
var account = req.options.account;
var key = req.options.key;
yield req.wallet.removeKey(account, key);
var options = req.options;
var acct = options.name || options.account;
var key = options.accountKey;
enforce(key, 'Key is required.');
yield req.wallet.removeKey(acct, key);
send(200, { success: true });
}));
// Create address
this.post('/wallet/:id/address', con(function* (req, res, send, next) {
var account = req.options.account;
var address = yield req.wallet.createReceive(account);
var options = req.options;
var acct = options.name || options.account;
var address = yield req.wallet.createReceive(acct);
send(200, address.toJSON());
}));
// Create nested address
this.post('/wallet/:id/nested', con(function* (req, res, send, next) {
var account = req.options.account;
var address = yield req.wallet.createNested(account);
var options = req.options;
var acct = options.name || options.account;
var address = yield req.wallet.createNested(acct);
send(200, address.toJSON());
}));
// Wallet Balance
this.get('/wallet/:id/balance', con(function* (req, res, send, next) {
var account = req.options.account;
var balance = yield req.wallet.getBalance(account);
var options = req.options;
var acct = options.name || options.account;
var balance = yield req.wallet.getBalance(acct);
if (!balance)
return send(404);
@ -744,18 +880,61 @@ HTTPServer.prototype._init = function _init() {
// Wallet UTXOs
this.get('/wallet/:id/coin', con(function* (req, res, send, next) {
var account = req.options.account;
var coins = yield req.wallet.getCoins(account);
var options = req.options;
var acct = options.name || options.account;
var coins = yield req.wallet.getCoins(acct);
sortCoins(coins);
send(200, coins.map(function(coin) {
return coin.toJSON();
}));
}));
// Locked coins
this.get('/wallet/:id/coin/locked', con(function* (req, res, send, next) {
var locked = this.wallet.getLocked();
send(200, locked.map(function(outpoint) {
return outpoint.toJSON();
}));
}));
// Lock coin
this.put('/wallet/:id/coin/locked', con(function* (req, res, send, next) {
var options = req.options.hash;
var outpoint;
enforce(options.hash, 'Hash is required.');
enforce(options.index != null, 'Index is required.');
outpoint = new Outpoint(options.hash, options.index);
this.wallet.lockCoin(outpoint);
}));
// Unlock coin
this.del('/wallet/:id/coin/locked', con(function* (req, res, send, next) {
var options = req.options.hash;
var outpoint;
enforce(options.hash, 'Hash is required.');
enforce(options.index != null, 'Index is required.');
outpoint = new Outpoint(options.hash, options.index);
this.wallet.unlockCoin(outpoint);
}));
// Wallet Coin
this.get('/wallet/:id/coin/:hash/:index', con(function* (req, res, send, next) {
var hash = req.options.hash;
var index = req.options.index;
var coin = yield req.wallet.getCoin(hash, index);
var coin;
enforce(hash, 'Hash is required.');
enforce(index != null, 'Index is required.');
coin = yield req.wallet.getCoin(hash, index);
if (!coin)
return send(404);
@ -765,9 +944,15 @@ HTTPServer.prototype._init = function _init() {
// Wallet TXs
this.get('/wallet/:id/tx/history', con(function* (req, res, send, next) {
var account = req.options.account;
var txs = yield req.wallet.getHistory(account);
var details = yield req.wallet.toDetails(txs);
var options = req.options;
var acct = options.name || options.account;
var txs = yield req.wallet.getHistory(acct);
var details;
sortTX(txs);
details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
@ -775,9 +960,15 @@ HTTPServer.prototype._init = function _init() {
// Wallet Pending TXs
this.get('/wallet/:id/tx/unconfirmed', con(function* (req, res, send, next) {
var account = req.options.account;
var txs = yield req.wallet.getUnconfirmed(account);
var details = yield req.wallet.toDetails(txs);
var options = req.options;
var acct = options.name || options.account;
var txs = yield req.wallet.getUnconfirmed(acct);
var details;
sortTX(txs);
details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
}));
@ -785,9 +976,9 @@ HTTPServer.prototype._init = function _init() {
// Wallet TXs within time range
this.get('/wallet/:id/tx/range', con(function* (req, res, send, next) {
var account = req.options.account;
var options = req.options;
var txs = yield req.wallet.getRange(account, options);
var acct = options.name || options.account;
var txs = yield req.wallet.getRange(acct, options);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
@ -796,9 +987,10 @@ HTTPServer.prototype._init = function _init() {
// Last Wallet TXs
this.get('/wallet/:id/tx/last', con(function* (req, res, send, next) {
var account = req.options.account;
var limit = req.options.limit;
var txs = yield req.wallet.getLast(account, limit);
var options = req.options;
var acct = options.name || options.account;
var limit = options.limit;
var txs = yield req.wallet.getLast(acct, limit);
var details = yield req.wallet.toDetails(txs);
send(200, details.map(function(tx) {
return tx.toJSON();
@ -808,13 +1000,17 @@ HTTPServer.prototype._init = function _init() {
// Wallet TX
this.get('/wallet/:id/tx/:hash', con(function* (req, res, send, next) {
var hash = req.options.hash;
var tx = yield req.wallet.getTX(hash);
var details;
var tx, details;
enforce(hash, 'Hash is required.');
tx = yield req.wallet.getTX(hash);
if (!tx)
return send(404);
details = yield req.wallet.toDetails(tx);
send(200, details.toJSON());
}));
@ -1373,6 +1569,30 @@ function softMerge(a, b, soft) {
}
}
function enforce(value, msg) {
var err;
if (!value) {
err = new Error(msg);
err.statusCode = 400;
throw err;
}
}
function sortTX(txs) {
return txs.sort(function(a, b) {
return a.ps - b.ps;
});
}
function sortCoins(coins) {
return coins.sort(function(a, b) {
a = a.height === -1 ? 0x7fffffff : a.height;
b = b.height === -1 ? 0x7fffffff : b.height;
return a - b;
});
}
/*
* Expose
*/

View File

@ -309,6 +309,90 @@ HTTPWallet.prototype.retoken = co(function* retoken(passphrase) {
return token;
});
/**
* Import private key.
* @param {Number|String} account
* @param {String} key
* @returns {Promise}
*/
HTTPWallet.prototype.importPrivate = function importPrivate(id, account, key) {
return this.client.importPrivate(this.id, account, key);
};
/**
* Import public key.
* @param {Number|String} account
* @param {String} key
* @returns {Promise}
*/
HTTPWallet.prototype.importPublic = function importPublic(id, account, key) {
return this.client.importPublic(this.id, account, key);
};
/**
* Import address.
* @param {Number|String} account
* @param {String} address
* @returns {Promise}
*/
HTTPWallet.prototype.importAddress = function importAddress(id, account, address) {
return this.client.importAddress(this.id, account, address);
};
/**
* Lock a coin.
* @param {String} hash
* @param {Number} index
* @returns {Promise}
*/
HTTPWallet.prototype.lockCoin = function lockCoin(id, hash, index) {
return this.client.lockCoin(this.id, hash, index);
};
/**
* Unlock a coin.
* @param {String} hash
* @param {Number} index
* @returns {Promise}
*/
HTTPWallet.prototype.unlockCoin = function unlockCoin(id, hash, index) {
return this.client.unlockCoin(this.id, hash, index);
};
/**
* Get locked coins.
* @returns {Promise}
*/
HTTPWallet.prototype.getLocked = function getLocked(id) {
return this.client.getLocked(this.id);
};
/**
* Lock wallet.
* @returns {Promise}
*/
HTTPWallet.prototype.lock = function lock(id) {
return this.client.lock(this.id);
};
/**
* Unlock wallet.
* @param {String} passphrase
* @param {Number} timeout
* @returns {Promise}
*/
HTTPWallet.prototype.unlock = function unlock(id, passphrase, timeout) {
return this.client.unlock(this.id, passphrase, timeout);
};
/*
* Expose
*/