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
5 changes: 2 additions & 3 deletions libbitcoinkernel-sys/bitcoin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -488,9 +488,8 @@ try_append_cxx_flags("-fno-extended-identifiers" TARGET core_interface SKIP_LINK
# which can cause issues with coverage builds, particularly when using
# Clang in the OSS-Fuzz environment due to its use of other options
# and a third party script, or with GCC.
try_append_cxx_flags("-fdebug-prefix-map=A=B" TARGET core_interface SKIP_LINK
IF_CHECK_PASSED "-fdebug-prefix-map=${PROJECT_SOURCE_DIR}/src=."
)
# Set `-fmacro-prefix-map`, so that source file names are expanded without the
# src prefix.
try_append_cxx_flags("-fmacro-prefix-map=A=B" TARGET core_interface SKIP_LINK
IF_CHECK_PASSED "-fmacro-prefix-map=${PROJECT_SOURCE_DIR}/src=."
)
Expand Down
12 changes: 12 additions & 0 deletions libbitcoinkernel-sys/bitcoin/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ The codebase is maintained using the "contributor workflow" where everyone
without exception contributes patch proposals using "pull requests" (PRs). This
facilitates social contribution, easy testing and peer review.

Pull request authors must fully and confidently understand their own changes
and must have tested them. Contributors should mention which tests cover their
changes, or include the manual steps they used to confirm the change.
Contributors are expected to be prepared to clearly motivate and explain their
changes. If there is doubt, the pull request may be closed.
Please refer to the [peer review](#peer-review) section below for more details.

To contribute a patch, the workflow is as follows:

1. Fork repository ([only for the first time](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo))
Expand Down Expand Up @@ -338,6 +345,11 @@ reviewers that the changes warrant the review effort, and if reviewers are
"Concept NACK'ing" the PR, the author may need to present arguments and/or do
research backing their suggested changes.

Moreover, if there is reasonable doubt that the pull request author does not
fully understand the changes they are submitting themselves, or if it becomes
clear that they have not tested the changes on a basic level themselves, the
pull request may be closed immediately.

#### Conceptual Review

A review can be a conceptual review, where the reviewer leaves a comment
Expand Down
1 change: 1 addition & 0 deletions libbitcoinkernel-sys/bitcoin/contrib/guix/libexec/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ CONFIGFLAGS="-DREDUCE_EXPORTS=ON -DBUILD_BENCH=OFF -DBUILD_GUI_TESTS=OFF -DBUILD
# CFLAGS
HOST_CFLAGS="-O2 -g"
HOST_CFLAGS+=$(find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;)
HOST_CFLAGS+=" -fdebug-prefix-map=${DISTSRC}/src=."
case "$HOST" in
*mingw*) HOST_CFLAGS+=" -fno-ident" ;;
*darwin*) unset HOST_CFLAGS ;;
Expand Down
5 changes: 4 additions & 1 deletion libbitcoinkernel-sys/bitcoin/src/bench/connectblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <script/interpreter.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <undo.h>
#include <validation.h>

#include <cassert>
Expand Down Expand Up @@ -100,7 +101,9 @@ void BenchmarkConnectBlock(benchmark::Bench& bench, std::vector<CKey>& keys, std
auto* pindex{chainman->m_blockman.AddToBlockIndex(test_block, chainman->m_best_header)}; // Doing this here doesn't impact the benchmark
CCoinsViewCache viewNew{&chainstate.CoinsTip()};

assert(chainstate.ConnectBlock(test_block, test_block_state, pindex, viewNew));
CBlockUndo blockundo;
assert(chainstate.SpendBlock(test_block, pindex, viewNew, test_block_state, blockundo));
assert(chainstate.ConnectBlock(test_block, blockundo, test_block_state, pindex));
});
}

