rpc: better error codes.

This commit is contained in:
Christopher Jeffrey 2017-03-15 06:09:52 -07:00
parent 5ee8a9b306
commit 3ae417795f
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 551 additions and 366 deletions

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,7 @@ RPCBase.prototype.call = co(function* call(body, query) {
var cmds = body;
var out = [];
var array = true;
var i, cmd, result;
var i, cmd, result, code;
if (!Array.isArray(cmds)) {
cmds = [cmds];
@ -116,19 +116,56 @@ RPCBase.prototype.call = co(function* call(body, query) {
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
assert(cmd && typeof cmd === 'object', 'Command must be an object.');
assert(typeof cmd.method === 'string', 'Method must be a string.');
if (!cmd || typeof cmd !== 'object') {
out.push({
result: null,
error: {
message: 'Invalid request.',
code: RPCBase.errors.INVALID_REQUEST
},
id: null
});
continue;
}
if (cmd.id && typeof cmd.id === 'object') {
out.push({
result: null,
error: {
message: 'Invalid ID.',
code: RPCBase.errors.INVALID_REQUEST
},
id: null
});
continue;
}
if (typeof cmd.method !== 'string') {
out.push({
result: null,
error: {
message: 'Method not found.',
code: RPCBase.errors.METHOD_NOT_FOUND
},
id: cmd.id
});
continue;
}
if (!cmd.params)
cmd.params = [];
assert(Array.isArray(cmd.params), 'Params must be an array.');
assert(!cmd.id || typeof cmd.id !== 'object', 'Invalid ID.');
}
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
if (!Array.isArray(cmd.params)) {
out.push({
result: null,
error: {
message: 'Invalid params.',
code: RPCBase.errors.INVALID_PARAMS
},
id: cmd.id
});
continue;
}
if (cmd.method !== 'getwork'
&& cmd.method !== 'getblocktemplate'
@ -148,25 +185,25 @@ RPCBase.prototype.call = co(function* call(body, query) {
try {
result = yield this.execute(cmd);
} catch (err) {
if (err.type === 'RPCError') {
out.push({
result: null,
error: {
message: err.message,
code: -1
},
id: cmd.id
});
continue;
switch (err.type) {
case 'RPCError':
code = err.code;
break;
case 'ValidationError':
code = RPCBase.errors.TYPE_ERROR;
break;
default:
code = RPCBase.errors.INTERNAL_ERROR;
this.logger.error('RPC internal error.');
this.logger.error(err);
break;
}
this.logger.error(err);
out.push({
result: null,
error: {
message: err.message,
code: 1
code: code
},
id: cmd.id
});
@ -254,15 +291,18 @@ RPCBase.prototype.attach = function attach(rpc) {
* @ignore
*/
function RPCError(msg, code) {
function RPCError(code, msg) {
Error.call(this);
if (Error.captureStackTrace)
Error.captureStackTrace(this, RPCError);
assert(typeof code === 'number');
assert(typeof msg === 'string');
this.type = 'RPCError';
this.message = msg;
this.code = code != null ? code : -1;
this.code = code;
}
util.inherits(RPCError, Error);

View File

@ -87,7 +87,7 @@ Validator.prototype.get = function get(key, fallback) {
map = this.data[i];
if (!map || typeof map !== 'object')
throw new Error('Data is not an object.');
throw new ValidationError('Data is not an object.');
value = map[key];
@ -115,7 +115,7 @@ Validator.prototype.str = function str(key, fallback) {
return fallback;
if (typeof value !== 'string')
throw new Error(fmt(key) + ' must be a string.');
throw new ValidationError(fmt(key) + ' must be a string.');
return value;
};
@ -138,17 +138,17 @@ Validator.prototype.num = function num(key, fallback) {
if (typeof value !== 'string') {
if (typeof value !== 'number')
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
return value;
}
if (!/^\d+$/.test(value))
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
value = parseInt(value, 10);
if (!isFinite(value))
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
return value;
};
@ -170,7 +170,7 @@ Validator.prototype.u32 = function u32(key, fallback) {
return fallback;
if (value % 1 !== 0 || value < 0 || value > 0xffffffff)
throw new Error(fmt(key) + ' must be a uint32.');
throw new ValidationError(fmt(key) + ' must be a uint32.');
return value;
};
@ -192,7 +192,7 @@ Validator.prototype.u64 = function u64(key, fallback) {
return fallback;
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
throw new Error(fmt(key) + ' must be a uint64.');
throw new ValidationError(fmt(key) + ' must be a uint64.');
return value;
};
@ -214,7 +214,7 @@ Validator.prototype.i32 = function i32(key, fallback) {
return fallback;
if (value % 1 !== 0 || Math.abs(value) > 0x7fffffff)
throw new Error(fmt(key) + ' must be an int32.');
throw new ValidationError(fmt(key) + ' must be an int32.');
return value;
};
@ -236,7 +236,7 @@ Validator.prototype.i64 = function i64(key, fallback) {
return fallback;
if (value % 1 !== 0 || Math.abs(value) > 0x1fffffffffffff)
throw new Error(fmt(key) + ' must be an int64.');
throw new ValidationError(fmt(key) + ' must be an int64.');
return value;
};
@ -259,22 +259,22 @@ Validator.prototype.amt = function amt(key, fallback) {
if (typeof value !== 'string') {
if (typeof value !== 'number')
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
return value;
}
if (!/^\d+(\.\d{0,8})?$/.test(value))
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
value = parseFloat(value);
if (!isFinite(value))
throw new Error(fmt(key) + ' must be a number.');
throw new ValidationError(fmt(key) + ' must be a number.');
value *= 1e8;
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
throw new Error(fmt(key) + ' must be a uint64.');
throw new ValidationError(fmt(key) + ' must be a uint64.');
return value;
};
@ -298,7 +298,7 @@ Validator.prototype.btc = function btc(key, fallback) {
value *= 1e8;
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
throw new Error(fmt(key) + ' must be a uint64.');
throw new ValidationError(fmt(key) + ' must be a uint64.');
return value;
};
@ -323,17 +323,17 @@ Validator.prototype.hash = function hash(key, fallback) {
if (typeof value !== 'string') {
if (!Buffer.isBuffer(value))
throw new Error(fmt(key) + ' must be a hash.');
throw new ValidationError(fmt(key) + ' must be a hash.');
if (value.length !== 32)
throw new Error(fmt(key) + ' must be a hash.');
throw new ValidationError(fmt(key) + ' must be a hash.');
return value.toString('hex');
}
if (value.length !== 64)
throw new Error(fmt(key) + ' must be a hex string.');
throw new ValidationError(fmt(key) + ' must be a hex string.');
if (!/^[0-9a-f]+$/i.test(value))
throw new Error(fmt(key) + ' must be a hex string.');
throw new ValidationError(fmt(key) + ' must be a hex string.');
for (i = 0; i < value.length; i += 2)
out = value.slice(i, i + 2) + out;
@ -379,7 +379,7 @@ Validator.prototype.numstr = function numstr(key, fallback) {
if (typeof value !== 'string') {
if (typeof value !== 'number')
throw new Error(fmt(key) + ' must be a number or string.');
throw new ValidationError(fmt(key) + ' must be a number or string.');
return value;
}
@ -409,7 +409,7 @@ Validator.prototype.bool = function bool(key, fallback) {
if (typeof value !== 'string') {
if (typeof value !== 'boolean')
throw new Error(fmt(key) + ' must be a boolean.');
throw new ValidationError(fmt(key) + ' must be a boolean.');
return value;
}
@ -419,7 +419,7 @@ Validator.prototype.bool = function bool(key, fallback) {
if (value === 'false' || value === '0')
return false;
throw new Error(fmt(key) + ' must be a boolean.');
throw new ValidationError(fmt(key) + ' must be a boolean.');
};
/**
@ -445,14 +445,14 @@ Validator.prototype.buf = function buf(key, fallback, enc) {
if (typeof value !== 'string') {
if (!Buffer.isBuffer(value))
throw new Error(fmt(key) + ' must be a buffer.');
throw new ValidationError(fmt(key) + ' must be a buffer.');
return value;
}
data = new Buffer(value, enc);
if (data.length !== Buffer.byteLength(value, enc))
throw new Error(fmt(key) + ' must be a ' + enc + ' string.');
throw new ValidationError(fmt(key) + ' must be a ' + enc + ' string.');
return data;
};
@ -476,7 +476,7 @@ Validator.prototype.array = function array(key, fallback) {
if (typeof value !== 'string') {
if (!Array.isArray(value))
throw new Error(fmt(key) + ' must be a list/array.');
throw new ValidationError(fmt(key) + ' must be a list/array.');
return value;
}
@ -512,7 +512,7 @@ Validator.prototype.obj = function obj(key, fallback) {
return fallback;
if (!value || typeof value !== 'object')
throw new Error(fmt(key) + ' must be an object.');
throw new ValidationError(fmt(key) + ' must be an object.');
return value;
};
@ -534,7 +534,7 @@ Validator.prototype.func = function func(key, fallback) {
return fallback;
if (typeof value !== 'function')
throw new Error(fmt(key) + ' must be a function.');
throw new ValidationError(fmt(key) + ' must be a function.');
return value;
};
@ -549,8 +549,31 @@ function fmt(key) {
return key;
}
function inherits(obj, from) {
var f = function() {};
f.prototype = from.prototype;
obj.prototype = new f;
obj.prototype.constructor = obj;
}
function ValidationError(msg) {
Error.call(this);
if (Error.captureStackTrace)
Error.captureStackTrace(this, ValidationError);
this.type = 'ValidationError';
this.message = msg;
}
inherits(ValidationError, Error);
/*
* Expose
*/
module.exports = Validator;
exports = Validator;
exports.Validator = Validator;
exports.Error = ValidationError;
module.exports = exports;

View File

@ -26,6 +26,7 @@ var pkg = require('../pkg');
var Validator = require('../utils/validator');
var common = require('./common');
var RPCError = RPCBase.RPCError;
var errs = RPCBase.errors;
var MAGIC_STRING = RPCBase.MAGIC_STRING;
/**
@ -126,7 +127,7 @@ RPC.prototype.help = co(function* _help(args, help) {
RPC.prototype.stop = co(function* stop(args, help) {
if (help || args.length !== 0)
throw new RPCError('stop');
throw new RPCError(errs.MISC_ERROR, 'stop');
this.wdb.close();
@ -141,16 +142,20 @@ RPC.prototype.fundRawTransaction = co(function* fundRawTransaction(args, help) {
var rate = this.feeRate;
var change, tx;
if (help || args.length < 1 || args.length > 2)
throw new RPCError('fundrawtransaction "hexstring" ( options )');
if (help || args.length < 1 || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'fundrawtransaction "hexstring" ( options )');
}
if (!data)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.');
tx = MTX.fromRaw(data);
tx = fromRaw(MTX, data);
if (tx.outputs.length === 0)
throw new RPCError('TX must have at least one output.');
if (tx.outputs.length === 0) {
throw new RPCError(errs.INVALID_PARAMETER,
'TX must have at least one output.');
}
if (options) {
valid = new Validator([options]);
@ -158,7 +163,7 @@ RPC.prototype.fundRawTransaction = co(function* fundRawTransaction(args, help) {
rate = valid.btc('feeRate');
if (change)
change = Address.fromBase58(change, this.network);
change = parseAddress(change, this.network);
}
options = {
@ -166,7 +171,11 @@ RPC.prototype.fundRawTransaction = co(function* fundRawTransaction(args, help) {
changeAddress: change
};
yield wallet.fund(tx, options);
try {
yield wallet.fund(tx, options);
} catch (e) {
throw new RPCError(errs.WALLET_INSUFFICIENT_FUNDS, e.message);
}
return {
hex: tx.toRaw().toString('hex'),
@ -185,7 +194,7 @@ RPC.prototype.resendWalletTransactions = co(function* resendWalletTransactions(a
var i, tx, txs;
if (help || args.length !== 0)
throw new RPCError('resendwallettransactions');
throw new RPCError(errs.MISC_ERROR, 'resendwallettransactions');
txs = yield wallet.resend();
@ -199,7 +208,7 @@ RPC.prototype.resendWalletTransactions = co(function* resendWalletTransactions(a
RPC.prototype.addMultisigAddress = co(function* addMultisigAddress(args, help) {
if (help || args.length < 2 || args.length > 3) {
throw new RPCError('addmultisigaddress'
throw new RPCError(errs.MISC_ERROR, 'addmultisigaddress'
+ ' nrequired ["key",...] ( "account" )');
}
@ -209,7 +218,7 @@ RPC.prototype.addMultisigAddress = co(function* addMultisigAddress(args, help) {
RPC.prototype.addWitnessAddress = co(function* addWitnessAddress(args, help) {
if (help || args.length < 1 || args.length > 1)
throw new RPCError('addwitnessaddress "address"');
throw new RPCError(errs.MISC_ERROR, 'addwitnessaddress "address"');
// Unlikely to be implemented.
throw new Error('Not implemented.');
@ -220,7 +229,7 @@ RPC.prototype.backupWallet = co(function* backupWallet(args, help) {
var dest = valid.str(0);
if (help || args.length !== 1 || !dest)
throw new RPCError('backupwallet "destination"');
throw new RPCError(errs.MISC_ERROR, 'backupwallet "destination"');
yield this.wdb.backup(dest);
@ -235,15 +244,15 @@ RPC.prototype.dumpPrivKey = co(function* dumpPrivKey(args, help) {
var ring;
if (help || args.length !== 1)
throw new RPCError('dumpprivkey "bitcoinaddress"');
throw new RPCError(errs.MISC_ERROR, 'dumpprivkey "bitcoinaddress"');
if (!hash)
throw new RPCError('Invalid address.');
throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid address.');
ring = yield wallet.getPrivateKey(hash);
if (!ring)
throw new RPCError('Key not found.');
throw new RPCError(errs.MISC_ERROR, 'Key not found.');
return ring.toSecret();
});
@ -256,10 +265,10 @@ RPC.prototype.dumpWallet = co(function* dumpWallet(args, help) {
var i, tip, addr, fmt, str, out, hash, hashes, ring;
if (help || args.length !== 1)
throw new RPCError('dumpwallet "filename"');
throw new RPCError(errs.MISC_ERROR, 'dumpwallet "filename"');
if (!file)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
tip = yield this.wdb.getTip();
@ -312,15 +321,21 @@ RPC.prototype.encryptWallet = co(function* encryptWallet(args, help) {
var passphrase = valid.str(0, '');
if (!wallet.master.encrypted && (help || args.length !== 1))
throw new RPCError('encryptwallet "passphrase"');
throw new RPCError(errs.MISC_ERROR, 'encryptwallet "passphrase"');
if (wallet.master.encrypted)
throw new RPCError('Already running with an encrypted wallet');
if (wallet.master.encrypted) {
throw new RPCError(errs.WALLET_WRONG_ENC_STATE,
'Already running with an encrypted wallet.');
}
if (passphrase.length < 1)
throw new RPCError('encryptwallet "passphrase"');
throw new RPCError(errs.MISC_ERROR, 'encryptwallet "passphrase"');
yield wallet.setPassphrase(passphrase);
try {
yield wallet.setPassphrase(passphrase);
} catch (e) {
throw new RPCError(errs.WALLET_ENCRYPTION_FAILED, 'Encryption failed.');
}
return 'wallet encrypted; we do not need to stop!';
});
@ -332,7 +347,7 @@ RPC.prototype.getAccountAddress = co(function* getAccountAddress(args, help) {
var account;
if (help || args.length !== 1)
throw new RPCError('getaccountaddress "account"');
throw new RPCError(errs.MISC_ERROR, 'getaccountaddress "account"');
if (!name)
name = 'default';
@ -353,10 +368,10 @@ RPC.prototype.getAccount = co(function* getAccount(args, help) {
var path;
if (help || args.length !== 1)
throw new RPCError('getaccount "bitcoinaddress"');
throw new RPCError(errs.MISC_ERROR, 'getaccount "bitcoinaddress"');
if (!hash)
throw new RPCError('Invalid address.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid address.');
path = yield wallet.getPath(hash);
@ -373,7 +388,7 @@ RPC.prototype.getAddressesByAccount = co(function* getAddressesByAccount(args, h
var i, path, address, addrs, paths;
if (help || args.length !== 1)
throw new RPCError('getaddressesbyaccount "account"');
throw new RPCError(errs.MISC_ERROR, 'getaddressesbyaccount "account"');
if (name === '')
name = 'default';
@ -399,8 +414,10 @@ RPC.prototype.getBalance = co(function* getBalance(args, help) {
var watchOnly = valid.bool(2, false);
var value, balance;
if (help || args.length > 3)
throw new RPCError('getbalance ( "account" minconf includeWatchonly )');
if (help || args.length > 3) {
throw new RPCError(errs.MISC_ERROR,
'getbalance ( "account" minconf includeWatchonly )');
}
if (name === '')
name = 'default';
@ -428,7 +445,7 @@ RPC.prototype.getNewAddress = co(function* getNewAddress(args, help) {
var address;
if (help || args.length > 1)
throw new RPCError('getnewaddress ( "account" )');
throw new RPCError(errs.MISC_ERROR, 'getnewaddress ( "account" )');
if (name === '')
name = 'default';
@ -443,7 +460,7 @@ RPC.prototype.getRawChangeAddress = co(function* getRawChangeAddress(args, help)
var address;
if (help || args.length > 1)
throw new RPCError('getrawchangeaddress');
throw new RPCError(errs.MISC_ERROR, 'getrawchangeaddress');
address = yield wallet.createChange();
@ -461,8 +478,10 @@ RPC.prototype.getReceivedByAccount = co(function* getReceivedByAccount(args, hel
var lastConf = -1;
var i, j, path, wtx, output, conf, hash, paths, txs;
if (help || args.length < 1 || args.length > 2)
throw new RPCError('getreceivedbyaccount "account" ( minconf )');
if (help || args.length < 1 || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'getreceivedbyaccount "account" ( minconf )');
}
if (name === '')
name = 'default';
@ -508,11 +527,13 @@ RPC.prototype.getReceivedByAddress = co(function* getReceivedByAddress(args, hel
var total = 0;
var i, j, wtx, output, txs;
if (help || args.length < 1 || args.length > 2)
throw new RPCError('getreceivedbyaddress "bitcoinaddress" ( minconf )');
if (help || args.length < 1 || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'getreceivedbyaddress "bitcoinaddress" ( minconf )');
}
if (!hash)
throw new RPCError('Invalid address');
throw new RPCError(errs.TYPE_ERROR, 'Invalid address');
txs = yield wallet.getHistory();
@ -542,7 +563,7 @@ RPC.prototype._toWalletTX = co(function* _toWalletTX(wtx) {
var i, member;
if (!details)
throw new RPCError('TX not found.');
throw new RPCError(errs.WALLET_ERROR, 'TX not found.');
for (i = 0; i < details.inputs.length; i++) {
member = details.inputs[i];
@ -613,16 +634,18 @@ RPC.prototype.getTransaction = co(function* getTransaction(args, help) {
var watchOnly = valid.bool(1, false);
var wtx;
if (help || args.length < 1 || args.length > 2)
throw new RPCError('gettransaction "txid" ( includeWatchonly )');
if (help || args.length < 1 || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'gettransaction "txid" ( includeWatchonly )');
}
if (!hash)
throw new RPCError('Invalid parameter');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter');
wtx = yield wallet.getTX(hash);
if (!wtx)
throw new RPCError('TX not found.');
throw new RPCError(errs.WALLET_ERROR, 'TX not found.');
return yield this._toWalletTX(wtx, watchOnly);
});
@ -634,15 +657,15 @@ RPC.prototype.abandonTransaction = co(function* abandonTransaction(args, help) {
var result;
if (help || args.length !== 1)
throw new RPCError('abandontransaction "txid"');
throw new RPCError(errs.MISC_ERROR, 'abandontransaction "txid"');
if (!hash)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
result = yield wallet.abandon(hash);
if (!result)
throw new RPCError('Transaction not in wallet.');
throw new RPCError(errs.WALLET_ERROR, 'Transaction not in wallet.');
return null;
});
@ -652,7 +675,7 @@ RPC.prototype.getUnconfirmedBalance = co(function* getUnconfirmedBalance(args, h
var balance;
if (help || args.length > 0)
throw new RPCError('getunconfirmedbalance');
throw new RPCError(errs.MISC_ERROR, 'getunconfirmedbalance');
balance = yield wallet.getBalance();
@ -664,7 +687,7 @@ RPC.prototype.getWalletInfo = co(function* getWalletInfo(args, help) {
var balance;
if (help || args.length !== 0)
throw new RPCError('getwalletinfo');
throw new RPCError(errs.MISC_ERROR, 'getwalletinfo');
balance = yield wallet.getBalance();
@ -690,10 +713,12 @@ RPC.prototype.importPrivKey = co(function* importPrivKey(args, help) {
var rescan = valid.bool(2, false);
var key;
if (help || args.length < 1 || args.length > 3)
throw new RPCError('importprivkey "bitcoinprivkey" ( "label" rescan )');
if (help || args.length < 1 || args.length > 3) {
throw new RPCError(errs.MISC_ERROR,
'importprivkey "bitcoinprivkey" ( "label" rescan )');
}
key = KeyRing.fromSecret(secret, this.network);
key = parseSecret(secret, this.network);
yield wallet.importKey(0, key);
@ -714,10 +739,10 @@ RPC.prototype.importWallet = co(function* importWallet(args, help) {
var data, key;
if (help || args.length !== 1)
throw new RPCError('importwallet "filename" ( rescan )');
throw new RPCError(errs.MISC_ERROR, 'importwallet "filename" ( rescan )');
if (fs.unsupported)
throw new RPCError('FS not available.');
throw new RPCError(errs.INTERNAL_ERROR, 'FS not available.');
data = yield fs.readFile(file, 'utf8');
@ -735,9 +760,9 @@ RPC.prototype.importWallet = co(function* importWallet(args, help) {
parts = line.split(/\s+/);
if (parts.length < 4)
throw new RPCError('Malformed wallet.');
throw new RPCError(errs.DESERIALIZATION_ERROR, 'Malformed wallet.');
secret = KeyRing.fromSecret(parts[0], this.network);
secret = parseSecret(parts[0], this.network);
time = +parts[1];
label = parts[2];
@ -765,21 +790,23 @@ RPC.prototype.importAddress = co(function* importAddress(args, help) {
var p2sh = valid.bool(3, false);
var script;
if (help || args.length < 1 || args.length > 4)
throw new RPCError('importaddress "address" ( "label" rescan p2sh )');
if (help || args.length < 1 || args.length > 4) {
throw new RPCError(errs.MISC_ERROR,
'importaddress "address" ( "label" rescan p2sh )');
}
if (p2sh) {
script = valid.buf(0);
if (!script)
throw new RPCError('Invalid parameters.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameters.');
script = Script.fromRaw(script);
script = fromRaw(Script, script);
script = Script.fromScripthash(script.hash160());
addr = script.getAddress();
} else {
addr = Address.fromBase58(addr, this.network);
addr = parseAddress(addr, this.network);
}
yield wallet.importAddress(0, addr);
@ -797,11 +824,13 @@ RPC.prototype.importPubkey = co(function* importPubkey(args, help) {
var rescan = valid.bool(2, false);
var key;
if (help || args.length < 1 || args.length > 4)
throw new RPCError('importpubkey "pubkey" ( "label" rescan )');
if (help || args.length < 1 || args.length > 4) {
throw new RPCError(errs.MISC_ERROR,
'importpubkey "pubkey" ( "label" rescan )');
}
if (!data)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
key = KeyRing.fromPublic(data, this.network);
@ -815,7 +844,7 @@ RPC.prototype.importPubkey = co(function* importPubkey(args, help) {
RPC.prototype.keyPoolRefill = co(function* keyPoolRefill(args, help) {
if (help || args.length > 1)
throw new RPCError('keypoolrefill ( newsize )');
throw new RPCError(errs.MISC_ERROR, 'keypoolrefill ( newsize )');
return null;
});
@ -827,8 +856,10 @@ RPC.prototype.listAccounts = co(function* listAccounts(args, help) {
var map = {};
var i, accounts, account, balance, value;
if (help || args.length > 2)
throw new RPCError('listaccounts ( minconf includeWatchonly)');
if (help || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'listaccounts ( minconf includeWatchonly)');
}
accounts = yield wallet.getAccounts();
@ -852,7 +883,7 @@ RPC.prototype.listAccounts = co(function* listAccounts(args, help) {
RPC.prototype.listAddressGroupings = co(function* listAddressGroupings(args, help) {
if (help)
throw new RPCError('listaddressgroupings');
throw new RPCError(errs.MISC_ERROR, 'listaddressgroupings');
throw new Error('Not implemented.');
});
@ -861,7 +892,7 @@ RPC.prototype.listLockUnspent = co(function* listLockUnspent(args, help) {
var i, outpoints, outpoint, out;
if (help || args.length > 0)
throw new RPCError('listlockunspent');
throw new RPCError(errs.MISC_ERROR, 'listlockunspent');
outpoints = wallet.getLocked();
out = [];
@ -884,8 +915,8 @@ RPC.prototype.listReceivedByAccount = co(function* listReceivedByAccount(args, h
var watchOnly = valid.bool(2, false);
if (help || args.length > 3) {
throw new RPCError('listreceivedbyaccount'
+ ' ( minconf includeempty includeWatchonly )');
throw new RPCError(errs.MISC_ERROR,
'listreceivedbyaccount ( minconf includeempty includeWatchonly )');
}
return yield this._listReceived(minconf, includeEmpty, watchOnly, true);
@ -898,8 +929,8 @@ RPC.prototype.listReceivedByAddress = co(function* listReceivedByAddress(args, h
var watchOnly = valid.bool(2, false);
if (help || args.length > 3) {
throw new RPCError('listreceivedbyaddress'
+ ' ( minconf includeempty includeWatchonly )');
throw new RPCError(errs.MISC_ERROR,
'listreceivedbyaddress ( minconf includeempty includeWatchonly )');
}
return yield this._listReceived(minconf, includeEmpty, watchOnly, false);
@ -1017,8 +1048,8 @@ RPC.prototype.listSinceBlock = co(function* listSinceBlock(args, help) {
var i, entry, highest, txs, wtx, json;
if (help) {
throw new RPCError('listsinceblock'
+ ' ( "blockhash" target-confirmations includeWatchonly)');
throw new RPCError(errs.MISC_ERROR,
'listsinceblock ( "blockhash" target-confirmations includeWatchonly)');
}
if (wallet.watchOnly !== watchOnly)
@ -1070,7 +1101,7 @@ RPC.prototype._toListTX = co(function* _toListTX(wtx) {
var i, member, index;
if (!details)
throw new RPCError('TX not found.');
throw new RPCError(errs.WALLET_ERROR, 'TX not found.');
for (i = 0; i < details.inputs.length; i++) {
member = details.inputs[i];
@ -1145,7 +1176,7 @@ RPC.prototype.listTransactions = co(function* listTransactions(args, help) {
var i, txs, wtx, json;
if (help || args.length > 4) {
throw new RPCError(
throw new RPCError(errs.MISC_ERROR,
'listtransactions ( "account" count from includeWatchonly)');
}
@ -1182,8 +1213,8 @@ RPC.prototype.listUnspent = co(function* listUnspent(args, help) {
var i, depth, address, hash, coins, coin, ring;
if (help || args.length > 3) {
throw new RPCError('listunspent'
+ ' ( minconf maxconf ["address",...] )');
throw new RPCError(errs.MISC_ERROR,
'listunspent ( minconf maxconf ["address",...] )');
}
if (addrs) {
@ -1193,10 +1224,10 @@ RPC.prototype.listUnspent = co(function* listUnspent(args, help) {
hash = Address.getHash(address, 'hex');
if (!hash)
throw new RPCError('Invalid address.');
throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid address.');
if (map[hash])
throw new RPCError('Duplicate address.');
throw new RPCError(errs.INVALID_PARAMETER, 'Duplicate address.');
map[hash] = true;
}
@ -1254,8 +1285,8 @@ RPC.prototype.lockUnspent = co(function* lockUnspent(args, help) {
var i, output, outpoint, hash, index;
if (help || args.length < 1 || args.length > 2) {
throw new RPCError('lockunspent'
+ ' unlock ([{"txid":"txid","vout":n},...])');
throw new RPCError(errs.MISC_ERROR,
'lockunspent unlock ([{"txid":"txid","vout":n},...])');
}
if (args.length === 1) {
@ -1265,7 +1296,7 @@ RPC.prototype.lockUnspent = co(function* lockUnspent(args, help) {
}
if (!outputs)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
for (i = 0; i < outputs.length; i++) {
output = outputs[i];
@ -1274,7 +1305,7 @@ RPC.prototype.lockUnspent = co(function* lockUnspent(args, help) {
index = valid.u32('vout');
if (hash == null || index == null)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter.');
outpoint = new Outpoint();
outpoint.hash = hash;
@ -1306,15 +1337,15 @@ RPC.prototype.sendFrom = co(function* sendFrom(args, help) {
var options, tx;
if (help || args.length < 3 || args.length > 6) {
throw new RPCError('sendfrom'
+ ' "fromaccount" "tobitcoinaddress"'
throw new RPCError(errs.MISC_ERROR,
'sendfrom "fromaccount" "tobitcoinaddress"'
+ ' amount ( minconf "comment" "comment-to" )');
}
if (!addr || value == null)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
addr = Address.fromBase58(addr, this.network);
addr = parseAddress(addr, this.network);
if (name === '')
name = 'default';
@ -1348,8 +1379,8 @@ RPC.prototype.sendMany = co(function* sendMany(args, help) {
var hash, output, options;
if (help || args.length < 2 || args.length > 5) {
throw new RPCError('sendmany'
+ ' "fromaccount" {"address":amount,...}'
throw new RPCError(errs.MISC_ERROR,
'sendmany "fromaccount" {"address":amount,...}'
+ ' ( minconf "comment" ["address",...] )');
}
@ -1357,7 +1388,7 @@ RPC.prototype.sendMany = co(function* sendMany(args, help) {
name = 'default';
if (!sendTo)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
keys = Object.keys(sendTo);
valid = new Validator([sendTo]);
@ -1365,14 +1396,14 @@ RPC.prototype.sendMany = co(function* sendMany(args, help) {
for (i = 0; i < keys.length; i++) {
key = keys[i];
value = valid.btc(key);
address = Address.fromBase58(key, this.network);
address = parseAddress(key, this.network);
hash = address.getHash('hex');
if (value == null)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter.');
if (uniq[hash])
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter.');
uniq[hash] = true;
@ -1403,16 +1434,15 @@ RPC.prototype.sendToAddress = co(function* sendToAddress(args, help) {
var options, tx;
if (help || args.length < 2 || args.length > 5) {
throw new RPCError('sendtoaddress'
+ ' "bitcoinaddress" amount'
+ ' ( "comment" "comment-to"'
+ ' subtractfeefromamount )');
throw new RPCError(errs.MISC_ERROR,
'sendtoaddress "bitcoinaddress" amount'
+ ' ( "comment" "comment-to" subtractfeefromamount )');
}
addr = Address.fromBase58(addr, this.network);
addr = parseAddress(addr, this.network);
if (!addr || value == null)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
options = {
subtractFee: subtractFee,
@ -1429,8 +1459,10 @@ RPC.prototype.sendToAddress = co(function* sendToAddress(args, help) {
});
RPC.prototype.setAccount = co(function* setAccount(args, help) {
if (help || args.length < 1 || args.length > 2)
throw new RPCError('setaccount "bitcoinaddress" "account"');
if (help || args.length < 1 || args.length > 2) {
throw new RPCError(errs.MISC_ERROR,
'setaccount "bitcoinaddress" "account"');
}
// Impossible to implement in bcoin:
throw new Error('Not implemented.');
@ -1441,10 +1473,10 @@ RPC.prototype.setTXFee = co(function* setTXFee(args, help) {
var rate = valid.btc(0);
if (help || args.length < 1 || args.length > 1)
throw new RPCError('settxfee amount');
throw new RPCError(errs.MISC_ERROR, 'settxfee amount');
if (rate == null)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
this.feeRate = rate;
@ -1458,21 +1490,23 @@ RPC.prototype.signMessage = co(function* signMessage(args, help) {
var msg = valid.str(1, '');
var sig, ring;
if (help || args.length !== 2)
throw new RPCError('signmessage "bitcoinaddress" "message"');
if (help || args.length !== 2) {
throw new RPCError(errs.MISC_ERROR,
'signmessage "bitcoinaddress" "message"');
}
addr = Address.getHash(addr, 'hex');
if (!addr)
throw new RPCError('Invalid address.');
throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid address.');
ring = yield wallet.getKey(addr);
if (!ring)
throw new RPCError('Address not found.');
throw new RPCError(errs.WALLET_ERROR, 'Address not found.');
if (!wallet.master.key)
throw new RPCError('Wallet is locked.');
throw new RPCError(errs.WALLET_UNLOCK_NEEDED, 'Wallet is locked.');
msg = new Buffer(MAGIC_STRING + msg, 'utf8');
msg = crypto.hash256(msg);
@ -1486,10 +1520,10 @@ RPC.prototype.walletLock = co(function* walletLock(args, help) {
var wallet = this.wallet;
if (help || (wallet.master.encrypted && args.length !== 0))
throw new RPCError('walletlock');
throw new RPCError(errs.MISC_ERROR, 'walletlock');
if (!wallet.master.encrypted)
throw new RPCError('Wallet is not encrypted.');
throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.');
yield wallet.lock();
@ -1503,15 +1537,15 @@ RPC.prototype.walletPassphraseChange = co(function* walletPassphraseChange(args,
var new_ = valid.str(1, '');
if (help || (wallet.master.encrypted && args.length !== 2)) {
throw new RPCError('walletpassphrasechange'
throw new RPCError(errs.MISC_ERROR, 'walletpassphrasechange'
+ ' "oldpassphrase" "newpassphrase"');
}
if (!wallet.master.encrypted)
throw new RPCError('Wallet is not encrypted.');
throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.');
if (old.length < 1 || new_.length < 1)
throw new RPCError('Invalid parameter');
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter');
yield wallet.setPassphrase(old, new_);
@ -1524,17 +1558,19 @@ RPC.prototype.walletPassphrase = co(function* walletPassphrase(args, help) {
var passphrase = valid.str(0, '');
var timeout = valid.u32(1);
if (help || (wallet.master.encrypted && args.length !== 2))
throw new RPCError('walletpassphrase "passphrase" timeout');
if (help || (wallet.master.encrypted && args.length !== 2)) {
throw new RPCError(errs.MISC_ERROR,
'walletpassphrase "passphrase" timeout');
}
if (!wallet.master.encrypted)
throw new RPCError('Wallet is not encrypted.');
throw new RPCError(errs.WALLET_WRONG_ENC_STATE, 'Wallet is not encrypted.');
if (passphrase.length < 1)
throw new RPCError('Invalid parameter');
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid parameter');
if (timeout == null)
throw new RPCError('Invalid parameter');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter');
yield wallet.unlock(passphrase, timeout);
@ -1548,27 +1584,27 @@ RPC.prototype.importPrunedFunds = co(function* importPrunedFunds(args, help) {
var hash, height;
if (help || args.length < 2 || args.length > 3) {
throw new RPCError('importprunedfunds'
+ ' "rawtransaction" "txoutproof" ( "label" )');
throw new RPCError(errs.MISC_ERROR,
'importprunedfunds "rawtransaction" "txoutproof" ( "label" )');
}
if (!tx || !block)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
tx = TX.fromRaw(tx);
block = MerkleBlock.fromRaw(block);
tx = fromRaw(TX, tx);
block = fromRaw(MerkleBlock, block);
hash = block.hash('hex');
if (!block.verify())
throw new RPCError('Invalid proof.');
throw new RPCError(errs.VERIFY_ERROR, 'Invalid proof.');
if (!block.hasTX(tx.hash('hex')))
throw new RPCError('Invalid proof.');
throw new RPCError(errs.VERIFY_ERROR, 'Invalid proof.');
height = yield this.client.getEntry(hash);
if (height === -1)
throw new RPCError('Invalid proof.');
throw new RPCError(errs.VERIFY_ERROR, 'Invalid proof.');
block = {
hash: hash,
@ -1577,7 +1613,7 @@ RPC.prototype.importPrunedFunds = co(function* importPrunedFunds(args, help) {
};
if (!(yield this.wdb.addTX(tx, block)))
throw new RPCError('No tracked address for TX.');
throw new RPCError(errs.WALLET_ERROR, 'No tracked address for TX.');
return null;
});
@ -1588,13 +1624,13 @@ RPC.prototype.removePrunedFunds = co(function* removePrunedFunds(args, help) {
var hash = valid.hash(0);
if (help || args.length !== 1)
throw new RPCError('removeprunedfunds "txid"');
throw new RPCError(errs.MISC_ERROR, 'removeprunedfunds "txid"');
if (!hash)
throw new RPCError('Invalid parameter.');
throw new RPCError(errs.TYPE_ERROR, 'Invalid parameter.');
if (!(yield wallet.remove(hash)))
throw new RPCError('Transaction not in wallet.');
throw new RPCError(errs.WALLET_ERROR, 'Transaction not in wallet.');
return null;
});
@ -1605,12 +1641,12 @@ RPC.prototype.selectWallet = co(function* selectWallet(args, help) {
var wallet;
if (help || args.length !== 1)
throw new RPCError('selectwallet "id"');
throw new RPCError(errs.MISC_ERROR, 'selectwallet "id"');
wallet = yield this.wdb.get(id);
if (!wallet)
throw new RPCError('Wallet not found.');
throw new RPCError(errs.WALLET_ERROR, 'Wallet not found.');
this.wallet = wallet;
@ -1619,7 +1655,7 @@ RPC.prototype.selectWallet = co(function* selectWallet(args, help) {
RPC.prototype.getMemoryInfo = co(function* getMemoryInfo(args, help) {
if (help || args.length !== 0)
throw new RPCError('getmemoryinfo');
throw new RPCError(errs.MISC_ERROR, 'getmemoryinfo');
return util.memoryUsage();
});
@ -1629,13 +1665,41 @@ RPC.prototype.setLogLevel = co(function* setLogLevel(args, help) {
var level = valid.str(0, '');
if (help || args.length !== 1)
throw new RPCError('setloglevel "level"');
throw new RPCError(errs.MISC_ERROR, 'setloglevel "level"');
this.logger.setLevel(level);
return null;
});
/*
* Helpers
*/
function fromRaw(ctor, raw) {
try {
return ctor.fromRaw(raw);
} catch (e) {
throw new RPCError(errs.DESERIALIZATION_ERROR, 'Deserialization error.');
}
}
function parseAddress(raw, network) {
try {
return Address.fromBase58(raw, network);
} catch (e) {
throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid address.');
}
}
function parseSecret(raw, network) {
try {
return KeyRing.fromSecret(raw, network);
} catch (e) {
throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid key.');
}
}
/*
* Expose
*/