Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bench_bench_dash_SOURCES = \
bench/pool.cpp \
bench/pow_hash.cpp \
bench/prevector.cpp \
bench/readblock.cpp \
bench/rollingbloom.cpp \
bench/rpc_blockchain.cpp \
bench/rpc_mempool.cpp \
Expand Down
53 changes: 53 additions & 0 deletions src/bench/readblock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bench/bench.h>
#include <bench/data.h>

#include <consensus/validation.h>
#include <node/blockstorage.h>
#include <streams.h>
#include <test/util/setup_common.h>
#include <util/chaintype.h>
#include <validation.h>

static FlatFilePos WriteBlockToDisk(ChainstateManager& chainman)
{
DataStream stream{benchmark::data::block413567};
CBlock block;
stream >> TX_WITH_WITNESS(block);

return chainman.m_blockman.SaveBlockToDisk(block, 0, nullptr);
}

static void ReadBlockFromDiskTest(benchmark::Bench& bench)
{
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
ChainstateManager& chainman{*testing_setup->m_node.chainman};

CBlock block;
const auto pos{WriteBlockToDisk(chainman)};

bench.run([&] {
const auto success{chainman.m_blockman.ReadBlockFromDisk(block, pos)};
assert(success);
});
}

static void ReadRawBlockFromDiskTest(benchmark::Bench& bench)
{
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
ChainstateManager& chainman{*testing_setup->m_node.chainman};

std::vector<uint8_t> block_data;
const auto pos{WriteBlockToDisk(chainman)};

bench.run([&] {
const auto success{chainman.m_blockman.ReadRawBlockFromDisk(block_data, pos)};
assert(success);
});
}

BENCHMARK(ReadBlockFromDiskTest, benchmark::PriorityLevel::HIGH);
BENCHMARK(ReadRawBlockFromDiskTest, benchmark::PriorityLevel::HIGH);
13 changes: 5 additions & 8 deletions src/rpc/mempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ RPCHelpMan sendrawtransaction()
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
"/kB.\nSet to 0 to accept any fee rate.\n"},
"/kB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate.\n"},
{"instantsend", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Deprecated and ignored"},
{"bypasslimits", RPCArg::Type::BOOL, RPCArg::Default{false}, "Bypass transaction policy limits"},
},
Expand Down Expand Up @@ -73,9 +73,7 @@ RPCHelpMan sendrawtransaction()
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));

const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate(AmountFromValue(request.params[1]));
const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>(1))};

int64_t virtual_size = GetVirtualTransactionSize(*tx);
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
Expand Down Expand Up @@ -111,7 +109,8 @@ static RPCHelpMan testmempoolaccept()
},
},
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
"/kB\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
},
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
Expand Down Expand Up @@ -155,9 +154,7 @@ static RPCHelpMan testmempoolaccept()
"Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
}

const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate(AmountFromValue(request.params[1]));
const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>(1))};

std::vector<CTransactionRef> txns;
txns.reserve(raw_transactions.size());
Expand Down
9 changes: 9 additions & 0 deletions src/rpc/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ CAmount AmountFromValue(const UniValue& value, int decimals)
return amount;
}

CFeeRate ParseFeeRate(const UniValue& json)
{
CAmount val{AmountFromValue(json)};
if (val >= COIN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee rates larger than or equal to 1BTC/kvB are not accepted");
return CFeeRate{val};
}

uint256 ParseHashV(const UniValue& v, std::string strName)
{
const std::string& strHex(v.get_str());
Expand Down Expand Up @@ -569,11 +576,13 @@ static void CheckRequiredOrDefault(const RPCArg& param)
void force_semicolon(ret_type)

// Optional arg (without default). Can also be called on required args, if needed.
TMPL_INST(nullptr, const UniValue*, maybe_arg;);
TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;);
TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;);
TMPL_INST(nullptr, const std::string*, maybe_arg ? &maybe_arg->get_str() : nullptr;);

// Required arg or optional arg with default value.
TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg););
TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););
TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););
TMPL_INST(CheckRequiredOrDefault, const std::string&, CHECK_NONFATAL(maybe_arg)->get_str(););
Expand Down
5 changes: 5 additions & 0 deletions src/rpc/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ bool ParseBoolV(const UniValue& v, const std::string &strName);
* @returns a CAmount if the various checks pass.
*/
CAmount AmountFromValue(const UniValue& value, int decimals = 8);
/**
* Parse a json number or string, denoting BTC/kvB, into a CFeeRate (sat/kvB).
* Reject negative values or rates larger than 1BTC/kvB.
*/
CFeeRate ParseFeeRate(const UniValue& json);

using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
std::string HelpExampleCli(const std::string& methodname, const std::string& args);
Expand Down
14 changes: 14 additions & 0 deletions test/functional/mempool_accept.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,23 @@ def run_test(self):
txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block)
self.generate(node, 1)
self.mempool_size = 0
# Also check feerate. 1BTC/kvB fails
assert_raises_rpc_error(-8, "Fee rates larger than or equal to 1BTC/kvB are not accepted", lambda: self.check_mempool_result(
result_expected=None,
rawtxs=[raw_tx_in_block],
maxfeerate=1,
))
# Check negative feerate
assert_raises_rpc_error(-3, "Amount out of range", lambda: self.check_mempool_result(
result_expected=None,
rawtxs=[raw_tx_in_block],
maxfeerate=-0.01,
))
# ... 0.99 passes
self.check_mempool_result(
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}],
rawtxs=[raw_tx_in_block],
maxfeerate=0.99,
)

self.log.info('A transaction not in the mempool')
Expand Down
Loading