rpc: make rpc safe for spv mode.

This commit is contained in:
Christopher Jeffrey 2016-09-17 19:00:58 -07:00
parent 666a7b7999
commit d9a3dac869
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -36,6 +36,8 @@ function RPC(node) {
this.miner = node.miner;
this.wallet = node.wallet;
this.walletdb = node.walletdb;
this.logger = node.logger;
this.locker = new bcoin.locker(this);
this.feeRate = null;
@ -775,8 +777,12 @@ RPC.prototype.getblock = function getblock(args, callback) {
return callback(err);
if (!block) {
if (self.chain.db.options.spv)
return callback(new RPCError('Block not available (spv mode)'));
if (self.chain.db.prune)
return callback(new RPCError('Block not available (pruned data)'));
return callback(new RPCError('Can\'t read block from disk'));
}
@ -1070,6 +1076,12 @@ RPC.prototype.getdifficulty = function getdifficulty(args, callback) {
};
RPC.prototype.getmempoolinfo = function getmempoolinfo(args, callback) {
if (args.help || args.length !== 0)
return callback(new RPCError('getmempoolinfo'));
if (!this.mempool)
return callback(new RPCError('No mempool available.'));
callback(null, {
size: this.mempool.totalTX,
bytes: this.mempool.getSize(),
@ -1085,6 +1097,9 @@ RPC.prototype.getmempoolancestors = function getmempoolancestors(args, callback)
if (args.help || args.length < 1 || args.length > 2)
return callback(new RPCError('getmempoolancestors txid (verbose)'));
if (!this.mempool)
return callback(new RPCError('No mempool available.'));
hash = toHash(args[0]);
if (!hash)
@ -1117,6 +1132,9 @@ RPC.prototype.getmempooldescendants = function getmempooldescendants(args, callb
if (args.help || args.length < 1 || args.length > 2)
return callback(new RPCError('getmempooldescendants txid (verbose)'));
if (!this.mempool)
return callback(new RPCError('No mempool available.'));
hash = toHash(args[0]);
if (!hash)
@ -1149,6 +1167,9 @@ RPC.prototype.getmempoolentry = function getmempoolentry(args, callback) {
if (args.help || args.length !== 1)
return callback(new RPCError('getmempoolentry txid'));
if (!this.mempool)
return callback(new RPCError('No mempool available.'));
hash = toHash(args[0]);
if (!hash)
@ -1228,6 +1249,12 @@ RPC.prototype.gettxout = function gettxout(args, callback) {
if (args.help || args.length < 2 || args.length > 3)
return callback(new RPCError('gettxout "txid" n ( includemempool )'));
if (this.chain.db.options.spv)
return callback(new RPCError('Cannot get coins in SPV mode.'));
if (this.chain.db.options.prune)
return callback(new RPCError('Cannot get coins when pruned.'));
hash = toHash(args[0]);
index = toNumber(args[1]);
mempool = true;
@ -1272,6 +1299,12 @@ RPC.prototype.gettxoutproof = function gettxoutproof(args, callback) {
+ ' ["txid",...] ( blockhash )'));
}
if (this.chain.db.options.spv)
return callback(new RPCError('Cannot get coins in SPV mode.'));
if (this.chain.db.options.prune)
return callback(new RPCError('Cannot get coins when pruned.'));
txids = toArray(args[0]);
block = args[1];
@ -1370,6 +1403,9 @@ RPC.prototype.gettxoutsetinfo = function gettxoutsetinfo(args, callback) {
if (args.help || args.length !== 0)
return callback(new RPCError('gettxoutsetinfo'));
if (this.chain.db.options.spv)
return callback(new RPCError('Chain state not available in SPV mode.'));
callback(null, {
height: this.chain.height,
bestblock: this.chain.tip.rhash,
@ -1385,6 +1421,12 @@ RPC.prototype.verifychain = function verifychain(args, callback) {
if (args.help || args.length > 2)
return callback(new RPCError('verifychain ( checklevel numblocks )'));
if (this.chain.db.options.spv)
return callback(new RPCError('Cannot verify chain in SPV mode.'));
if (this.chain.db.options.prune)
return callback(new RPCError('Cannot verify chain when pruned.'));
callback();
};
@ -1440,7 +1482,7 @@ RPC.prototype._submitwork = function getwork(data, callback) {
RPC.prototype._getwork = function _getwork(callback) {
var i, data, abbr;
this._getAttempt(function(err, attempt) {
this._getAttempt(true, function(err, attempt) {
if (err)
return callback(err);
@ -1610,7 +1652,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) {
if (!callback)
return;
this._getAttempt(function(err, attempt) {
this._getAttempt(false, function(err, attempt) {
if (err)
return callback(err);
@ -1695,7 +1737,7 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) {
flags: attempt.coinbaseFlags.toString('hex')
},
coinbasevalue: attempt.coinbase.outputs[0].value,
longpollid: self.chain.tip.rhash + utils.pad32(self.mempool.totalTX),
longpollid: self.chain.tip.rhash + utils.pad32(self._totalTX()),
target: utils.revHex(attempt.target.toString('hex')),
submitold: false,
mintime: block.ts,
@ -1736,7 +1778,6 @@ RPC.prototype._tmpl = function _tmpl(version, coinbase, rules, callback) {
};
RPC.prototype._poll = function _poll(lpid, callback) {
var self = this;
var watched, lastTX;
if (typeof lpid !== 'string')
@ -1753,7 +1794,7 @@ RPC.prototype._poll = function _poll(lpid, callback) {
watched = utils.revHex(watched);
if (self.chain.tip.hash !== watched)
if (this.chain.tip.hash !== watched)
return callback();
this.once('clear block', callback);
@ -1763,6 +1804,7 @@ RPC.prototype._clearBlock = function _clearBlock() {
this.attempt = null;
this.start = 0;
this.coinbase = {};
this.emit('clear block');
};
RPC.prototype._bindChain = function _bindChain() {
@ -1778,33 +1820,31 @@ RPC.prototype._bindChain = function _bindChain() {
return;
self._clearBlock();
self.emit('clear block');
});
this.mempool.on('tx', function() {
var diff;
if (!this.mempool)
return;
this.mempool.on('tx', function() {
if (!self.attempt)
return;
diff = utils.now() - self.start;
if (diff > 5) {
if (utils.now() - self.start > 5)
self._clearBlock();
self.emit('clear block');
}
});
};
RPC.prototype._getAttempt = function _getAttempt(callback) {
RPC.prototype._getAttempt = function _getAttempt(update, callback) {
var self = this;
var attempt = this.attempt;
this._bindChain();
if (attempt) {
attempt.updateNonce();
this.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone();
if (update) {
attempt.updateNonce();
this.coinbase[attempt.block.merkleRoot] = attempt.coinbase.clone();
}
return callback(null, attempt);
}
@ -1820,6 +1860,10 @@ RPC.prototype._getAttempt = function _getAttempt(callback) {
});
};
RPC.prototype._totalTX = function _totalTX() {
return this.mempool ? this.mempool.totalTX : 0;
};
RPC.prototype.getmininginfo = function getmininginfo(args, callback) {
var self = this;
@ -1845,7 +1889,7 @@ RPC.prototype.getmininginfo = function getmininginfo(args, callback) {
errors: '',
genproclimit: self.proclimit,
networkhashps: hashps,
pooledtx: self.mempool.totalTX,
pooledtx: self._totalTX(),
testnet: self.network !== bcoin.network.main,
chain: 'main',
generate: self.mining
@ -1878,6 +1922,9 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args, callb
+ ' <txid> <priority delta> <fee delta>'));
}
if (!this.mempool)
return callback(new RPCError('No mempool available.'));
hash = toHash(args[0]);
pri = args[1];
fee = args[2];
@ -1990,13 +2037,23 @@ RPC.prototype.setgenerate = function setgenerate(args, callback) {
RPC.prototype.generate = function generate(args, callback) {
var self = this;
var numblocks, hashes;
var numblocks;
callback = this.locker.lock(generate, [args, callback]);
if (!callback)
return;
if (args.help || args.length < 1 || args.length > 2)
return callback(new RPCError('generate numblocks ( maxtries )'));
numblocks = toNumber(args[0], 1);
hashes = [];
this._generate(numblocks, callback);
};
RPC.prototype._generate = function _generate(numblock, callback, force) {
var hashes = [];
utils.forRangeSerial(0, numblocks, function(i, next) {
self.miner.mineBlock(function(err, block) {
@ -2014,23 +2071,29 @@ RPC.prototype.generate = function generate(args, callback) {
RPC.prototype.generatetoaddress = function generatetoaddress(args, callback) {
var self = this;
var address;
var numblocks, address;
callback = this.locker.lock(generatetoaddress, [args, callback]);
if (!callback)
return;
if (args.help || args.length < 2 || args.length > 3) {
return callback(new RPCError('generatetoaddress'
+ ' numblocks address ( maxtries )'));
}
numblocks = toNumber(args[0], 1);
address = this.miner.address;
this.miner.address = bcoin.address.fromBase58(toString(args[1]));
args = args.slice();
args.splice(1, 1);
this.generate(args, function(err, hashes) {
this._generate(numblocks, function(err, hashes) {
if (err)
return callback(err);
self.miner.address = address;
callback(null, hashes);
});
};
@ -2246,7 +2309,7 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) {
merged = txs[0];
this.node.fillCoins(merged, function(err) {
this._fillCoins(merged, function(err) {
if (err)
return callback(err);
@ -2263,6 +2326,13 @@ RPC.prototype.signrawtransaction = function signrawtransaction(args, callback) {
});
};
RPC.prototype._fillCoins = function _fillCoins(tx, callback) {
if (this.chain.db.options.spv)
return callback();
this.node.fillCoins(tx, callback);
};
RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, args, callback) {
var keys = [];
var keyMap = {};
@ -2472,7 +2542,10 @@ RPC.prototype._createRedeem = function _createRedeem(args, callback) {
});
};
/* Utility functions */
/*
* Utility functions
*/
RPC.prototype.createmultisig = function createmultisig(args, callback) {
var self = this;
@ -2619,6 +2692,9 @@ RPC.prototype.estimatefee = function estimatefee(args, callback) {
if (args.help || args.length !== 1)
return callback(new RPCError('estimatefee nblocks'));
if (!this.fees)
return callback(new RPCError('Fee estimation not available.'));
blocks = toNumber(args[0], 1);
if (blocks < 1)
@ -2640,6 +2716,9 @@ RPC.prototype.estimatepriority = function estimatepriority(args, callback) {
if (args.help || args.length !== 1)
return callback(new RPCError('estimatepriority nblocks'));
if (!this.fees)
return callback(new RPCError('Priority estimation not available.'));
blocks = toNumber(args[0], 1);
if (blocks < 1)
@ -2656,6 +2735,9 @@ RPC.prototype.estimatesmartfee = function estimatesmartfee(args, callback) {
if (args.help || args.length !== 1)
return callback(new RPCError('estimatesmartfee nblocks'));
if (!this.fees)
return callback(new RPCError('Fee estimation not available.'));
blocks = toNumber(args[0], 1);
if (blocks < 1)
@ -2680,6 +2762,9 @@ RPC.prototype.estimatesmartpriority = function estimatesmartpriority(args, callb
if (args.help || args.length !== 1)
return callback(new RPCError('estimatesmartpriority nblocks'));
if (!this.fees)
return callback(new RPCError('Priority estimation not available.'));
blocks = toNumber(args[0], 1);
if (blocks < 1)