Skip to content

Commit

Permalink
Port consensus
Browse files Browse the repository at this point in the history
  • Loading branch information
timemarkovqtum committed Jul 31, 2024
1 parent 7e0cadd commit c3e90ef
Show file tree
Hide file tree
Showing 19 changed files with 300 additions and 106 deletions.
9 changes: 5 additions & 4 deletions src/bench/block_assemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ static void AssembleBlock(benchmark::Bench& bench)
witness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE);

// Collect some loose transactions that spend the coinbases of our mined blocks
constexpr size_t NUM_BLOCKS{200};
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
constexpr size_t NUM_BLOCKS{2100};
constexpr size_t coinbaseMaturity = 2000;
std::array<CTransactionRef, NUM_BLOCKS - coinbaseMaturity + 1> txs;
for (size_t b{0}; b < NUM_BLOCKS; ++b) {
CMutableTransaction tx;
tx.vin.emplace_back(MineBlock(test_setup->m_node, P2WSH_OP_TRUE));
tx.vin.back().scriptWitness = witness;
tx.vout.emplace_back(1337, P2WSH_OP_TRUE);
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
tx.vout.emplace_back(101337, P2WSH_OP_TRUE);
if (NUM_BLOCKS - b >= coinbaseMaturity)
txs.at(b) = MakeTransactionRef(tx);
}
{
Expand Down
2 changes: 1 addition & 1 deletion src/bench/duplicate_inputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static void DuplicateInputs(benchmark::Bench& bench)
naughtyTx.vout[0].nValue = 0;
naughtyTx.vout[0].scriptPubKey = SCRIPT_PUB;

uint64_t n_inputs = (((MAX_BLOCK_SERIALIZED_SIZE / WITNESS_SCALE_FACTOR) - (CTransaction(coinbaseTx).GetTotalSize() + CTransaction(naughtyTx).GetTotalSize())) / 41) - 100;
uint64_t n_inputs = (((dgpMaxBlockSerSize / WITNESS_SCALE_FACTOR) - (CTransaction(coinbaseTx).GetTotalSize() + CTransaction(naughtyTx).GetTotalSize())) / 41) - 100;
for (uint64_t x = 0; x < (n_inputs - 1); ++x) {
naughtyTx.vin.emplace_back(Txid::FromUint256(GetRandHash()), 0, CScript(), 0);
}
Expand Down
14 changes: 8 additions & 6 deletions src/bench/wallet_create_tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void generateFakeBlock(const CChainParams& params,
coinbase_tx.vin[0].prevout.SetNull();
coinbase_tx.vout.resize(2);
coinbase_tx.vout[0].scriptPubKey = coinbase_out_script;
coinbase_tx.vout[0].nValue = 49 * COIN;
coinbase_tx.vout[0].nValue = 19999 * COIN;
coinbase_tx.vin[0].scriptSig = CScript() << ++tip.tip_height << OP_0;
coinbase_tx.vout[1].scriptPubKey = coinbase_out_script; // extra output
coinbase_tx.vout[1].nValue = 1 * COIN;
Expand Down Expand Up @@ -104,7 +104,8 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type

// Check available balance
auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
constexpr size_t coinbaseMaturity = 2000;
assert(bal == (int64_t) (20000 * COIN * (chain_size - coinbaseMaturity)));

wallet::CCoinControl coin_control;
coin_control.m_allow_other_inputs = allow_other_inputs;
Expand All @@ -124,7 +125,7 @@ static void WalletCreateTx(benchmark::Bench& bench, const OutputType output_type
}

// If automatic coin selection is enabled, add the value of another UTXO to the target
if (coin_control.m_allow_other_inputs) target += 50 * COIN;
if (coin_control.m_allow_other_inputs) target += 20000 * COIN;
std::vector<wallet::CRecipient> recipients = {{dest, target, true}};

bench.epochIterations(5).run([&] {
Expand Down Expand Up @@ -155,7 +156,7 @@ static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType

// Generate chain; each coinbase will have two outputs to fill-up the wallet
const auto& params = Params();
unsigned int chain_size = 1000;
unsigned int chain_size = 3000;
for (unsigned int i = 0; i < chain_size / dest_wallet.size(); ++i) {
for (const auto& dest : dest_wallet) {
generateFakeBlock(params, test_setup->m_node, wallet, dest);
Expand All @@ -164,12 +165,13 @@ static void AvailableCoins(benchmark::Bench& bench, const std::vector<OutputType

// Check available balance
auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).GetTotalAmount()); // Cache
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));
constexpr size_t coinbaseMaturity = 2000;
assert(bal == (int64_t) (20000 * COIN * (chain_size - coinbaseMaturity)));

bench.epochIterations(2).run([&] {
LOCK(wallet.cs_wallet);
const auto& res = wallet::AvailableCoins(wallet);
assert(res.All().size() == (chain_size - COINBASE_MATURITY) * 2);
assert(res.All().size() == (chain_size - coinbaseMaturity) * 2);
});
}

Expand Down
6 changes: 1 addition & 5 deletions src/consensus/consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@

/** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */
extern unsigned int dgpMaxBlockSerSize;
static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000;
/** The maximum allowed weight for a block, see BIP 141 (network rule) */
extern unsigned int dgpMaxBlockWeight;

extern unsigned int dgpMaxBlockSize; // qtum
static const unsigned int MAX_BLOCK_WEIGHT = 4000000;

/** The maximum allowed number of signature check operations in a block (network rule) */
static const int64_t MAX_BLOCK_SIGOPS_COST = 80000;
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
static const int COINBASE_MATURITY = 100;
extern int64_t dgpMaxBlockSigOps;

extern unsigned int dgpMaxProtoMsgLength;
Expand Down
6 changes: 1 addition & 5 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,11 @@ struct Params {
int64_t nPowTargetTimespan;
int64_t nPowTargetTimespanV2;
int64_t nRBTPowTargetTimespan;
std::chrono::seconds PowTargetSpacing() const
{
return std::chrono::seconds{nPowTargetSpacing};
}
std::chrono::seconds TargetSpacingChrono(int height) const
{
return std::chrono::seconds{TargetSpacing(height)};
}
int64_t DifficultyAdjustmentInterval(int height=0) const
int64_t DifficultyAdjustmentInterval(int height) const
{
int64_t targetTimespan = TargetTimespan(height);
int64_t targetSpacing = TargetSpacing(height);
Expand Down
20 changes: 19 additions & 1 deletion src/consensus/tx_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <consensus/validation.h>
#ifndef BUILD_BITCOIN_INTERNAL
#include <script/solver.h>
#endif

bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
{
Expand All @@ -16,21 +19,36 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
if (tx.vout.empty())
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
if (::GetSerializeSize(TX_NO_WITNESS(tx)) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) {
if (::GetSerializeSize(TX_NO_WITNESS(tx)) > MAX_TRANSACTION_BASE_SIZE ||
::GetSerializeSize(TX_NO_WITNESS(tx)) * WITNESS_SCALE_FACTOR > dgpMaxBlockWeight) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize");
}

// Check for negative or overflow output values (see CVE-2010-5139)
CAmount nValueOut = 0;
for (const auto& txout : tx.vout)
{
if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake())
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");
if (txout.nValue < 0)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");

#ifndef BUILD_BITCOIN_INTERNAL
/////////////////////////////////////////////////////////// // qtum
if (txout.scriptPubKey.HasOpCall() || txout.scriptPubKey.HasOpCreate() || txout.scriptPubKey.HasOpSender()) {
std::vector<std::vector<unsigned char>> vSolutions;
TxoutType whichType = Solver(txout.scriptPubKey, vSolutions, true);
if (whichType == TxoutType::NONSTANDARD) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-contract-nonstandard");
}
}
///////////////////////////////////////////////////////////
#endif
}

// Check for duplicate inputs (see CVE-2018-17144)
Expand Down
28 changes: 16 additions & 12 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <script/interpreter.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <chainparams.h>

bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
Expand Down Expand Up @@ -180,7 +181,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
assert(!coin.IsSpent());

// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
if ((coin.IsCoinBase() || coin.IsCoinStake()) && nSpendHeight - coin.nHeight < ::Params().GetConsensus().CoinbaseMaturity(nSpendHeight)) {
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
}
Expand All @@ -192,18 +193,21 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
}
}

const CAmount value_out = tx.GetValueOut();
if (nValueIn < value_out) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
}
if (!tx.IsCoinStake())
{
const CAmount value_out = tx.GetValueOut();
if (nValueIn < value_out) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
}

