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.feeRate = null;
|
||||||
this.mining = false;
|
this.mining = false;
|
||||||
this.proclimit = 0;
|
this.proclimit = 0;
|
||||||
|
|
||||||
|
this.prevBlock = null;
|
||||||
|
this.currentBlock = null;
|
||||||
|
this.lastTX = 0;
|
||||||
|
this.start = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RPC.prototype.execute = function execute(json, callback) {
|
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,
|
confirmations: self.chain.height - entry.height + 1,
|
||||||
strippedsize: block.getBaseSize(),
|
strippedsize: block.getBaseSize(),
|
||||||
size: block.getSize(),
|
size: block.getSize(),
|
||||||
cost: block.getCost(),
|
weight: block.getCost(),
|
||||||
height: entry.height,
|
height: entry.height,
|
||||||
version: entry.version,
|
version: entry.version,
|
||||||
merkleroot: utils.revHex(entry.merkleRoot),
|
merkleroot: utils.revHex(entry.merkleRoot),
|
||||||
@ -1160,64 +1165,215 @@ RPC.prototype.getblocktemplate = function getblocktemplate(args, callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var txs = [];
|
var txs = [];
|
||||||
var txIndex = {};
|
var txIndex = {};
|
||||||
|
var mode = 'template';
|
||||||
|
var maxVersion = -1;
|
||||||
var i, j, tx, deps, input, dep, block;
|
var i, j, tx, deps, input, dep, block;
|
||||||
|
var opt, lpid, keys, vbavailable, vbrules, mutable, clientRules;
|
||||||
|
|
||||||
if (args.help || args.length > 1)
|
if (args.help || args.length > 1)
|
||||||
return callback(new RPCError('getblocktemplate ( "jsonrequestobject" )'));
|
return callback(new RPCError('getblocktemplate ( "jsonrequestobject" )'));
|
||||||
|
|
||||||
this.miner.createBlock(function(err, attempt) {
|
if (args.length === 1) {
|
||||||
if (err)
|
opt = args[0] || {};
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
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++) {
|
lpid = opt.longpollid;
|
||||||
tx = block.txs[i];
|
|
||||||
txIndex[tx.hash('hex')] = i;
|
|
||||||
deps = [];
|
|
||||||
|
|
||||||
for (j = 0; j < tx.inputs.length; j++) {
|
if (mode === 'proposal') {
|
||||||
input = tx.inputs[j];
|
if (!utils.isHex(opt.data))
|
||||||
dep = txIndex[input.prevout.hash];
|
return callback(new RPCError('Invalid parameter.'));
|
||||||
if (dep != null)
|
|
||||||
deps.push(dep);
|
|
||||||
}
|
|
||||||
|
|
||||||
txs.push({
|
block = bcoin.block.fromRaw(opt.data, 'hex');
|
||||||
data: tx.toRaw().toString('hex'),
|
|
||||||
txid: tx.rhash,
|
if (block.prevBlock !== self.chain.tip.hash)
|
||||||
hash: tx.rwhash,
|
return callback(new RPCError('inconclusive-not-best-prevblk'));
|
||||||
depends: deps,
|
|
||||||
fee: tx.getFee(),
|
return self.chain.add(block, function(err) {
|
||||||
sigops: tx.getSigops(),
|
if (err) {
|
||||||
cost: tx.getCost()
|
if (err.reason)
|
||||||
|
return callback(null, err.reason);
|
||||||
|
return callback(null, 'rejected');
|
||||||
|
}
|
||||||
|
return callback(null, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, {
|
if (Array.isArray(opt.rules)) {
|
||||||
capabilities: ['proposal'],
|
clientRules = [];
|
||||||
previousblockhash: utils.revHex(block.prevBlock),
|
for (i = 0; i < opt.rules.length; i++)
|
||||||
transactions: txs,
|
clientRules.push(String(opt.rules[i]));
|
||||||
coinbaseaux: attempt.coinbaseFlags.toString('hex'),
|
} else if (utils.isNumber(opt.maxversion)) {
|
||||||
coinbasevalue: attempt.coinbase.outputs[0].value,
|
maxVersion = opt.maxversion;
|
||||||
longpollid: self.chain.tip.rhash + self.mempool.total,
|
}
|
||||||
target: attempt.target.toString('hex'),
|
}
|
||||||
mintime: attempt.ts,
|
|
||||||
mutable: ['time', 'transactions', 'prevblock', 'version/force'],
|
if (this.pool.peers.all.length === 0)
|
||||||
noncerange: '00000000ffffffff',
|
return callback(new RPCError('Bitcoin is not connected!'));
|
||||||
sigoplimit: constants.block.MAX_SIGOPS_COST,
|
|
||||||
sizelimit: constants.block.MAX_SIZE,
|
if (!this.chain.isFull())
|
||||||
costlimit: constants.block.MAX_COST,
|
return callback(new RPCError('Bitcoin is downloading blocks...'));
|
||||||
curtime: block.ts,
|
|
||||||
bits: block.bits,
|
this._poll(lpid, function(err) {
|
||||||
height: attempt.height,
|
if (err)
|
||||||
default_witness_commitment: attempt.witness
|
return callback(err);
|
||||||
? attempt.coinbase.outputs[1].script.toJSON()
|
|
||||||
: undefined
|
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) {
|
RPC.prototype.getmininginfo = function getmininginfo(args, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
|||||||
@ -331,27 +331,32 @@ main.deployments = {
|
|||||||
testdummy: {
|
testdummy: {
|
||||||
bit: 28,
|
bit: 28,
|
||||||
startTime: 1199145601, // January 1, 2008
|
startTime: 1199145601, // January 1, 2008
|
||||||
timeout: 1230767999 // December 31, 2008
|
timeout: 1230767999, // December 31, 2008
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
csv: {
|
csv: {
|
||||||
bit: 0,
|
bit: 0,
|
||||||
startTime: 1462060800, // May 1st, 2016
|
startTime: 1462060800, // May 1st, 2016
|
||||||
timeout: 1493596800 // May 1st, 2017
|
timeout: 1493596800, // May 1st, 2017
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
witness: {
|
witness: {
|
||||||
bit: 1,
|
bit: 1,
|
||||||
startTime: 2000000000, // Far in the future
|
startTime: 2000000000, // Far in the future
|
||||||
timeout: 2100000000
|
timeout: 2100000000,
|
||||||
|
force: false
|
||||||
},
|
},
|
||||||
mast: {
|
mast: {
|
||||||
bit: 2,
|
bit: 2,
|
||||||
startTime: 2000000000, // Far in the future
|
startTime: 2000000000, // Far in the future
|
||||||
timeout: 2100000000
|
timeout: 2100000000,
|
||||||
|
force: false
|
||||||
}
|
}
|
||||||
// bip109: {
|
// bip109: {
|
||||||
// bit: 28,
|
// bit: 28,
|
||||||
// startTime: 1453939200, // Jan 28th, 2016
|
// startTime: 1453939200, // Jan 28th, 2016
|
||||||
// timeout: 1514764800 // Jan 1st, 2018
|
// timeout: 1514764800, // Jan 1st, 2018
|
||||||
|
// force: true
|
||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -554,22 +559,26 @@ testnet.deployments = {
|
|||||||
testdummy: {
|
testdummy: {
|
||||||
bit: 28,
|
bit: 28,
|
||||||
startTime: 1199145601, // January 1, 2008
|
startTime: 1199145601, // January 1, 2008
|
||||||
timeout: 1230767999 // December 31, 2008
|
timeout: 1230767999, // December 31, 2008
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
csv: {
|
csv: {
|
||||||
bit: 0,
|
bit: 0,
|
||||||
startTime: 1456790400, // March 1st, 2016
|
startTime: 1456790400, // March 1st, 2016
|
||||||
timeout: 1493596800 // May 1st, 2017
|
timeout: 1493596800, // May 1st, 2017
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
witness: {
|
witness: {
|
||||||
bit: 1,
|
bit: 1,
|
||||||
startTime: 1462060800, // May 1st 2016
|
startTime: 1462060800, // May 1st 2016
|
||||||
timeout: 1493596800 // May 1st 2017
|
timeout: 1493596800, // May 1st 2017
|
||||||
|
force: false
|
||||||
},
|
},
|
||||||
mast: {
|
mast: {
|
||||||
bit: 2,
|
bit: 2,
|
||||||
startTime: 2000000000, // Far in the future
|
startTime: 2000000000, // Far in the future
|
||||||
timeout: 2100000000
|
timeout: 2100000000,
|
||||||
|
force: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -699,22 +708,26 @@ regtest.deployments = {
|
|||||||
testdummy: {
|
testdummy: {
|
||||||
bit: 28,
|
bit: 28,
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
timeout: 999999999999
|
timeout: 999999999999,
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
csv: {
|
csv: {
|
||||||
bit: 0,
|
bit: 0,
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
timeout: 999999999999
|
timeout: 999999999999,
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
witness: {
|
witness: {
|
||||||
bit: 1,
|
bit: 1,
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
timeout: 999999999999
|
timeout: 999999999999,
|
||||||
|
force: false
|
||||||
},
|
},
|
||||||
mast: {
|
mast: {
|
||||||
bit: 2,
|
bit: 2,
|
||||||
startTime: 2000000000, // Far in the future
|
startTime: 2000000000, // Far in the future
|
||||||
timeout: 2100000000
|
timeout: 2100000000,
|
||||||
|
force: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -966,17 +979,20 @@ segnet4.deployments = {
|
|||||||
testdummy: {
|
testdummy: {
|
||||||
bit: 28,
|
bit: 28,
|
||||||
startTime: 1199145601, // January 1, 2008
|
startTime: 1199145601, // January 1, 2008
|
||||||
timeout: 1230767999 // December 31, 2008
|
timeout: 1230767999, // December 31, 2008
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
csv: {
|
csv: {
|
||||||
bit: 0,
|
bit: 0,
|
||||||
startTime: 1456790400, // March 1st, 2016
|
startTime: 1456790400, // March 1st, 2016
|
||||||
timeout: 1493596800 // May 1st, 2017
|
timeout: 1493596800, // May 1st, 2017
|
||||||
|
force: true
|
||||||
},
|
},
|
||||||
witness: {
|
witness: {
|
||||||
bit: 1,
|
bit: 1,
|
||||||
startTime: 0,
|
startTime: 0,
|
||||||
timeout: 999999999999
|
timeout: 999999999999,
|
||||||
|
force: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user