Skip to content

Commit 0025c9e

Browse files
committed
[mining] segwit option must be set in GBT
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.
1 parent 5f23460 commit 0025c9e

File tree

10 files changed

+30
-62
lines changed

10 files changed

+30
-62
lines changed

src/bench/block_assemble.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey
2727
{
2828
auto block = std::make_shared<CBlock>(
2929
BlockAssembler{Params()}
30-
.CreateNewBlock(coinbase_scriptPubKey, /* fMineWitnessTx */ true)
30+
.CreateNewBlock(coinbase_scriptPubKey)
3131
->block);
3232

3333
block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;

src/miner.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void BlockAssembler::resetBlock()
9595
nFees = 0;
9696
}
9797

98-
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx)
98+
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
9999
{
100100
int64_t nTimeStart = GetTimeMicros();
101101

@@ -139,7 +139,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
139139
// not activated.
140140
// TODO: replace this with a call to main to assess validity of a mempool
141141
// transaction (which in most cases can be a no-op).
142-
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx;
142+
fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());
143143

144144
int nPackagesSelected = 0;
145145
int nDescendantsUpdated = 0;

src/miner.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class BlockAssembler
157157
BlockAssembler(const CChainParams& params, const Options& options);
158158

159159
/** Construct a new block template with coinbase to scriptPubKeyIn */
160-
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, bool fMineWitnessTx=true);
160+
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
161161

162162
private:
163163
// utility functions

src/rpc/mining.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,15 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
311311
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
312312
" https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
313313
{
314-
{"template_request", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "A json object in the following spec",
314+
{"template_request", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "A json object in the following spec",
315315
{
316316
{"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
317317
{"capabilities", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
318318
{
319319
{"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
320320
},
321321
},
322-
{"rules", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A list of strings",
322+
{"rules", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A list of strings",
323323
{
324324
{"support", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "client side supported softfork deployment"},
325325
},
@@ -503,21 +503,17 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
503503
}
504504

505505
const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
506-
// If the caller is indicating segwit support, then allow CreateNewBlock()
507-
// to select witness transactions, after segwit activates (otherwise
508-
// don't).
509-
bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end();
506+
// GBT must be called with 'segwit' set in the rules
507+
if (setClientRules.count(segwit_info.name) != 1) {
508+
throw JSONRPCError(RPC_INVALID_PARAMETER, "getblocktemplate must be called with the segwit rule set (call with {\"rules\": [\"segwit\"]})");
509+
}
510510

511511
// Update block
512512
static CBlockIndex* pindexPrev;
513513
static int64_t nStart;
514514
static std::unique_ptr<CBlockTemplate> pblocktemplate;
515-
// Cache whether the last invocation was with segwit support, to avoid returning
516-
// a segwit-block to a non-segwit caller.
517-
static bool fLastTemplateSupportsSegwit = true;
518515
if (pindexPrev != chainActive.Tip() ||
519-
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) ||
520-
fLastTemplateSupportsSegwit != fSupportsSegwit)
516+
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
521517
{
522518
// Clear pindexPrev so future calls make a new block, despite any failures from here on
523519
pindexPrev = nullptr;
@@ -526,11 +522,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
526522
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
527523
CBlockIndex* pindexPrevNew = chainActive.Tip();
528524
nStart = GetTime();
529-
fLastTemplateSupportsSegwit = fSupportsSegwit;
530525

531526
// Create new block
532527
CScript scriptDummy = CScript() << OP_TRUE;
533-
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit);
528+
pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy);
534529
if (!pblocktemplate)
535530
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
536531

@@ -682,7 +677,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
682677
result.pushKV("bits", strprintf("%08x", pblock->nBits));
683678
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
684679

685-
if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) {
680+
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
686681
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
687682
}
688683

src/test/validation_block_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ std::shared_ptr<CBlock> Block(const uint256& prev_hash)
5454
CScript pubKey;
5555
pubKey << i++ << OP_TRUE;
5656

57-
auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey, false);
57+
auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
5858
auto pblock = std::make_shared<CBlock>(ptemplate->block);
5959
pblock->hashPrevBlock = prev_hash;
6060
pblock->nTime = ++time;

test/functional/feature_segwit.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def run_test(self):
9090

9191
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
9292
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
93-
tmpl = self.nodes[0].getblocktemplate({})
93+
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
9494
assert(tmpl['sizelimit'] == 1000000)
9595
assert('weightlimit' not in tmpl)
9696
assert(tmpl['sigoplimit'] == 20000)
@@ -232,15 +232,7 @@ def run_test(self):
232232
assert(tx.wit.is_null())
233233
assert(txid3 in self.nodes[0].getrawmempool())
234234

235-
# Now try calling getblocktemplate() without segwit support.
236-
template = self.nodes[0].getblocktemplate()
237-
238-
# Check that tx1 is the only transaction of the 3 in the template.
239-
template_txids = [t['txid'] for t in template['transactions']]
240-
assert(txid2 not in template_txids and txid3 not in template_txids)
241-
assert(txid1 in template_txids)
242-
243-
# Check that running with segwit support results in all 3 being included.
235+
# Check that getblocktemplate includes all transactions.
244236
template = self.nodes[0].getblocktemplate({"rules": ["segwit"]})
245237
template_txids = [t['txid'] for t in template['transactions']]
246238
assert(txid1 in template_txids)

test/functional/mining_basic.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
def assert_template(node, block, expect, rehash=True):
3131
if rehash:
3232
block.hashMerkleRoot = block.calc_merkle_root()
33-
rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal'})
33+
rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal', 'rules': ['segwit']})
3434
assert_equal(rsp, expect)
3535

3636

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

6161
# Mine a block to leave initial block download
6262
node.generatetoaddress(1, node.get_deterministic_priv_key().address)
63-
tmpl = node.getblocktemplate()
63+
tmpl = node.getblocktemplate({'rules': ['segwit']})
6464
self.log.info("getblocktemplate: Test capability advertised")
6565
assert 'proposal' in tmpl['capabilities']
6666
assert 'coinbasetxn' not in tmpl
@@ -86,6 +86,9 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
8686
block.nNonce = 0
8787
block.vtx = [coinbase_tx]
8888

89+
self.log.info("getblocktemplate: segwit rule must be set")
90+
assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate)
91+
8992
self.log.info("getblocktemplate: Test valid block")
9093
assert_template(node, block, None)
9194

@@ -102,7 +105,7 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
102105
assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize()))
103106