Expand Down
4 changes: 2 additions & 2 deletions libbitcoinkernel-sys/bitcoin/src/bench/mempool_stress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static void MemPoolAddTransactions(benchmark::Bench& bench)
std::vector<CTransactionRef> transactions;
// Create 1000 clusters of 100 transactions each
for (int i=0; i<100; i++) {
auto new_txs = CreateCoinCluster(det_rand, childTxs, /*min_ancestors*/ 1);
auto new_txs = CreateCoinCluster(det_rand, childTxs, /*min_ancestors=*/ 1);
transactions.insert(transactions.end(), new_txs.begin(), new_txs.end());
}

Expand Down Expand Up @@ -156,7 +156,7 @@ static void ComplexMemPool(benchmark::Bench& bench)
// in the same state at the end of the function, so we benchmark both
// mining a block and reorging the block's contents back into the mempool.
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
pool.removeForBlock(tx_remove_for_block, /*nBlockHeight*/100);
pool.removeForBlock(tx_remove_for_block, /*nBlockHeight=*/100);
for (auto& tx: tx_remove_for_block) {
AddTx(tx, pool, det_rand);
}
Expand Down
23 changes: 23 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,29 @@ const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
}
}

std::vector<std::reference_wrapper<const Coin>> CCoinsViewCache::AccessCoins(const CTransaction& tx) const
{
std::vector<std::reference_wrapper<const Coin>> coins;
coins.reserve(tx.vin.size());
for (const CTxIn& input : tx.vin) {
coins.emplace_back(AccessCoin(input.prevout));
}
return coins;
}

std::vector<CTxOut> CCoinsViewCache::GetUnspentOutputs(const CTransaction& tx) const
{
std::vector<CTxOut> spent_outputs;
spent_outputs.reserve(tx.vin.size());
for (const auto& txin : tx.vin) {
const COutPoint& prevout = txin.prevout;
const Coin& coin = AccessCoin(prevout);
assert(!coin.IsSpent());
spent_outputs.emplace_back(coin.out);
}
return spent_outputs;
}

bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
CCoinsMap::const_iterator it = FetchCoin(outpoint);
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
Expand Down
17 changes: 17 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,23 @@ class CCoinsViewCache : public CCoinsViewBacked
*/
const Coin& AccessCoin(const COutPoint &output) const;

/**
* Return a vector of references to Coins in the cache in order they get
* consumed by the tx's inputs, or coinEmpty if the Coin is not
* found.
*
* Generally, this should only be held for a short scope. The coins should
* generally not be held through any other calls to this cache.
*/
std::vector<std::reference_wrapper<const Coin>> AccessCoins(const CTransaction& tx) const;

/**
* Return a vector of unspent outputs of coins in the cache that are spent
* by the provided transaction. The coins they belong to must be unspent in
* the cache.
*/
std::vector<CTxOut> GetUnspentOutputs(const CTransaction& tx) const;

