mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
Merge gbt_rpc_options-28+knots
This commit is contained in:
commit
dc8fc35fc0
@ -40,13 +40,14 @@ public:
|
||||
virtual std::optional<uint256> getTipHash() = 0;
|
||||
|
||||
/**
|
||||
* Construct a new block template
|
||||
* Construct a new block template. For the createNewBlock variant, subclass options (if any) are silently lost and overridden by any config args. For createNewBlock2, the options are assumed to be complete.
|
||||
*
|
||||
* @param[in] script_pub_key the coinbase output
|
||||
* @param[in] options options for creating the block
|
||||
* @returns a block template
|
||||
*/
|
||||
virtual std::unique_ptr<node::CBlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0;
|
||||
virtual std::unique_ptr<node::CBlockTemplate> createNewBlock2(const CScript& script_pub_key, const node::BlockCreateOptions& assemble_options) = 0;
|
||||
|
||||
/**
|
||||
* Processes new block. A valid new block is automatically relayed to peers.
|
||||
|
@ -939,6 +939,11 @@ public:
|
||||
{
|
||||
BlockAssembler::Options assemble_options{options};
|
||||
ApplyArgsManOptions(*Assert(m_node.args), assemble_options);
|
||||
return createNewBlock2(script_pub_key, assemble_options);
|
||||
}
|
||||
|
||||
std::unique_ptr<CBlockTemplate> createNewBlock2(const CScript& script_pub_key, const BlockCreateOptions& assemble_options) override
|
||||
{
|
||||
return BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key);
|
||||
}
|
||||
|
||||
|
@ -65,11 +65,15 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
|
||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||
}
|
||||
|
||||
static BlockAssembler::Options ClampOptions(BlockAssembler::Options options)
|
||||
BlockCreateOptions BlockCreateOptions::Clamped() const
|
||||
{
|
||||
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);
|
||||
BlockAssembler::Options options = *this;
|
||||
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:
|
||||
@ -82,7 +86,7 @@ BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool
|
||||
: chainparams{chainstate.m_chainman.GetParams()},
|
||||
m_mempool{options.use_mempool ? mempool : nullptr},
|
||||
m_chainstate{chainstate},
|
||||
m_options{ClampOptions(options)}
|
||||
m_options{options.Clamped()}
|
||||
{
|
||||
// Whether we need to account for byte usage (in addition to weight usage)
|
||||
fNeedSizeAccounting = (options.nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE - 1000);
|
||||
|
@ -31,7 +31,6 @@ class ChainstateManager;
|
||||
namespace Consensus { struct Params; };
|
||||
|
||||
namespace node {
|
||||
static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
|
||||
|
||||
struct CBlockTemplate
|
||||
{
|
||||
@ -166,15 +165,7 @@ private:
|
||||
bool blockFinished;
|
||||
|
||||
public:
|
||||
struct Options : BlockCreateOptions {
|
||||
// Configuration parameters for the block size
|
||||
size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT};
|
||||
size_t nBlockMaxSize{DEFAULT_BLOCK_MAX_SIZE};
|
||||
CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
|
||||
// Whether to call TestBlockValidity() at the end of CreateNewBlock().
|
||||
bool test_block_validity{true};
|
||||
bool print_modified_fee{DEFAULT_PRINT_MODIFIED_FEE};
|
||||
};
|
||||
using Options = BlockCreateOptions;
|
||||
|
||||
explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options);
|
||||
|
||||
|
@ -13,6 +13,9 @@
|
||||
#ifndef BITCOIN_NODE_TYPES_H
|
||||
#define BITCOIN_NODE_TYPES_H
|
||||
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/policy.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace node {
|
||||
@ -27,6 +30,8 @@ enum class TransactionError {
|
||||
INVALID_PACKAGE,
|
||||
};
|
||||
|
||||
static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
|
||||
|
||||
struct BlockCreateOptions {
|
||||
/**
|
||||
* Set false to omit mempool transactions
|
||||
@ -49,6 +54,18 @@ struct BlockCreateOptions {
|
||||
* transaction outputs.
|
||||
*/
|
||||
size_t coinbase_output_max_additional_sigops{400};
|
||||
|
||||
// Configuration parameters for the block size
|
||||
size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT};
|
||||
size_t nBlockMaxSize{DEFAULT_BLOCK_MAX_SIZE};
|
||||
CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
|
||||
// Whether to call TestBlockValidity() at the end of CreateNewBlock().
|
||||
bool test_block_validity{true};
|
||||
bool print_modified_fee{DEFAULT_PRINT_MODIFIED_FEE};
|
||||
|
||||
BlockCreateOptions Clamped() const;
|
||||
|
||||
friend bool operator==(const BlockCreateOptions& a, const BlockCreateOptions& b) noexcept = default;
|
||||
};
|
||||
} // namespace node
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <script/signingprovider.h>
|
||||
#include <txmempool.h>
|
||||
#include <univalue.h>
|
||||
#include <util/check.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
@ -580,6 +581,8 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static UniValue TemplateToJSON(const Consensus::Params&, const ChainstateManager&, const CBlockTemplate*, const CBlockIndex*, const std::set<std::string>& setClientRules, unsigned int nTransactionsUpdatedLast);
|
||||
|
||||
static RPCHelpMan getblocktemplate()
|
||||
{
|
||||
return RPCHelpMan{"getblocktemplate",
|
||||
@ -594,9 +597,14 @@ static RPCHelpMan getblocktemplate()
|
||||
{"template_request", RPCArg::Type::OBJ, RPCArg::Optional::NO, "Format of the template",
|
||||
{
|
||||
{"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', 'serverlist', 'workid'"},
|
||||
{"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'skip_validity_test', 'serverlist', 'workid'"},
|
||||
}},
|
||||
{"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
|
||||
{
|
||||
@ -604,6 +612,7 @@ static RPCHelpMan getblocktemplate()
|
||||
{"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
|
||||
}},
|
||||
{"longpollid", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "delay processing request until the result would vary significantly from the \"longpollid\" of a prior template"},
|
||||
{"minfeerate", RPCArg::Type::NUM, RPCArg::DefaultHint{"set by -blockmintxfee"}, "only include transactions with a minimum sats/vbyte (disables template cache)"},
|
||||
{"data", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "proposed block data to check, encoded in hexadecimal; valid only for mode=\"proposal\""},
|
||||
},
|
||||
},
|
||||
@ -680,6 +689,14 @@ static RPCHelpMan getblocktemplate()
|
||||
LOCK(cs_main);
|
||||
uint256 tip{CHECK_NONFATAL(miner.getTipHash()).value()};
|
||||
|
||||
BlockAssembler::Options options;
|
||||
{
|
||||
const ArgsManager& args{EnsureAnyArgsman(request.context)};
|
||||
ApplyArgsManOptions(args, options);
|
||||
}
|
||||
const BlockAssembler::Options options_def{options.Clamped()};
|
||||
bool bypass_cache{false};
|
||||
|
||||
std::string strMode = "template";
|
||||
UniValue lpval = NullUniValue;
|
||||
std::set<std::string> setClientRules;
|
||||
@ -733,6 +750,39 @@ static RPCHelpMan getblocktemplate()
|
||||
setClientRules.insert(v.get_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!oparam["blockmaxsize"].isNull()) {
|
||||
options.nBlockMaxSize = oparam["blockmaxsize"].getInt<size_t>();
|
||||
}
|
||||
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 */};
|
||||
}
|
||||
options = options.Clamped();
|
||||
bypass_cache |= !(options == options_def);
|
||||
|
||||
// NOTE: Intentionally not setting bypass_cache for skip_validity_test since _using_ the cache is fine
|
||||
const UniValue& client_caps = oparam.find_value("capabilities");
|
||||
if (client_caps.isArray()) {
|
||||
for (unsigned int i = 0; i < client_caps.size(); ++i) {
|
||||
const UniValue& v = client_caps[i];
|
||||
if (!v.isStr()) continue;
|
||||
if (v.get_str() == "skip_validity_test") {
|
||||
options.test_block_validity = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strMode != "template")
|
||||
@ -817,8 +867,20 @@ static RPCHelpMan getblocktemplate()
|
||||
static int64_t time_start;
|
||||
static std::unique_ptr<CBlockTemplate> pblocktemplate;
|
||||
if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
|
||||
bypass_cache ||
|
||||
(miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
|
||||
{
|
||||
if (bypass_cache || !options.test_block_validity) {
|
||||
// Create one-off template unrelated to cache
|
||||
const auto tx_update_counter = miner.getTransactionsUpdated();
|
||||
CBlockIndex* const local_pindexPrev = chainman.m_blockman.LookupBlockIndex(tip);
|
||||
const CScript dummy_script{CScript() << OP_TRUE};
|
||||
auto tmpl = miner.createNewBlock2(dummy_script, options);
|
||||
if (!tmpl) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||
return TemplateToJSON(consensusParams, chainman, &*tmpl, local_pindexPrev, setClientRules, tx_update_counter);
|
||||
}
|
||||
CHECK_NONFATAL(options == options_def);
|
||||
|
||||
// Clear pindexPrev so future calls make a new block, despite any failures from here on
|
||||
pindexPrev = nullptr;
|
||||
|
||||
@ -844,6 +906,14 @@ static RPCHelpMan getblocktemplate()
|
||||
UpdateTime(pblock, consensusParams, pindexPrev);
|
||||
pblock->nNonce = 0;
|
||||
|
||||
return TemplateToJSON(consensusParams, chainman, &*pblocktemplate, pindexPrev, setClientRules, nTransactionsUpdatedLast);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static UniValue TemplateToJSON(const Consensus::Params& consensusParams, const ChainstateManager& chainman, const CBlockTemplate* const pblocktemplate, const CBlockIndex* const pindexPrev, const std::set<std::string>& setClientRules, const unsigned int nTransactionsUpdatedLast) {
|
||||
const CBlock* const pblock = &pblocktemplate->block;
|
||||
|
||||
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
|
||||
const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
|
||||
|
||||
@ -911,6 +981,7 @@ static RPCHelpMan getblocktemplate()
|
||||
aRules.push_back("!signet");
|
||||
}
|
||||
|
||||
auto block_version = pblock->nVersion;
|
||||
UniValue vbavailable(UniValue::VOBJ);
|
||||
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
||||
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
|
||||
@ -922,7 +993,7 @@ static RPCHelpMan getblocktemplate()
|
||||
break;
|
||||
case ThresholdState::LOCKED_IN:
|
||||
// Ensure bit is set in block version
|
||||
pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
|
||||
block_version |= chainman.m_versionbitscache.Mask(consensusParams, pos);
|
||||
[[fallthrough]];
|
||||
case ThresholdState::STARTED:
|
||||
{
|
||||
@ -931,7 +1002,7 @@ static RPCHelpMan getblocktemplate()
|
||||
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
|
||||
if (!vbinfo.gbt_force) {
|
||||
// If the client doesn't support this, don't indicate it in the [default] version
|
||||
pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
|
||||
block_version &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -951,7 +1022,7 @@ static RPCHelpMan getblocktemplate()
|
||||
}
|
||||
}
|
||||
}
|
||||
result.pushKV("version", pblock->nVersion);
|
||||
result.pushKV("version", block_version);
|
||||
result.pushKV("rules", std::move(aRules));
|
||||
result.pushKV("vbavailable", std::move(vbavailable));
|
||||
result.pushKV("vbrequired", int(0));
|
||||
@ -960,7 +1031,7 @@ static RPCHelpMan getblocktemplate()
|
||||
result.pushKV("transactions", std::move(transactions));
|
||||
result.pushKV("coinbaseaux", std::move(aux));
|
||||
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
|
||||
result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast));
|
||||
result.pushKV("longpollid", pindexPrev->GetBlockHash().GetHex() + ToString(nTransactionsUpdatedLast));
|
||||
result.pushKV("target", hashTarget.GetHex());
|
||||
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
|
||||
result.pushKV("mutable", std::move(aMutable));
|
||||
@ -991,8 +1062,6 @@ static RPCHelpMan getblocktemplate()
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class submitblock_StateCatcher final : public CValidationInterface
|
||||
|
@ -16,6 +16,7 @@ from test_framework.blocktools import (
|
||||
get_witness_script,
|
||||
NORMAL_GBT_REQUEST_PARAMS,
|
||||
TIME_GENESIS_BLOCK,
|
||||
WITNESS_SCALE_FACTOR,
|
||||
)
|
||||
from test_framework.messages import (
|
||||
BLOCK_HEADER_SIZE,
|
||||
@ -32,15 +33,18 @@ 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
|
||||
MAX_FUTURE_BLOCK_TIME = 2 * 3600
|
||||
MAX_TIMEWARP = 600
|
||||
ASSUMED_BLOCK_OVERHEAD_SIZE = 1000
|
||||
ASSUMED_BLOCK_OVERHEAD_WEIGHT = ASSUMED_BLOCK_OVERHEAD_SIZE * WITNESS_SCALE_FACTOR
|
||||
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):
|
||||
@ -81,15 +85,21 @@ class MiningTest(BitcoinTestFramework):
|
||||
self.restart_node(0)
|
||||
self.connect_nodes(0, 1)
|
||||
|
||||
def test_blockmintxfee_parameter(self):
|
||||
self.log.info("Test -blockmintxfee setting")
|
||||
def test_blockmintxfee_parameter(self, *, use_rpc=False):
|
||||
if not use_rpc:
|
||||
self.log.info("Test -blockmintxfee setting")
|
||||
self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
|
||||
node = self.nodes[0]
|
||||
|
||||
# test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
|
||||
for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
|
||||
blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
|
||||
if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
|
||||
if use_rpc:
|
||||
blockmintxfee_sat_vb = blockmintxfee_sat_kvb / 1000
|
||||
self.log.info(f"-> Test RPC param minfeerate={blockmintxfee_sat_vb} ({blockmintxfee_sat_kvb} sat/kvB)...")
|
||||
self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
|
||||
self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
|
||||
elif blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
|
||||
self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
|
||||
else:
|
||||
blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
|
||||
@ -109,16 +119,124 @@ class MiningTest(BitcoinTestFramework):
|
||||
node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
|
||||
|
||||
# check that tx below specified fee-rate is neither in template nor in the actual block
|
||||
block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
|
||||
req = NORMAL_GBT_REQUEST_PARAMS
|
||||
if use_rpc:
|
||||
req = copy.deepcopy(req)
|
||||
req['minfeerate'] = blockmintxfee_sat_vb
|
||||
block_template = node.getblocktemplate(req)
|
||||
block_template_txids = [tx['txid'] for tx in block_template['transactions']]
|
||||
self.generate(self.wallet, 1, sync_fun=self.no_op)
|
||||
block = node.getblock(node.getbestblockhash(), verbosity=2)
|
||||
block_txids = [tx['txid'] for tx in block['tx']]
|
||||
|
||||
assert tx_with_min_feerate['txid'] in block_template_txids
|
||||
assert tx_with_min_feerate['txid'] in block_txids
|
||||
assert tx_below_min_feerate['txid'] not in block_template_txids
|
||||
assert tx_below_min_feerate['txid'] not in block_txids
|
||||
|
||||
if not use_rpc:
|
||||
assert tx_with_min_feerate['txid'] in block_txids
|
||||
assert tx_below_min_feerate['txid'] not in block_txids
|
||||
|
||||
def test_rpc_params(self):
|
||||
self.log.info("Test minfeerate RPC param")
|
||||
self.test_blockmintxfee_parameter(use_rpc=True)
|
||||
|
||||
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
|
||||
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)
|
||||
normal_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in self.nodes[0].getblocktemplate(req)['transactions']) // 2)
|
||||
last_size = ASSUMED_BLOCK_OVERHEAD_SIZE
|
||||
for target_size in (50000, 100000, 150000):
|
||||
self.log.info(f"-> Test RPC param blockmaxsize={target_size}...")
|
||||
req['blockmaxsize'] = target_size
|
||||
tmpl = self.nodes[0].getblocktemplate(req)
|
||||
blk_size = ASSUMED_BLOCK_OVERHEAD_SIZE + (sum(len(tx['data']) for tx in tmpl['transactions']) // 2)
|
||||
assert blk_size < normal_size
|
||||
assert blk_size < target_size
|
||||
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'])
|
||||
last_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT
|
||||
for target_weight in (200000, 400000, 600000):
|
||||
self.log.info(f"-> Test RPC param blockmaxweight={target_weight}...")
|
||||
req['blockmaxweight'] = target_weight
|
||||
tmpl = self.nodes[0].getblocktemplate(req)
|
||||
blk_weight = ASSUMED_BLOCK_OVERHEAD_WEIGHT + sum(tx['weight'] for tx in tmpl['transactions'])
|
||||
assert blk_weight < normal_weight
|
||||
assert blk_weight < target_weight
|
||||
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)")
|
||||
@ -171,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):
|
||||
@ -377,6 +495,7 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
|
||||
|
||||
self.test_blockmintxfee_parameter()
|
||||
self.test_rpc_params()
|
||||
self.test_timewarp()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user