mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
RPC/Mining: getblocktemplate: Support overriding blockreserved{sigops,size,weight} per request
This commit is contained in:
parent
9d3fa237bb
commit
2fe5723811
@ -68,9 +68,12 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
|
||||
BlockCreateOptions BlockCreateOptions::Clamped() const
|
||||
{
|
||||
BlockAssembler::Options options = *this;
|
||||
Assert(options.coinbase_max_additional_size <= MAX_BLOCK_SERIALIZED_SIZE - 1000);
|
||||
Assert(options.coinbase_max_additional_weight <= DEFAULT_BLOCK_MAX_WEIGHT);
|
||||
Assert(options.coinbase_output_max_additional_sigops <= MAX_BLOCK_SIGOPS_COST);
|
||||
constexpr size_t theoretical_min_gentx_sz = 1+4+1+36+1+1+4+1+4;
|
||||
constexpr size_t theoretical_min_gentx_weight = theoretical_min_gentx_sz * WITNESS_SCALE_FACTOR;
|
||||
CHECK_NONFATAL(options.coinbase_max_additional_size <= MAX_BLOCK_SERIALIZED_SIZE - 1000);
|
||||
CHECK_NONFATAL(options.coinbase_max_additional_weight <= DEFAULT_BLOCK_MAX_WEIGHT);
|
||||
CHECK_NONFATAL(options.coinbase_max_additional_weight >= theoretical_min_gentx_weight);
|
||||
CHECK_NONFATAL(options.coinbase_output_max_additional_sigops <= MAX_BLOCK_SIGOPS_COST);
|
||||
// Limit size to between coinbase_max_additional_size and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity:
|
||||
options.nBlockMaxSize = std::clamp<size_t>(options.nBlockMaxSize, options.coinbase_max_additional_size, MAX_BLOCK_SERIALIZED_SIZE - 1000);
|
||||
// Limit weight to between coinbase_max_additional_weight and DEFAULT_BLOCK_MAX_WEIGHT for sanity:
|
||||
|
@ -586,6 +586,9 @@ static RPCHelpMan getblocktemplate()
|
||||
{"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
|
||||
{"blockmaxsize", RPCArg::Type::NUM, RPCArg::DefaultHint{"set by -blockmaxsize"}, "limit returned block to specified size (disables template cache)"},
|
||||
{"blockmaxweight", RPCArg::Type::NUM, RPCArg::DefaultHint{"set by -blockmaxweight"}, "limit returned block to specified weight (disables template cache)"},
|
||||
{"blockreservedsigops", RPCArg::Type::NUM, RPCArg::Default{node::BlockCreateOptions{}.coinbase_output_max_additional_sigops}, "reserve specified number of sigops in returned block for generation transaction (disables template cache)"},
|
||||
{"blockreservedsize", RPCArg::Type::NUM, RPCArg::Default{node::BlockCreateOptions{}.coinbase_max_additional_size}, "reserve specified size in returned block for generation transaction (disables template cache)"},
|
||||
{"blockreservedweight", RPCArg::Type::NUM, RPCArg::Default{node::BlockCreateOptions{}.coinbase_max_additional_weight}, "reserve specified weight in returned block for generation transaction (disables template cache)"},
|
||||
{"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED, "A list of strings",
|
||||
{
|
||||
{"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'skip_validity_test', 'serverlist', 'workid'"},
|
||||
@ -740,6 +743,15 @@ static RPCHelpMan getblocktemplate()
|
||||
if (!oparam["blockmaxweight"].isNull()) {
|
||||
options.nBlockMaxWeight = oparam["blockmaxweight"].getInt<size_t>();
|
||||
}
|
||||
if (!oparam["blockreservedsize"].isNull()) {
|
||||
options.coinbase_max_additional_size = oparam["blockreservedsize"].getInt<size_t>();
|
||||
}
|
||||
if (!oparam["blockreservedweight"].isNull()) {
|
||||
options.coinbase_max_additional_weight = oparam["blockreservedweight"].getInt<size_t>();
|
||||
}
|
||||
if (!oparam["blockreservedsigops"].isNull()) {
|
||||
options.coinbase_output_max_additional_sigops = oparam["blockreservedsigops"].getInt<size_t>();
|
||||
}
|
||||
if (!oparam["minfeerate"].isNull()) {
|
||||
options.blockMinFeeRate = CFeeRate{AmountFromValue(oparam["minfeerate"]), COIN /* sat/vB */};
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
get_fee,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
from test_framework.wallet import MiniWallet, MiniWalletMode
|
||||
|
||||
|
||||
DIFFICULTY_ADJUSTMENT_INTERVAL = 144
|
||||
@ -44,6 +44,7 @@ ASSUMED_BLOCK_OVERHEAD_WEIGHT = ASSUMED_BLOCK_OVERHEAD_SIZE * WITNESS_SCALE_FACT
|
||||
VERSIONBITS_TOP_BITS = 0x20000000
|
||||
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
|
||||
DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
|
||||
MAX_SIGOP_COST = 80000
|
||||
|
||||
|
||||
def assert_template(node, block, expect, rehash=True):
|
||||
@ -142,13 +143,25 @@ class MiningTest(BitcoinTestFramework):
|
||||
node = self.nodes[0]
|
||||
|
||||
self.log.info("Preparing mempool")
|
||||
self.restart_node(0, extra_args=['-limitancestorcount=1000', '-limitancestorsize=7000', '-limitdescendantcount=1000', '-limitdescendantsize=7000'])
|
||||
|
||||
# Fill the mempool
|
||||
while node.getmempoolinfo()['bytes'] < 200000:
|
||||
self.wallet.send_self_transfer_multi(
|
||||
target_mempool_size = 200000
|
||||
last_tx_size = 0
|
||||
utxo = self.wallet.get_utxo() # save for small coins
|
||||
while node.getmempoolinfo()['bytes'] < target_mempool_size - last_tx_size:
|
||||
tx = self.wallet.send_self_transfer_multi(
|
||||
from_node=self.nodes[0],
|
||||
num_outputs=1000,
|
||||
)
|
||||
last_tx_size = len(tx['hex']) / 2
|
||||
while node.getmempoolinfo()['bytes'] < 200000:
|
||||
tx = self.wallet.send_self_transfer_multi(
|
||||
utxos_to_spend=[utxo],
|
||||
from_node=node,
|
||||
num_outputs=1,
|
||||
)
|
||||
utxo = tx['new_utxos'][0]
|
||||
|
||||
self.log.info("Test blockmaxsize RPC param")
|
||||
req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
|
||||
@ -164,6 +177,21 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert blk_size > last_size
|
||||
last_size = blk_size
|
||||
|
||||
self.log.info("Test blockreservedsize RPC param")
|
||||
req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
|
||||
req['blockmaxsize'] = 150000
|
||||
normal_size = (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
|
||||
last_size = 0
|
||||
for reserved_size in (100000, 10000, 100):
|
||||
self.log.info(f"-> Test RPC param blockreservedsize={reserved_size}...")
|
||||
req['blockreservedsize'] = reserved_size
|
||||
tmpl = self.nodes[0].getblocktemplate(req)
|
||||
blk_size = (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
|
||||
assert blk_size < normal_size if reserved_size > 1000 else blk_size > normal_size
|
||||
assert blk_size + reserved_size <= req['blockmaxsize']
|
||||
assert blk_size > last_size
|
||||
last_size = blk_size
|
||||
|
||||
self.log.info("Test blockmaxweight RPC param")
|
||||
req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
|
||||
normal_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
|
||||
@ -178,6 +206,38 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert blk_weight > last_weight
|
||||
last_weight = blk_weight
|
||||
|
||||
self.log.info("Test blockreservedweight RPC param")
|
||||
req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
|
||||
req['blockmaxweight'] = 600000
|
||||
normal_weight = sum(tx['weight'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
|
||||
last_weight = 0
|
||||
for reserved_weight in (400000, 40000, 400):
|
||||
self.log.info(f"-> Test RPC param blockreservedweight={reserved_weight}...")
|
||||
req['blockreservedweight'] = reserved_weight
|
||||
tmpl = self.nodes[0].getblocktemplate(req)
|
||||
blk_weight = sum(tx['weight'] for tx in tmpl['transactions'])
|
||||
assert blk_weight < normal_weight if reserved_weight > 4000 else blk_weight > normal_weight
|
||||
assert blk_weight + reserved_weight <= req['blockmaxweight']
|
||||
assert blk_weight > last_weight
|
||||
last_weight = blk_weight
|
||||
|
||||
self.log.info("Test blockreservedsigops RPC param")
|
||||
req = copy.deepcopy(NORMAL_GBT_REQUEST_PARAMS)
|
||||
normal_sigops = sum(tx['sigops'] for tx in self.nodes[0].getblocktemplate(req)['transactions'])
|
||||
assert normal_sigops
|
||||
last_sigops = 0
|
||||
baseline_sigops = MAX_SIGOP_COST - normal_sigops
|
||||
for reserved_sigops in (800, 400, 100):
|
||||
reserved_sigops += baseline_sigops
|
||||
self.log.info(f"-> Test RPC param blockreservedsigops={reserved_sigops}...")
|
||||
req['blockreservedsigops'] = reserved_sigops
|
||||
tmpl = self.nodes[0].getblocktemplate(req)
|
||||
blk_sigops = sum(tx['sigops'] for tx in tmpl['transactions'])
|
||||
assert blk_sigops < normal_sigops if reserved_sigops > 400 else blk_sigops > normal_sigops
|
||||
assert blk_sigops + reserved_sigops <= MAX_SIGOP_COST
|
||||
assert blk_sigops > last_sigops
|
||||
last_sigops = blk_sigops
|
||||
|
||||
def test_timewarp(self):
|
||||
self.log.info("Test timewarp attack mitigation (BIP94)")
|
||||
node = self.nodes[0]
|
||||
@ -229,7 +289,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
self.wallet = MiniWallet(node)
|
||||
self.wallet = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
|
||||
self.mine_chain()
|
||||
|
||||
def assert_submitblock(block, result_str_1, result_str_2=None):
|
||||
|
Loading…
Reference in New Issue
Block a user