/**
* Add a coin. Set possible_overwrite to true if an unspent version may
* already exist in the cache.
Expand Down
9 changes: 3 additions & 6 deletions libbitcoinkernel-sys/bitcoin/src/common/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@

using util::ReplaceAll;

// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();

#ifndef WIN32
std::string ShellEscape(const std::string& arg)
{
Expand Down Expand Up @@ -130,8 +127,8 @@ std::optional<size_t> GetTotalRAM()
return std::nullopt;
}

// Obtain the application startup time (used for uptime calculation)
int64_t GetStartupTime()
SteadyClock::duration GetUptime()
{
return nStartupTime;
static const auto g_startup_time{SteadyClock::now()};
return SteadyClock::now() - g_startup_time;
}
6 changes: 4 additions & 2 deletions libbitcoinkernel-sys/bitcoin/src/common/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
#define BITCOIN_COMMON_SYSTEM_H

#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <util/time.h>

#include <chrono>
#include <cstdint>
#include <optional>
#include <string>

// Application startup time (used for uptime calculation)
int64_t GetStartupTime();
/// Monotonic uptime (not affected by system time changes).
SteadyClock::duration GetUptime();

void SetupEnvironment();
[[nodiscard]] bool SetupNetworking();
Expand Down
63 changes: 41 additions & 22 deletions libbitcoinkernel-sys/bitcoin/src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <util/check.h>
#include <util/moneystr.h>

#include <span>

bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
if (tx.nLockTime == 0)
Expand Down Expand Up @@ -123,62 +125,73 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx)
return nSigOps;
}

unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
template <Consensus::CoinRef T>
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const std::span<T> coins)
{
if (tx.IsCoinBase())
return 0;

unsigned int nSigOps = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout);
Assert(coins.size() == tx.vin.size());
auto input_it = tx.vin.begin();
for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
const Coin& coin = *it;
assert(!coin.IsSpent());
const CTxOut &prevout = coin.out;
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig);
nSigOps += prevout.scriptPubKey.GetSigOpCount(input_it->scriptSig);
}
return nSigOps;
}

int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags)
template unsigned int GetP2SHSigOpCount<const Coin>(
const CTransaction& tx, const std::span<const Coin>);

template unsigned int GetP2SHSigOpCount<std::reference_wrapper<const Coin>>(
const CTransaction& tx, const std::span<std::reference_wrapper<const Coin>>);

template <Consensus::CoinRef T>
int64_t GetTransactionSigOpCost(const CTransaction& tx, const std::span<T> coins, script_verify_flags flags)
{
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;

if (tx.IsCoinBase())
return nSigOps;

if (flags & SCRIPT_VERIFY_P2SH) {
nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR;
nSigOps += GetP2SHSigOpCount(tx, coins) * WITNESS_SCALE_FACTOR;
}

for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout);
Assert(coins.size() == tx.vin.size());
auto input_it = tx.vin.begin();
for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
const Coin& coin = *it;
assert(!coin.IsSpent());
const CTxOut &prevout = coin.out;
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, tx.vin[i].scriptWitness, flags);
nSigOps += CountWitnessSigOps(input_it->scriptSig, prevout.scriptPubKey, input_it->scriptWitness, flags);
}
return nSigOps;
}
template int64_t GetTransactionSigOpCost<const Coin>(
const CTransaction& tx, std::span<const Coin> coins, script_verify_flags flags);

bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "bad-txns-inputs-missingorspent",
strprintf("%s: inputs missing/spent", __func__));
}
template int64_t GetTransactionSigOpCost<std::reference_wrapper<const Coin>>(
const CTransaction& tx, const std::span<std::reference_wrapper<const Coin>> coins, script_verify_flags flags);

template <Consensus::CoinRef T>
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const std::span<T> coins, int nSpendHeight, CAmount& txfee)
{
CAmount nValueIn = 0;
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
Assert(coins.size() == tx.vin.size());
auto input_it = tx.vin.begin();
for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
const Coin& coin = *it;
assert(!coin.IsSpent());

// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
strprintf("tried to spend coinbase at depth %d", static_cast<int>(nSpendHeight - coin.nHeight)));
}

// Check for negative or overflow input values
Expand All @@ -203,3 +216,9 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
txfee = txfee_aux;
return true;
}

template bool Consensus::CheckTxInputs<const Coin>(
const CTransaction& tx, TxValidationState& state, const std::span<const Coin> coins, int nSpendHeight, CAmount& txfee);

template bool Consensus::CheckTxInputs<std::reference_wrapper<const Coin>>(
const CTransaction& tx, TxValidationState& state, const std::span<std::reference_wrapper<const Coin>> coins, int nSpendHeight, CAmount& txfee);
25 changes: 18 additions & 7 deletions libbitcoinkernel-sys/bitcoin/src/consensus/tx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H
#define BITCOIN_CONSENSUS_TX_VERIFY_H

#include <coins.h>
#include <consensus/amount.h>
#include <script/verify_flags.h>

#include <concepts>
#include <cstdint>
#include <span>
#include <vector>

class CBlockIndex;
Expand All @@ -19,13 +22,19 @@ class TxValidationState;
/** Transaction validation functions */

