rpc: implement getblocktemplate versionbits and polling.
This commit is contained in:
parent
74c4294a36
commit
c7b725450a
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user