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
2 changes: 1 addition & 1 deletion libbitcoinkernel-sys/bitcoin/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ jobs:
file-env: './ci/test/00_setup_env_arm.sh'
provider: 'gha'

- name: 'ASan + LSan + UBSan + integer'
- name: 'ASan + LSan + UBSan + integer, no depends, USDT'
cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' # has to match container in ci/test/00_setup_env_native_asan.sh for tracing tools
fallback-runner: 'ubuntu-24.04'
timeout-minutes: 120
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export NO_DEPENDS=1
export GOAL="install"
export CI_LIMIT_STACK_SIZE=1
export BITCOIN_CONFIG="\
--preset=dev-mode \
-DWITH_USDT=ON -DWITH_ZMQ=ON -DBUILD_GUI=ON \
-DSANITIZERS=address,float-divide-by-zero,integer,undefined \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
Expand Down
10 changes: 0 additions & 10 deletions libbitcoinkernel-sys/bitcoin/doc/i2p.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,3 @@ In most cases, the default router settings should work fine.

Please see the "General Guidance for Developers" section in https://geti2p.net/en/docs/api/samv3
if you are developing a downstream application that may be bundling I2P with Bitcoin.

## Privacy recommendations

- Operating a node that listens on multiple networks (e.g. IPv4 and I2P) can help
strengthen the Bitcoin network, as nodes in this configuration (i.e. bridge nodes) increase
the cost and complexity of launching eclipse and partition attacks. However, under certain
conditions, an adversary that can connect to your node on multiple networks may be
able to correlate those identities by observing shared runtime characteristics. It
is not recommended to expose your node over multiple networks if you require
unlinkability across those identities.
7 changes: 0 additions & 7 deletions libbitcoinkernel-sys/bitcoin/doc/tor.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,3 @@ for normal IPv4/IPv6 communication, use:
Otherwise it is trivial to link them, which may reduce privacy. Onion
services created automatically (as in section 2) always have only one port
open.
- Operating a node that listens on multiple networks (e.g. IPv4 and Tor) can help
strengthen the Bitcoin network, as nodes in this configuration (i.e. bridge nodes) increase
the cost and complexity of launching eclipse and partition attacks. However, under certain
conditions, an adversary that can connect to your node on multiple networks may be
able to correlate those identities by observing shared runtime characteristics. It
is not recommended to expose your node over multiple networks if you require
unlinkability across those identities.
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
2 changes: 1 addition & 1 deletion libbitcoinkernel-sys/bitcoin/src/bitcoin-chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class TestValidationInterface : public ValidationInterface

std::optional<std::string> m_expected_valid_block = std::nullopt;

void BlockChecked(const Block block, const BlockValidationState state) override
void BlockChecked(const Block block, const BlockValidationStateView state) override
{
auto mode{state.GetValidationMode()};
switch (mode) {
Expand Down
6 changes: 6 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
#include <chain.h>
#include <tinyformat.h>
#include <util/check.h>
#include <util/time.h>

std::string CBlockFileInfo::ToString() const
{
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
}

std::string CBlockIndex::ToString() const
{
Expand Down
41 changes: 41 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,47 @@ static constexpr int32_t SEQ_ID_INIT_FROM_DISK = 1;
*/
static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60;

class CBlockFileInfo
{
public:
unsigned int nBlocks{}; //!< number of blocks stored in file
unsigned int nSize{}; //!< number of used bytes of block file
unsigned int nUndoSize{}; //!< number of used bytes in the undo file
unsigned int nHeightFirst{}; //!< lowest height of block in file
unsigned int nHeightLast{}; //!< highest height of block in file
uint64_t nTimeFirst{}; //!< earliest time of block in file
uint64_t nTimeLast{}; //!< latest time of block in file

SERIALIZE_METHODS(CBlockFileInfo, obj)
{
READWRITE(VARINT(obj.nBlocks));
READWRITE(VARINT(obj.nSize));
READWRITE(VARINT(obj.nUndoSize));
READWRITE(VARINT(obj.nHeightFirst));
READWRITE(VARINT(obj.nHeightLast));
READWRITE(VARINT(obj.nTimeFirst));
READWRITE(VARINT(obj.nTimeLast));
}

CBlockFileInfo() = default;

std::string ToString() const;

/** update statistics (does not update nSize) */
void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn)
{
if (nBlocks == 0 || nHeightFirst > nHeightIn)
nHeightFirst = nHeightIn;
if (nBlocks == 0 || nTimeFirst > nTimeIn)
nTimeFirst = nTimeIn;
nBlocks++;
if (nHeightIn > nHeightLast)
nHeightLast = nHeightIn;
if (nTimeIn > nTimeLast)
nTimeLast = nTimeIn;
}
};

enum BlockStatus : uint32_t {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
Expand Down
27 changes: 25 additions & 2 deletions libbitcoinkernel-sys/bitcoin/src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,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 Expand Up @@ -349,8 +372,8 @@ void CCoinsViewCache::SanityCheck() const
assert(recomputed_usage == cachedCoinsUsage);
}

static const uint64_t MIN_TRANSACTION_OUTPUT_WEIGHT{WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut())};
static const uint64_t MAX_OUTPUTS_PER_BLOCK{MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT};
static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut());
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT;

const Coin& AccessByTxid(const CCoinsViewCache& view, const Txid& txid)
{
Expand Down
16 changes: 16 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,22 @@ class CCoinsViewCache : public CCoinsViewBacked
*/
const Coin& AccessCoin(const COutPoint &output) const;

/**
* Return a vector of references to Coins in the cache, 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
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 Sorted span of Coins containing previous transaction outputs tx is spending
* @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, const 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 Sorted span of Coins containing previous transaction outputs we're spending
* @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, const 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 Sorted span of Coins containing previous transaction outputs we're spending
* @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, const std::span<T> coins, script_verify_flags flags);

/**
* Check if transaction is final and can be included in a block with the
Expand Down
10 changes: 5 additions & 5 deletions libbitcoinkernel-sys/bitcoin/src/flatfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@

struct FlatFilePos
{
int32_t nFile{-1};
uint32_t nPos{0};
int nFile{-1};
unsigned int nPos{0};

SERIALIZE_METHODS(FlatFilePos, obj) { READWRITE(VARINT_MODE(obj.nFile, VarIntMode::NONNEGATIVE_SIGNED), VARINT(obj.nPos)); }

FlatFilePos() = default;

FlatFilePos(int32_t nFileIn, uint32_t nPosIn)
: nFile{nFileIn},
nPos{nPosIn}
FlatFilePos(int nFileIn, unsigned int nPosIn) :
nFile(nFileIn),
nPos(nPosIn)
{}

friend bool operator==(const FlatFilePos &a, const FlatFilePos &b) {
Expand Down
Loading
Loading