namespace Consensus {

template <typename T>
concept CoinRef = std::convertible_to<T, const Coin&>;

/**
* Check whether all inputs of this transaction are valid (no double spends and amounts)
* Check whether all inputs of this transaction are valid (amounts and maturity)
* This does not modify the UTXO set. This does not check scripts and sigs.
* @param[in] coins span of Coins containing previous transaction outputs in the order we're spending them
* @param[out] txfee Set to the transaction fee if successful.
* Preconditions: tx.IsCoinBase() is false.
*/
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
template <Consensus::CoinRef T>
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, std::span<T> coins, int nSpendHeight, CAmount& txfee);
} // namespace Consensus

/** Auxiliary functions for transaction validation (ideally should not be exposed) */
Expand All @@ -40,20 +49,22 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx);
/**
* Count ECDSA signature operations in pay-to-script-hash inputs.
*
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
* @param[in] coins span of Coins containing previous transaction outputs in the order we're spending them
* @return maximum number of sigops required to validate this transaction's inputs
* @see CTransaction::FetchInputs
*/
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs);
template <Consensus::CoinRef T>
unsigned int GetP2SHSigOpCount(const CTransaction& tx, std::span<T> coins);

/**
* Compute total signature operation cost of a transaction.
* @param[in] tx Transaction for which we are computing the cost
* @param[in] inputs Map of previous transactions that have outputs we're spending
* @param[in] tx Transaction for which we are computing the cost
* @param[in] coins span of Coins containing previous transaction outputs in the order we're spending them
* @param[in] flags Script verification flags
* @return Total signature operation cost of tx
*/
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags);
template <Consensus::CoinRef T>
int64_t GetTransactionSigOpCost(const CTransaction& tx, std::span<T> coins, script_verify_flags flags);

/**
* Check if transaction is final and can be included in a block with the
Expand Down
2 changes: 1 addition & 1 deletion libbitcoinkernel-sys/bitcoin/src/index/blockfilterindex.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BlockFilterIndex final : public BaseIndex

Mutex m_cs_headers_cache;
/** cache of block hash to filter header, to avoid disk access when responding to getcfcheckpt. */
std::unordered_map<uint256, uint256, FilterHeaderHasher> m_headers_cache GUARDED_BY(m_cs_headers_cache);
std::unordered_map<uint256, uint256, BlockHasher> m_headers_cache GUARDED_BY(m_cs_headers_cache);

// Last computed header to avoid disk reads on every new block.
uint256 m_last_header{};
Expand Down
7 changes: 6 additions & 1 deletion libbitcoinkernel-sys/bitcoin/src/init/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void AddLoggingArgs(ArgsManager& argsman)
"If <category> is not supplied or if <category> is 1 or \"all\", output all debug logging. If <category> is 0 or \"none\", any other categories are ignored. Other valid values for <category> are: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-debugexclude=<category>", "Exclude debug and trace logging for a category. Can be used in conjunction with -debug=1 to output debug and trace logging for all categories except the specified category. This option can be specified multiple times to exclude multiple categories. This takes priority over \"-debug\"", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logips", strprintf("Include IP addresses in log output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-loglevel=<level>|<category>:<level>", strprintf("Set the global or per-category severity level for logging categories enabled with the -debug configuration option or the logging RPC. Possible values are %s (default=%s). The following levels are always logged: error, warning, info. If <category>:<level> is supplied, the setting will override the global one and may be specified multiple times to set multiple category-specific levels. <category> can be: %s.", LogInstance().LogLevelsString(), LogInstance().LogLevelToStr(BCLog::DEFAULT_LOG_LEVEL), LogInstance().LogCategoriesString()), ArgsManager::DISALLOW_NEGATION | ArgsManager::DISALLOW_ELISION | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
Expand Down Expand Up @@ -98,6 +98,11 @@ util::Result<void> SetLoggingCategories(const ArgsManager& args)
return util::Error{strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)};
}
}

if (LogInstance().GetCategoryMask() != BCLog::NONE) {
LogInfo("Debug logging is enabled (-debug). Additional log output may contain privacy-sensitive information. Be cautious when sharing logs.");
}

return {};
}

Expand Down
Loading
Loading