rpc: implement getblocktemplate versionbits and polling.

This commit is contained in:
Christopher Jeffrey 2016-08-10 17:21:58 -07:00
parent 74c4294a36
commit c7b725450a
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 231 additions and 59 deletions

View File

@ -29,6 +29,11 @@ function RPC(node) {
this.feeRate = null;
this.mining = false;
this.proclimit = 0;
this.prevBlock = null;
this.currentBlock = null;
this.lastTX = 0;
this.start = 0;
}
RPC.prototype.execute = function execute(json, callback) {
@ -892,7 +897,7 @@ RPC.prototype.blockToJSON = function blockToJSON(entry, block, txDetails, callba
confirmations: self.chain.height - entry.height + 1,
strippedsize: block.getBaseSize(),
size: block.getSize(),
cost: block.getCost(),
weight: block.getCost(),
height: entry.height,
version: entry.version,
merkleroot: utils.revHex(entry.merkleRoot),
@ -1160,64 +1165,215 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) {
var self = this;
var txs = [];
var txIndex = {};
var mode = 'template';
var maxVersion = -1;
var i, j, tx, deps, input, dep, block;
var opt, lpid, keys, vbavailable, vbrules, mutable, clientRules;
if (args.help || args.length > 1)
return callback(new RPCError('getblocktemplate ( "jsonrequestobject" )'));
this.miner.createBlock(function(err, attempt) {
if (err)
return callback(err);
if (args.length === 1) {
opt = args[0] || {};
block = attempt.block;
if (opt.mode != null) {
mode = opt.mode;
if (mode !== 'template' && mode !== 'proposal')
return callback(new RPCError('Invalid mode.'));
}
for (i = 1; i < block.txs.length; i++) {
tx = block.txs[i];
txIndex[tx.hash('hex')] = i;
deps = [];
lpid = opt.longpollid;
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
dep = txIndex[input.prevout.hash];
if (dep != null)
deps.push(dep);
}
if (mode === 'proposal') {
if (!utils.isHex(opt.data))
return callback(new RPCError('Invalid parameter.'));
txs.push({
data: tx.toRaw().toString('hex'),
txid: tx.rhash,
hash: tx.rwhash,
depends: deps,
fee: tx.getFee(),
sigops: tx.getSigops(),
cost: tx.getCost()
block = bcoin.block.fromRaw(opt.data, 'hex');
if (block.prevBlock !== self.chain.tip.hash)
return callback(new RPCError('inconclusive-not-best-prevblk'));
return self.chain.add(block, function(err) {
if (err) {
if (err.reason)
return callback(null, err.reason);
return callback(null, 'rejected');
}
return callback(null, null);
});
}
callback(null, {
capabilities: ['proposal'],
previousblockhash: utils.revHex(block.prevBlock),
transactions: txs,
coinbaseaux: attempt.coinbaseFlags.toString('hex'),
coinbasevalue: attempt.coinbase.outputs[0].value,
longpollid: self.chain.tip.rhash + self.mempool.total,
target: attempt.target.toString('hex'),
mintime: attempt.ts,
mutable: ['time', 'transactions', 'prevblock', 'version/force'],
noncerange: '00000000ffffffff',
sigoplimit: constants.block.MAX_SIGOPS_COST,
sizelimit: constants.block.MAX_SIZE,
costlimit: constants.block.MAX_COST,
curtime: block.ts,
bits: block.bits,
height: attempt.height,
default_witness_commitment: attempt.witness
? attempt.coinbase.outputs[1].script.toJSON()
: undefined
if (Array.isArray(opt.rules)) {
clientRules = [];
for (i = 0; i < opt.rules.length; i++)
clientRules.push(String(opt.rules[i]));
} else if (utils.isNumber(opt.maxversion)) {
maxVersion = opt.maxversion;
}
}
if (this.pool.peers.all.length === 0)
return callback(new RPCError('Bitcoin is not connected!'));
if (!this.chain.isFull())
return callback(new RPCError('Bitcoin is downloading blocks...'));
this._poll(lpid, function(err) {
if (err)
return callback(err);
self._createBlock(function(err, attempt) {
if (err)
return callback(err);
block = attempt.block;
for (i = 1; i < block.txs.length; i++) {
tx = block.txs[i];
txIndex[tx.hash('hex')] = i;
deps = [];
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
dep = txIndex[input.prevout.hash];
if (dep != null)
deps.push(dep);
}
txs.push({
data: tx.toRaw().toString('hex'),
txid: tx.rhash,
hash: tx.rwhash,
depends: deps,
fee: tx.getFee(),
sigops: tx.getSigops(),
weight: tx.getCost()
});
}
keys = Object.keys(self.network.deployments);
vbavailable = {};
vbrules = [];
mutable = ['time', 'transactions', 'prevblock'];
if (maxVersion >= 2)
mutable.push('version/force');
utils.forEachSerial(keys, function(id, next) {
var deployment = self.network.deployments[id];
self.chain.getState(self.chain.tip, id, function(err, state) {
if (err)
return next(err);
switch (state) {
case constants.thresholdStates.DEFINED:
case constants.thresholdStates.FAILED:
break;
case constants.thresholdStates.LOCKED_IN:
block.version |= 1 << deployment.bit;
case constants.thresholdStates.STARTED:
vbavailable[id] = deployment.bit;
if (clientRules) {
if (clientRules.indexOf(id) === -1 && !deployment.force)
block.version &= ~(1 << deployment.bit);
}
break;
case constants.thresholdStates.ACTIVE:
vbrules.push(id);
if (clientRules) {
if (clientRules.indexOf(id) === -1 && !deployment.force)
return next(new RPCError('Client must support ' + id + '.'));
}
break;
}
next();
});
}, function(err) {
if (err)
return callback(err);
block.version >>>= 0;
callback(null, {
capabilities: ['proposal'],
version: block.version,
rules: vbrules,
vbavailable: vbavailable,
vbrequired: 0,
previousblockhash: utils.revHex(block.prevBlock),
transactions: txs,
coinbaseaux: {
flags: new Buffer(attempt.coinbaseFlags, 'utf8').toString('hex')
},
coinbasevalue: attempt.coinbase.outputs[0].value,
longpollid: self.chain.tip.rhash + self.mempool.total,
target: utils.revHex(attempt.target.toString('hex')),
mintime: attempt.ts,
mutable: mutable,
noncerange: '00000000ffffffff',
sigoplimit: constants.block.MAX_SIGOPS_COST,
sizelimit: constants.block.MAX_SIZE,
costlimit: constants.block.MAX_COST,
curtime: block.ts,
bits: String(block.bits),
height: attempt.height,
default_witness_commitment: attempt.witness
? attempt.coinbase.outputs[1].script.toJSON()
: undefined
});
});
});
});
};
RPC.prototype._poll = function _poll(lpid, callback) {
var self = this;
var watched, lastTX;
if (lpid == null)
return callback();
if (typeof lpid === 'string') {
watched = lpid.slice(0, 64);
lastTX = +lpid.slice(64);
if (!utils.isHex(watched) || !utils.isNumber(lastTX))
return callback(new RPCError('Invalid parameter.'));
watched = utils.revHex(watched);
} else {
watched = this.chain.tip.hash;
lastTX = this.lastTX;
}
function listener() {
if (self.chain.tip.hash !== watched || self.mempool.total !== lastTX) {
self.chain.removeListener('block', listener);
self.mempool.removeListener('tx', listener);
return callback();
}
}
this.chain.on('block', listener);
this.mempool.on('tx', listener);
};
RPC.prototype._createBlock = function _createBlock(callback) {
var self = this;
if (this.prevBlock !== this.chain.tip.hash
|| (this.mempool.total !== this.lastTX && utils.now() - this.start > 5)) {
return this.miner.createBlock(function(err, attempt) {
if (err)
return callback(err);
self.prevBlock = attempt.block.prevBlock;
self.currentBlock = attempt;
self.lastTX = self.mempool.total;
self.start = utils.now();
callback(null, attempt);
});
}
return callback(null, this.currentBlock);
};
RPC.prototype.getmininginfo = function getmininginfo(args, callback) {
var self = this;

View File

@ -331,27 +331,32 @@ main.deployments = {
testdummy: {
bit: 28,
startTime: 1199145601, // January 1, 2008
timeout: 1230767999 // December 31, 2008
timeout: 1230767999, // December 31, 2008
force: true
},
csv: {
bit: 0,
startTime: 1462060800, // May 1st, 2016
timeout: 1493596800 // May 1st, 2017
timeout: 1493596800, // May 1st, 2017
force: true
},
witness: {
bit: 1,
startTime: 2000000000, // Far in the future
timeout: 2100000000
timeout: 2100000000,
force: false
},
mast: {
bit: 2,
startTime: 2000000000, // Far in the future
timeout: 2100000000
timeout: 2100000000,
force: false
}
// bip109: {
// bit: 28,
// startTime: 1453939200, // Jan 28th, 2016
// timeout: 1514764800 // Jan 1st, 2018
// timeout: 1514764800, // Jan 1st, 2018
// force: true
// }
};
@ -554,22 +559,26 @@ testnet.deployments = {
testdummy: {
bit: 28,
startTime: 1199145601, // January 1, 2008
timeout: 1230767999 // December 31, 2008
timeout: 1230767999, // December 31, 2008
force: true
},
csv: {
bit: 0,
startTime: 1456790400, // March 1st, 2016
timeout: 1493596800 // May 1st, 2017
timeout: 1493596800, // May 1st, 2017
force: true
},
witness: {
bit: 1,
startTime: 1462060800, // May 1st 2016
timeout: 1493596800 // May 1st 2017
timeout: 1493596800, // May 1st 2017
force: false
},
mast: {
bit: 2,
startTime: 2000000000, // Far in the future
timeout: 2100000000
timeout: 2100000000,
force: false
}
};
@ -699,22 +708,26 @@ regtest.deployments = {
testdummy: {
bit: 28,
startTime: 0,
timeout: 999999999999
timeout: 999999999999,
force: true
},
csv: {
bit: 0,
startTime: 0,
timeout: 999999999999
timeout: 999999999999,
force: true
},
witness: {
bit: 1,
startTime: 0,
timeout: 999999999999
timeout: 999999999999,
force: false
},
mast: {
bit: 2,
startTime: 2000000000, // Far in the future
timeout: 2100000000
timeout: 2100000000,
force: false
}
};
@ -966,17 +979,20 @@ segnet4.deployments = {
testdummy: {
bit: 28,
startTime: 1199145601, // January 1, 2008
timeout: 1230767999 // December 31, 2008
timeout: 1230767999, // December 31, 2008
force: true
},
csv: {
bit: 0,
startTime: 1456790400, // March 1st, 2016
timeout: 1493596800 // May 1st, 2017
timeout: 1493596800, // May 1st, 2017
force: true
},
witness: {
bit: 1,
startTime: 0,
timeout: 999999999999
timeout: 999999999999,
force: false
}
};