mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
Merge #14811: Mining: Enforce that segwit option must be set in GBT
d2ce315fbf
[docs] add release note for change to GBT (John Newbery)0025c9eae4
[mining] segwit option must be set in GBT (John Newbery) Pull request description: Calling getblocktemplate without the segwit rule specified is most likely a client error, since it results in lower fees for the miner. Prevent this client error by failing getblocktemplate if called without the segwit rule specified. Of the previous 1000 blocks (measured at block [551591 (hash 0x...173c811)](https://blockstream.info/block/000000000000000000173c811e79858808abc3216af607035973f002bef60a7a)), 991 included segwit transactions. Tree-SHA512: 7933b073d72683c9ab9318db46a085ec19a56a14937945c73f783ac7656887619a86b74db0bdfcb8121df44f63a1d6a6fb19e98505b2a26a6a8a6e768e442fee
This commit is contained in:
commit
feda41e0a7
@ -65,6 +65,13 @@ platform.
|
||||
Notable changes
|
||||
===============
|
||||
|
||||
Mining
|
||||
------
|
||||
|
||||
- Calls to `getblocktemplate` will fail if the segwit rule is not specified.
|
||||
Calling `getblocktemplate` without segwit specified is almost certainly
|
||||
a misconfiguration since doing so results in lower rewards for the miner.
|
||||
|
||||
Command line option changes
|
||||
---------------------------
|
||||
|
||||
@ -182,6 +189,8 @@ in the Low-level Changes section below.
|
||||
P2SH-P2WPKH, and P2SH-P2WSH. Requests for P2WSH and P2SH-P2WSH accept
|
||||
an additional `witnessscript` parameter.
|
||||
|
||||
- See the [Mining](#mining) section for changes to `getblocktemplate`.
|
||||
|
||||
Low-level changes
|
||||
=================
|
||||
|
||||
|
@ -27,7 +27,7 @@ static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey
|
||||
{
|
||||
auto block = std::make_shared<CBlock>(
|
||||
BlockAssembler{Params()}
|
||||
.CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true)
|
||||
.CreateNewBlock(coinbase_scriptPubKey)
|
||||
->block);
|
||||
|
||||
block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
|
||||
|
@ -95,7 +95,7 @@ void BlockAssembler::resetBlock()
|
||||
nFees = 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx)
|
||||
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||
{
|
||||
int64_t nTimeStart = GetTimeMicros();
|
||||
|
||||
@ -139,7 +139,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
||||
// not activated.
|
||||
// TODO: replace this with a call to main to assess validity of a mempool
|
||||
// transaction (which in most cases can be a no-op).
|
||||
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx;
|
||||
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
|
||||
|
||||
int nPackagesSelected = 0;
|
||||
int nDescendantsUpdated = 0;
|
||||
|
@ -157,7 +157,7 @@ public:
|
||||
BlockAssembler(const CChainParams& params, const Options& options);
|
||||
|
||||
/** Construct a new block template with coinbase to scriptPubKeyIn */
|
||||
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx=true);
|
||||
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
||||
|
||||
private:
|
||||
// utility functions
|
||||
|
@ -311,7 +311,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
|
||||
" https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
|
||||
{
|
||||
{"template_request", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "A json object in the following spec",
|
||||
{"template_request", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object in the following spec",
|
||||
{
|
||||
{"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
|
||||
{"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
|
||||
@ -319,7 +319,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
{"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
|
||||
},
|
||||
},
|
||||
{"rules", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
|
||||
{"rules", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A list of strings",
|
||||
{
|
||||
{"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"},
|
||||
},
|
||||
@ -503,21 +503,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
}
|
||||
|
||||
const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
|
||||
// If the caller is indicating segwit support, then allow CreateNewBlock()
|
||||
// to select witness transactions, after segwit activates (otherwise
|
||||
// don't).
|
||||
bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end();
|
||||
// GBT must be called with 'segwit' set in the rules
|
||||
if (setClientRules.count(segwit_info.name) != 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
|
||||
}
|
||||
|
||||
// Update block
|
||||
static CBlockIndex* pindexPrev;
|
||||
static int64_t nStart;
|
||||
static std::unique_ptr<CBlockTemplate> pblocktemplate;
|
||||
// Cache whether the last invocation was with segwit support, to avoid returning
|
||||
// a segwit-block to a non-segwit caller.
|
||||
static bool fLastTemplateSupportsSegwit = true;
|
||||
if (pindexPrev != chainActive.Tip() ||
|
||||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) ||
|
||||
fLastTemplateSupportsSegwit != fSupportsSegwit)
|
||||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
|
||||
{
|
||||
// Clear pindexPrev so future calls make a new block, despite any failures from here on
|
||||
pindexPrev = nullptr;
|
||||
@ -526,11 +522,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
||||
nStart = GetTime();
|
||||
fLastTemplateSupportsSegwit = fSupportsSegwit;
|
||||
|
||||
// Create new block
|
||||
CScript scriptDummy = CScript() << OP_TRUE;
|
||||
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit);
|
||||
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy);
|
||||
if (!pblocktemplate)
|
||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||
|
||||
@ -682,7 +677,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
result.pushKV("bits", strprintf("%08x", pblock->nBits));
|
||||
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
|
||||
|
||||
if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) {
|
||||
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
|
||||
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash)
|
||||
CScript pubKey;
|
||||
pubKey << i++ << OP_TRUE;
|
||||
|
||||
auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey, false);
|
||||
auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
|
||||
auto pblock = std::make_shared<CBlock>(ptemplate->block);
|
||||
pblock->hashPrevBlock = prev_hash;
|
||||
pblock->nTime = ++time;
|
||||
|
@ -90,7 +90,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
|
||||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
||||
tmpl = self.nodes[0].getblocktemplate({})
|
||||
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
|
||||
assert(tmpl['sizelimit'] == 1000000)
|
||||
assert('weightlimit' not in tmpl)
|
||||
assert(tmpl['sigoplimit'] == 20000)
|
||||
@ -232,15 +232,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
assert(tx.wit.is_null())
|
||||
assert(txid3 in self.nodes[0].getrawmempool())
|
||||
|
||||
# Now try calling getblocktemplate() without segwit support.
|
||||
template = self.nodes[0].getblocktemplate()
|
||||
|
||||
# Check that tx1 is the only transaction of the 3 in the template.
|
||||
template_txids = [t['txid'] for t in template['transactions']]
|
||||
assert(txid2 not in template_txids and txid3 not in template_txids)
|
||||
assert(txid1 in template_txids)
|
||||
|
||||
# Check that running with segwit support results in all 3 being included.
|
||||
# Check that getblocktemplate includes all transactions.
|
||||
template = self.nodes[0].getblocktemplate({"rules": ["segwit"]})
|
||||
template_txids = [t['txid'] for t in template['transactions']]
|
||||
assert(txid1 in template_txids)
|
||||
|
@ -30,7 +30,7 @@ from test_framework.script import CScriptNum
|
||||
def assert_template(node, block, expect, rehash=True):
|
||||
if rehash:
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal'})
|
||||
rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal', 'rules': ['segwit']})
|
||||
assert_equal(rsp, expect)
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
|
||||
# Mine a block to leave initial block download
|
||||
node.generatetoaddress(1, node.get_deterministic_priv_key().address)
|
||||
tmpl = node.getblocktemplate()
|
||||
tmpl = node.getblocktemplate({'rules': ['segwit']})
|
||||
self.log.info("getblocktemplate: Test capability advertised")
|
||||
assert 'proposal' in tmpl['capabilities']
|
||||
assert 'coinbasetxn' not in tmpl
|
||||
@ -86,6 +86,9 @@ class MiningTest(BitcoinTestFramework):
|
||||
block.nNonce = 0
|
||||
block.vtx = [coinbase_tx]
|
||||
|
||||
self.log.info("getblocktemplate: segwit rule must be set")
|
||||
assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate)
|
||||
|
||||
self.log.info("getblocktemplate: Test valid block")
|
||||
assert_template(node, block, None)
|
||||
|
||||
@ -102,7 +105,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize()))
|
||||
|
||||
self.log.info("getblocktemplate: Test truncated final transaction")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'})
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit']})
|
||||
|
||||
self.log.info("getblocktemplate: Test duplicate transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
@ -132,7 +135,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
bad_block_sn = bytearray(block.serialize())
|
||||
assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1)
|
||||
bad_block_sn[TX_COUNT_OFFSET] += 1
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'})
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit']})
|
||||
|
||||
self.log.info("getblocktemplate: Test bad bits")
|
||||
bad_block = copy.deepcopy(block)
|
||||
|
@ -15,14 +15,14 @@ class LongpollThread(threading.Thread):
|
||||
def __init__(self, node):
|
||||
threading.Thread.__init__(self)
|
||||
# query current longpollid
|
||||
template = node.getblocktemplate()
|
||||
template = node.getblocktemplate({'rules': ['segwit']})
|
||||
self.longpollid = template['longpollid']
|
||||
# create a new connection to the node, we can't use the same
|
||||
# connection from two threads
|
||||
self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir)
|
||||
|
||||
def run(self):
|
||||
self.node.getblocktemplate({'longpollid':self.longpollid})
|
||||
self.node.getblocktemplate({'longpollid': self.longpollid, 'rules': ['segwit']})
|
||||
|
||||
class GetBlockTemplateLPTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -34,10 +34,10 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
|
||||
def run_test(self):
|
||||
self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.")
|
||||
self.nodes[0].generate(10)
|
||||
template = self.nodes[0].getblocktemplate()
|
||||
template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
|
||||
longpollid = template['longpollid']
|
||||
# longpollid should not change between successive invocations if nothing else happens
|
||||
template2 = self.nodes[0].getblocktemplate()
|
||||
template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']})
|
||||
assert(template2['longpollid'] == longpollid)
|
||||
|
||||
# Test 1: test that the longpolling wait if we do nothing
|
||||
|
@ -142,10 +142,10 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
|
||||
# getblocktemplate to (eventually) return a new block.
|
||||
mock_time = int(time.time())
|
||||
self.nodes[0].setmocktime(mock_time)
|
||||
template = self.nodes[0].getblocktemplate()
|
||||
template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
|
||||
self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN))
|
||||
self.nodes[0].setmocktime(mock_time+10)
|
||||
new_template = self.nodes[0].getblocktemplate()
|
||||
new_template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
|
||||
|
||||
assert(template != new_template)
|
||||
|
||||
|
@ -545,31 +545,13 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
@subtest
|
||||
def test_getblocktemplate_before_lockin(self):
|
||||
# Node0 is segwit aware, node2 is not.
|
||||
for node in [self.nodes[0], self.nodes[2]]:
|
||||
gbt_results = node.getblocktemplate()
|
||||
block_version = gbt_results['version']
|
||||
# If we're not indicating segwit support, we will still be
|
||||
# signalling for segwit activation.
|
||||
assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0])
|
||||
# If we don't specify the segwit rule, then we won't get a default
|
||||
# commitment.
|
||||
assert('default_witness_commitment' not in gbt_results)
|
||||
|
||||
# Workaround:
|
||||
# Can either change the tip, or change the mempool and wait 5 seconds
|
||||
# to trigger a recomputation of getblocktemplate.
|
||||
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
|
||||
# Using mocktime lets us avoid sleep()
|
||||
sync_mempools(self.nodes)
|
||||
self.nodes[0].setmocktime(int(time.time()) + 10)
|
||||
self.nodes[2].setmocktime(int(time.time()) + 10)
|
||||
|
||||
for node in [self.nodes[0], self.nodes[2]]:
|
||||
gbt_results = node.getblocktemplate({"rules": ["segwit"]})
|
||||
block_version = gbt_results['version']
|
||||
if node == self.nodes[2]:
|
||||
# If this is a non-segwit node, we should still not get a witness
|
||||
# If this is a non-segwit node, we should not get a witness
|
||||
# commitment, nor a version bit signalling segwit.
|
||||
assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
|
||||
assert('default_witness_commitment' not in gbt_results)
|
||||
@ -586,10 +568,6 @@ class SegWitTest(BitcoinTestFramework):
|
||||
script = get_witness_script(witness_root, 0)
|
||||
assert_equal(witness_commitment, bytes_to_hex_str(script))
|
||||
|
||||
# undo mocktime
|
||||
self.nodes[0].setmocktime(0)
|
||||
self.nodes[2].setmocktime(0)
|
||||
|
||||
@subtest
|
||||
def advance_to_segwit_lockin(self):
|
||||
"""Mine enough blocks to lock in segwit, but don't activate."""
|
||||
|
Loading…
Reference in New Issue
Block a user