Skip to content

Commit

Permalink
Merge bitcoin#14811: Mining: Enforce that segwit option must be set i…
Browse files Browse the repository at this point in the history
…n GBT

d2ce315 [docs] add release note for change to GBT (John Newbery)
0025c9e [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
  • Loading branch information
MarcoFalke committed Dec 21, 2018
2 parents 45fe390 + d2ce315 commit feda41e
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 62 deletions.
9 changes: 9 additions & 0 deletions doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------------------

Expand Down Expand Up @@ -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
=================

Expand Down
2 changes: 1 addition & 1 deletion src/bench/block_assemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/miner.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class BlockAssembler
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
Expand Down
23 changes: 9 additions & 14 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,15 +311,15 @@ 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",
{
{"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"},
},
Expand Down Expand Up @@ -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;
Expand All @@ -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");

Expand Down Expand Up @@ -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()));
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/validation_block_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 2 additions & 10 deletions test/functional/feature_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def run_test(self):

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)
Expand Down Expand Up @@ -232,15 +232,7 @@ def run_test(self):
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)
Expand Down
11 changes: 7 additions & 4 deletions test/functional/mining_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
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)


Expand Down Expand Up @@ -60,7 +60,7 @@ def assert_submitblock(block, result_str_1, result_str_2=None):

# 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
Expand All @@ -86,6 +86,9 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
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)

Expand All @@ -102,7 +105,7 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
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)
Expand Down Expand Up @@ -132,7 +135,7 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
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)
Expand Down
8 changes: 4 additions & 4 deletions test/functional/mining_getblocktemplate_longpoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -34,10 +34,10 @@ def skip_test_if_missing_module(self):
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
Expand Down
4 changes: 2 additions & 2 deletions test/functional/mining_prioritisetransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ def run_test(self):
# 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)

Expand Down
24 changes: 1 addition & 23 deletions test/functional/p2p_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,31 +545,13 @@ def advance_to_segwit_started(self):

@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)
Expand All @@ -586,10 +568,6 @@ def test_getblocktemplate_before_lockin(self):
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."""
Expand Down

0 comments on commit feda41e

Please sign in to comment.