104107
self.log.info("getblocktemplate: Test truncated final transaction")
105-
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'})
108+
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal', 'rules': ['segwit']})
106109

107110
self.log.info("getblocktemplate: Test duplicate transaction")
108111
bad_block = copy.deepcopy(block)
@@ -132,7 +135,7 @@ def assert_submitblock(block, result_str_1, result_str_2=None):
132135
bad_block_sn = bytearray(block.serialize())
133136
assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1)
134137
bad_block_sn[TX_COUNT_OFFSET] += 1
135-
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'})
138+
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal', 'rules': ['segwit']})
136139

137140
self.log.info("getblocktemplate: Test bad bits")
138141
bad_block = copy.deepcopy(block)

test/functional/mining_getblocktemplate_longpoll.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ class LongpollThread(threading.Thread):
1515
def __init__(self, node):
1616
threading.Thread.__init__(self)
1717
# query current longpollid
18-
template = node.getblocktemplate()
18+
template = node.getblocktemplate({'rules': ['segwit']})
1919
self.longpollid = template['longpollid']
2020
# create a new connection to the node, we can't use the same
2121
# connection from two threads
2222
self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir)
2323

2424
def run(self):
25-
self.node.getblocktemplate({'longpollid':self.longpollid})
25+
self.node.getblocktemplate({'longpollid': self.longpollid, 'rules': ['segwit']})
2626

2727
class GetBlockTemplateLPTest(BitcoinTestFramework):
2828
def set_test_params(self):
@@ -34,10 +34,10 @@ def skip_test_if_missing_module(self):
3434
def run_test(self):
3535
self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.")
3636
self.nodes[0].generate(10)
37-
template = self.nodes[0].getblocktemplate()
37+
template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
3838
longpollid = template['longpollid']
3939
# longpollid should not change between successive invocations if nothing else happens
40-
template2 = self.nodes[0].getblocktemplate()
40+
template2 = self.nodes[0].getblocktemplate({'rules': ['segwit']})
4141
assert(template2['longpollid'] == longpollid)
4242

4343
# Test 1: test that the longpolling wait if we do nothing

test/functional/mining_prioritisetransaction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,10 @@ def run_test(self):
142142
# getblocktemplate to (eventually) return a new block.
143143
mock_time = int(time.time())
144144
self.nodes[0].setmocktime(mock_time)
145-
template = self.nodes[0].getblocktemplate()
145+
template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
146146
self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=-int(self.relayfee*COIN))
147147
self.nodes[0].setmocktime(mock_time+10)
148-
new_template = self.nodes[0].getblocktemplate()
148+
new_template = self.nodes[0].getblocktemplate({'rules': ['segwit']})
149149

150150
assert(template != new_template)
151151

test/functional/p2p_segwit.py

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -545,31 +545,13 @@ def advance_to_segwit_started(self):
545545

546546
@subtest
547547
def test_getblocktemplate_before_lockin(self):
548-
# Node0 is segwit aware, node2 is not.
549-
for node in [self.nodes[0], self.nodes[2]]:
550-
gbt_results = node.getblocktemplate()
551-
block_version = gbt_results['version']
552-
# If we're not indicating segwit support, we will still be
553-
# signalling for segwit activation.
554-
assert_equal((block_version & (1 << VB_WITNESS_BIT) != 0), node == self.nodes[0])
555-
# If we don't specify the segwit rule, then we won't get a default
556-
# commitment.
557-
assert('default_witness_commitment' not in gbt_results)
558-
559-
# Workaround:
560-
# Can either change the tip, or change the mempool and wait 5 seconds
561-
# to trigger a recomputation of getblocktemplate.
562548
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
563-
# Using mocktime lets us avoid sleep()
564-
sync_mempools(self.nodes)
565-
self.nodes[0].setmocktime(int(time.time()) + 10)
566-
self.nodes[2].setmocktime(int(time.time()) + 10)
567549

568550
for node in [self.nodes[0], self.nodes[2]]:
569551
gbt_results = node.getblocktemplate({"rules": ["segwit"]})
570552
block_version = gbt_results['version']
571553
if node == self.nodes[2]:
572-
# If this is a non-segwit node, we should still not get a witness
554+
# If this is a non-segwit node, we should not get a witness
573555
# commitment, nor a version bit signalling segwit.
574556
assert_equal(block_version & (1 << VB_WITNESS_BIT), 0)
575557
assert('default_witness_commitment' not in gbt_results)
@@ -586,10 +568,6 @@ def test_getblocktemplate_before_lockin(self):
586568
script = get_witness_script(witness_root, 0)
587569
assert_equal(witness_commitment, bytes_to_hex_str(script))
588570

589-
# undo mocktime
590-
self.nodes[0].setmocktime(0)
591-
self.nodes[2].setmocktime(0)
592-
593571
@subtest
594572
def advance_to_segwit_lockin(self):
595573
"""Mine enough blocks to lock in segwit, but don't activate."""

0 commit comments

Comments
 (0)