// Tally transaction fees
const CAmount txfee_aux = nValueIn - value_out;
if (!MoneyRange(txfee_aux)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
}
// Tally transaction fees
const CAmount txfee_aux = nValueIn - value_out;
if (!MoneyRange(txfee_aux)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
}

txfee = txfee_aux;
txfee = txfee_aux;
}
return true;
}
2 changes: 1 addition & 1 deletion src/qt/transactiondesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall

if (wtx.is_coinbase)
{
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
quint32 numBlocksToMaturity = Params().GetConsensus().CoinbaseMaturity(numBlocks) + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
}

Expand Down
6 changes: 4 additions & 2 deletions src/test/fuzz/package_eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <util/rbf.h>
#include <validation.h>
#include <validationinterface.h>
#include <chainparams.h>

using node::NodeContext;

Expand All @@ -40,9 +41,10 @@ void initialize_tx_pool()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();

for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
for (int i = 0; i < 2 * coinbaseMaturity; ++i) {
COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_EMPTY)};
if (i < COINBASE_MATURITY) {
if (i < coinbaseMaturity) {
// Remember the txids to avoid expensive disk access later on
g_outpoints_coinbase_init_mature.push_back(prevout);
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/fuzz/pow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ FUZZ_TARGET(pow, .init = initialize_pow)
current_block.nHeight = current_height;
}
if (fuzzed_data_provider.ConsumeBool()) {
const uint32_t seconds = current_height * consensus_params.nPowTargetSpacing;
const uint32_t seconds = current_height * consensus_params.TargetSpacing(current_height);
if (!AdditionOverflow(fixed_time, seconds)) {
current_block.nTime = fixed_time + seconds;
}
Expand All @@ -64,7 +64,7 @@ FUZZ_TARGET(pow, .init = initialize_pow)
{
(void)GetBlockProof(current_block);
(void)CalculateNextWorkRequired(&current_block, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, std::numeric_limits<int64_t>::max()), consensus_params);
if (current_block.nHeight != std::numeric_limits<int>::max() && current_block.nHeight - (consensus_params.DifficultyAdjustmentInterval() - 1) >= 0) {
if (current_block.nHeight != std::numeric_limits<int>::max() && current_block.nHeight - (consensus_params.DifficultyAdjustmentInterval(current_block.nHeight) - 1) >= 0) {
(void)GetNextWorkRequired(&current_block, &(*block_header), consensus_params);
}
}
Expand Down Expand Up @@ -105,12 +105,12 @@ FUZZ_TARGET(pow_transition, .init = initialize_pow)
nbits = pow_limit.GetCompact();
}
// Create one difficulty adjustment period worth of headers
for (int height = 0; height < consensus_params.DifficultyAdjustmentInterval(); ++height) {
for (int height = 0; height < consensus_params.DifficultyAdjustmentInterval(height); ++height) {
CBlockHeader header;
header.nVersion = version;
header.nTime = old_time;
header.nBits = nbits;
if (height == consensus_params.DifficultyAdjustmentInterval() - 1) {
if (height == consensus_params.DifficultyAdjustmentInterval(height) - 1) {
header.nTime = new_time;
}
auto current_block{std::make_unique<CBlockIndex>(header)};
Expand Down
5 changes: 3 additions & 2 deletions src/test/fuzz/process_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ void initialize_process_message()
}

static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
/*chain_type=*/ChainType::UNITTEST,
/*extra_args=*/{"-txreconciliation"});
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
for (int i = 0; i < 2 * coinbaseMaturity; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
Expand Down
5 changes: 3 additions & 2 deletions src/test/fuzz/process_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ const TestingSetup* g_setup;
void initialize_process_messages()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
/*chain_type=*/ChainType::UNITTEST,
/*extra_args=*/{"-txreconciliation"});
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
for (int i = 0; i < 2 * coinbaseMaturity; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
SyncWithValidationInterfaceQueue();
Expand Down
11 changes: 7 additions & 4 deletions src/test/fuzz/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <util/rbf.h>
#include <validation.h>
#include <validationinterface.h>
#include <chainparams.h>

using node::BlockAssembler;
using node::NodeContext;
Expand All @@ -42,10 +43,11 @@ void initialize_tx_pool()
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();

for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
for (int i = 0; i < 2 * coinbaseMaturity; ++i) {
COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_OP_TRUE)};
// Remember the txids to avoid expensive disk access later on
auto& outpoints = i < COINBASE_MATURITY ?
auto& outpoints = i < coinbaseMaturity ?
g_outpoints_coinbase_init_mature :
g_outpoints_coinbase_init_immature;
outpoints.push_back(prevout);
Expand Down Expand Up @@ -92,7 +94,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Cha
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, dgpMaxBlockWeight);
options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
auto assembler = BlockAssembler{chainstate, &tx_pool, options};
auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
Expand Down Expand Up @@ -197,7 +199,8 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
outpoints_rbf = outpoints_supply;

// The sum of the values of all spendable outpoints
constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN};
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
const CAmount SUPPLY_TOTAL{coinbaseMaturity * 50 * COIN};

SetMempoolConstraints(*node.args, fuzzed_data_provider);
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
Expand Down
5 changes: 3 additions & 2 deletions src/test/fuzz/utxo_snapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const std::vector<std::shared_ptr<CBlock>>* g_chain;

void initialize_chain()
{
const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)};
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
const auto params{CreateChainParams(ArgsManager{}, ChainType::UNITTEST)};
int coinbaseMaturity = params->GetConsensus().CoinbaseMaturity(0);
static const auto chain{CreateBlockChain(2 * coinbaseMaturity, *params)};
g_chain = &chain;
}

Expand Down
5 changes: 3 additions & 2 deletions src/test/fuzz/utxo_total_supply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ FUZZ_TARGET(utxo_total_supply)
{
/** The testing setup that creates a chainman only (no chainstate) */
ChainTestingSetup test_setup{
ChainType::REGTEST,
ChainType::UNITTEST,
{
"-testactivationheight=bip34@2",
},
Expand Down Expand Up @@ -94,7 +94,8 @@ FUZZ_TARGET(utxo_total_supply)
// Get at which height we duplicate the coinbase
// Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
// Up to 300 seems reasonable.
int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0);
int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 3 * coinbaseMaturity);
// Always pad with OP_0 at the end to avoid bad-cb-length error
const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0;
// Mine the first block with this duplicate
Expand Down
Loading

0 comments on commit c3e90ef

Please sign in to comment.