mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
02741a7706
d5b4c0b69e543de51bb37d602d488ee0949ba185 pool: change memusage_test to use int64_t, add allocation check (Martin Leitner-Ankerl) ce881bf9fcb7c30bb1fafd6ce38844f4f829452a pool: make sure PoolAllocator uses the correct alignment (Martin Leitner-Ankerl) Pull request description: The class `CTxOut` has a member `CAmount` which is an int64_t, and on ARM 32bit int64_t are 8 byte aligned, which is larger than the pointer alignment of 4 bytes. So for `CCoinsMap` to be able to use the pool, we need to use the alignment of the member instead of just `alignof(void*)`. This fixes #28906 (first noted in https://github.com/bitcoin/bitcoin/issues/28718#issuecomment-1807197107) and #28440. ACKs for top commit: pinheadmz: ACK d5b4c0b69e543de51bb37d602d488ee0949ba185 hebasto: re-ACK d5b4c0b69e543de51bb37d602d488ee0949ba185, the only change since my recent [review](https://github.com/bitcoin/bitcoin/pull/28913#pullrequestreview-1739334189) is an updated test. theStack: Tested ACK d5b4c0b69e543de51bb37d602d488ee0949ba185 Tree-SHA512: 4446793fad6d56f0fe22e09ac9ade051e86de11ac039cd61c0f6b7f79874242878a6a46a2c76ac3b8f1d53464872620d39139f54b1471daccad26d6bb1ae8ca1
394 lines
14 KiB
C++
394 lines
14 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_COINS_H
|
|
#define BITCOIN_COINS_H
|
|
|
|
#include <compressor.h>
|
|
#include <core_memusage.h>
|
|
#include <memusage.h>
|
|
#include <primitives/transaction.h>
|
|
#include <serialize.h>
|
|
#include <support/allocators/pool.h>
|
|
#include <uint256.h>
|
|
#include <util/hasher.h>
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
|
|
/**
|
|
* A UTXO entry.
|
|
*
|
|
* Serialized format:
|
|
* - VARINT((coinbase ? 1 : 0) | (height << 1))
|
|
* - the non-spent CTxOut (via TxOutCompression)
|
|
*/
|
|
class Coin
|
|
{
|
|
public:
|
|
//! unspent transaction output
|
|
CTxOut out;
|
|
|
|
//! whether containing transaction was a coinbase
|
|
unsigned int fCoinBase : 1;
|
|
|
|
//! at which height this containing transaction was included in the active block chain
|
|
uint32_t nHeight : 31;
|
|
|
|
//! construct a Coin from a CTxOut and height/coinbase information.
|
|
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) {}
|
|
Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn) : out(outIn), fCoinBase(fCoinBaseIn),nHeight(nHeightIn) {}
|
|
|
|
void Clear() {
|
|
out.SetNull();
|
|
fCoinBase = false;
|
|
nHeight = 0;
|
|
}
|
|
|
|
//! empty constructor
|
|
Coin() : fCoinBase(false), nHeight(0) { }
|
|
|
|
bool IsCoinBase() const {
|
|
return fCoinBase;
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Serialize(Stream &s) const {
|
|
assert(!IsSpent());
|
|
uint32_t code = nHeight * uint32_t{2} + fCoinBase;
|
|
::Serialize(s, VARINT(code));
|
|
::Serialize(s, Using<TxOutCompression>(out));
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Unserialize(Stream &s) {
|
|
uint32_t code = 0;
|
|
::Unserialize(s, VARINT(code));
|
|
nHeight = code >> 1;
|
|
fCoinBase = code & 1;
|
|
::Unserialize(s, Using<TxOutCompression>(out));
|
|
}
|
|
|
|
/** Either this coin never existed (see e.g. coinEmpty in coins.cpp), or it
|
|
* did exist and has been spent.
|
|
*/
|
|
bool IsSpent() const {
|
|
return out.IsNull();
|
|
}
|
|
|
|
size_t DynamicMemoryUsage() const {
|
|
return memusage::DynamicUsage(out.scriptPubKey);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A Coin in one level of the coins database caching hierarchy.
|
|
*
|
|
* A coin can either be:
|
|
* - unspent or spent (in which case the Coin object will be nulled out - see Coin.Clear())
|
|
* - DIRTY or not DIRTY
|
|
* - FRESH or not FRESH
|
|
*
|
|
* Out of these 2^3 = 8 states, only some combinations are valid:
|
|
* - unspent, FRESH, DIRTY (e.g. a new coin created in the cache)
|
|
* - unspent, not FRESH, DIRTY (e.g. a coin changed in the cache during a reorg)
|
|
* - unspent, not FRESH, not DIRTY (e.g. an unspent coin fetched from the parent cache)
|
|
* - spent, FRESH, not DIRTY (e.g. a spent coin fetched from the parent cache)
|
|
* - spent, not FRESH, DIRTY (e.g. a coin is spent and spentness needs to be flushed to the parent)
|
|
*/
|
|
struct CCoinsCacheEntry
|
|
{
|
|
Coin coin; // The actual cached data.
|
|
unsigned char flags;
|
|
|
|
enum Flags {
|
|
/**
|
|
* DIRTY means the CCoinsCacheEntry is potentially different from the
|
|
* version in the parent cache. Failure to mark a coin as DIRTY when
|
|
* it is potentially different from the parent cache will cause a
|
|
* consensus failure, since the coin's state won't get written to the
|
|
* parent when the cache is flushed.
|
|
*/
|
|
DIRTY = (1 << 0),
|
|
/**
|
|
* FRESH means the parent cache does not have this coin or that it is a
|
|
* spent coin in the parent cache. If a FRESH coin in the cache is
|
|
* later spent, it can be deleted entirely and doesn't ever need to be
|
|
* flushed to the parent. This is a performance optimization. Marking a
|
|
* coin as FRESH when it exists unspent in the parent cache will cause a
|
|
* consensus failure, since it might not be deleted from the parent
|
|
* when this cache is flushed.
|
|
*/
|
|
FRESH = (1 << 1),
|
|
};
|
|
|
|
CCoinsCacheEntry() : flags(0) {}
|
|
explicit CCoinsCacheEntry(Coin&& coin_) : coin(std::move(coin_)), flags(0) {}
|
|
CCoinsCacheEntry(Coin&& coin_, unsigned char flag) : coin(std::move(coin_)), flags(flag) {}
|
|
};
|
|
|
|
/**
|
|
* PoolAllocator's MAX_BLOCK_SIZE_BYTES parameter here uses sizeof the data, and adds the size
|
|
* of 4 pointers. We do not know the exact node size used in the std::unordered_node implementation
|
|
* because it is implementation defined. Most implementations have an overhead of 1 or 2 pointers,
|
|
* so nodes can be connected in a linked list, and in some cases the hash value is stored as well.
|
|
* Using an additional sizeof(void*)*4 for MAX_BLOCK_SIZE_BYTES should thus be sufficient so that
|
|
* all implementations can allocate the nodes from the PoolAllocator.
|
|
*/
|
|
using CCoinsMap = std::unordered_map<COutPoint,
|
|
CCoinsCacheEntry,
|
|
SaltedOutpointHasher,
|
|
std::equal_to<COutPoint>,
|
|
PoolAllocator<std::pair<const COutPoint, CCoinsCacheEntry>,
|
|
sizeof(std::pair<const COutPoint, CCoinsCacheEntry>) + sizeof(void*) * 4>>;
|
|
|
|
using CCoinsMapMemoryResource = CCoinsMap::allocator_type::ResourceType;
|
|
|
|
/** Cursor for iterating over CoinsView state */
|
|
class CCoinsViewCursor
|
|
{
|
|
public:
|
|
CCoinsViewCursor(const uint256 &hashBlockIn): hashBlock(hashBlockIn) {}
|
|
virtual ~CCoinsViewCursor() {}
|
|
|
|
virtual bool GetKey(COutPoint &key) const = 0;
|
|
virtual bool GetValue(Coin &coin) const = 0;
|
|
virtual unsigned int GetValueSize() const = 0;
|
|
|
|
virtual bool Valid() const = 0;
|
|
virtual void Next() = 0;
|
|
|
|
//! Get best block at the time this cursor was created
|
|
const uint256 &GetBestBlock() const { return hashBlock; }
|
|
private:
|
|
uint256 hashBlock;
|
|
};
|
|
|
|
/** Abstract view on the open txout dataset. */
|
|
class CCoinsView
|
|
{
|
|
public:
|
|
/** Retrieve the Coin (unspent transaction output) for a given outpoint.
|
|
* Returns true only when an unspent coin was found, which is returned in coin.
|
|
* When false is returned, coin's value is unspecified.
|
|
*/
|
|
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const;
|
|
|
|
//! Just check whether a given outpoint is unspent.
|
|
virtual bool HaveCoin(const COutPoint &outpoint) const;
|
|
|
|
//! Retrieve the block hash whose state this CCoinsView currently represents
|
|
virtual uint256 GetBestBlock() const;
|
|
|
|
//! Retrieve the range of blocks that may have been only partially written.
|
|
//! If the database is in a consistent state, the result is the empty vector.
|
|
//! Otherwise, a two-element vector is returned consisting of the new and
|
|
//! the old block hash, in that order.
|
|
virtual std::vector<uint256> GetHeadBlocks() const;
|
|
|
|
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
|
//! The passed mapCoins can be modified.
|
|
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true);
|
|
|
|
//! Get a cursor to iterate over the whole state
|
|
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
|
|
|
|
//! As we use CCoinsViews polymorphically, have a virtual destructor
|
|
virtual ~CCoinsView() {}
|
|
|
|
//! Estimate database size (0 if not implemented)
|
|
virtual size_t EstimateSize() const { return 0; }
|
|
};
|
|
|
|
|
|
/** CCoinsView backed by another CCoinsView */
|
|
class CCoinsViewBacked : public CCoinsView
|
|
{
|
|
protected:
|
|
CCoinsView *base;
|
|
|
|
public:
|
|
CCoinsViewBacked(CCoinsView *viewIn);
|
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
|
uint256 GetBestBlock() const override;
|
|
std::vector<uint256> GetHeadBlocks() const override;
|
|
void SetBackend(CCoinsView &viewIn);
|
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
|
|
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
|
|
size_t EstimateSize() const override;
|
|
};
|
|
|
|
|
|
/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
|
|
class CCoinsViewCache : public CCoinsViewBacked
|
|
{
|
|
private:
|
|
const bool m_deterministic;
|
|
|
|
protected:
|
|
/**
|
|
* Make mutable so that we can "fill the cache" even from Get-methods
|
|
* declared as "const".
|
|
*/
|
|
mutable uint256 hashBlock;
|
|
mutable CCoinsMapMemoryResource m_cache_coins_memory_resource{};
|
|
mutable CCoinsMap cacheCoins;
|
|
|
|
/* Cached dynamic memory usage for the inner Coin objects. */
|
|
mutable size_t cachedCoinsUsage{0};
|
|
|
|
public:
|
|
CCoinsViewCache(CCoinsView *baseIn, bool deterministic = false);
|
|
|
|
/**
|
|
* By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache.
|
|
*/
|
|
CCoinsViewCache(const CCoinsViewCache &) = delete;
|
|
|
|
// Standard CCoinsView methods
|
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
|
uint256 GetBestBlock() const override;
|
|
void SetBestBlock(const uint256 &hashBlock);
|
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
|
|
std::unique_ptr<CCoinsViewCursor> Cursor() const override {
|
|
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
|
|
}
|
|
|
|
/**
|
|
* Check if we have the given utxo already loaded in this cache.
|
|
* The semantics are the same as HaveCoin(), but no calls to
|
|
* the backing CCoinsView are made.
|
|
*/
|
|
bool HaveCoinInCache(const COutPoint &outpoint) const;
|
|
|
|
/**
|
|
* Return a reference to Coin in the cache, or coinEmpty if not found. This is
|
|
* more efficient than GetCoin.
|
|
*
|
|
* Generally, do not hold the reference returned for more than a short scope.
|
|
* While the current implementation allows for modifications to the contents
|
|
* of the cache while holding the reference, this behavior should not be relied
|
|
* on! To be safe, best to not hold the returned reference through any other
|
|
* calls to this cache.
|
|
*/
|
|
const Coin& AccessCoin(const COutPoint &output) const;
|
|
|
|
/**
|
|
* Add a coin. Set possible_overwrite to true if an unspent version may
|
|
* already exist in the cache.
|
|
*/
|
|
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
|
|
|
/**
|
|
* Emplace a coin into cacheCoins without performing any checks, marking
|
|
* the emplaced coin as dirty.
|
|
*
|
|
* NOT FOR GENERAL USE. Used only when loading coins from a UTXO snapshot.
|
|
* @sa ChainstateManager::PopulateAndValidateSnapshot()
|
|
*/
|
|
void EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin);
|
|
|
|
/**
|
|
* Spend a coin. Pass moveto in order to get the deleted data.
|
|
* If no unspent output exists for the passed outpoint, this call
|
|
* has no effect.
|
|
*/
|
|
bool SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr);
|
|
|
|
/**
|
|
* Push the modifications applied to this cache to its base and wipe local state.
|
|
* Failure to call this method or Sync() before destruction will cause the changes
|
|
* to be forgotten.
|
|
* If false is returned, the state of this cache (and its backing view) will be undefined.
|
|
*/
|
|
bool Flush();
|
|
|
|
/**
|
|
* Push the modifications applied to this cache to its base while retaining
|
|
* the contents of this cache (except for spent coins, which we erase).
|
|
* Failure to call this method or Flush() before destruction will cause the changes
|
|
* to be forgotten.
|
|
* If false is returned, the state of this cache (and its backing view) will be undefined.
|
|
*/
|
|
bool Sync();
|
|
|
|
/**
|
|
* Removes the UTXO with the given outpoint from the cache, if it is
|
|
* not modified.
|
|
*/
|
|
void Uncache(const COutPoint &outpoint);
|
|
|
|
//! Calculate the size of the cache (in number of transaction outputs)
|
|
unsigned int GetCacheSize() const;
|
|
|
|
//! Calculate the size of the cache (in bytes)
|
|
size_t DynamicMemoryUsage() const;
|
|
|
|
//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
|
|
bool HaveInputs(const CTransaction& tx) const;
|
|
|
|
//! Force a reallocation of the cache map. This is required when downsizing
|
|
//! the cache because the map's allocator may be hanging onto a lot of
|
|
//! memory despite having called .clear().
|
|
//!
|
|
//! See: https://stackoverflow.com/questions/42114044/how-to-release-unordered-map-memory
|
|
void ReallocateCache();
|
|
|
|
//! Run an internal sanity check on the cache data structure. */
|
|
void SanityCheck() const;
|
|
|
|
private:
|
|
/**
|
|
* @note this is marked const, but may actually append to `cacheCoins`, increasing
|
|
* memory usage.
|
|
*/
|
|
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
|
|
};
|
|
|
|
//! Utility function to add all of a transaction's outputs to a cache.
|
|
//! When check is false, this assumes that overwrites are only possible for coinbase transactions.
|
|
//! When check is true, the underlying view may be queried to determine whether an addition is
|
|
//! an overwrite.
|
|
// TODO: pass in a boolean to limit these possible overwrites to known
|
|
// (pre-BIP34) cases.
|
|
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false);
|
|
|
|
//! Utility function to find any unspent output with a given txid.
|
|
//! This function can be quite expensive because in the event of a transaction
|
|
//! which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK
|
|
//! lookups to database, so it should be used with care.
|
|
const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid);
|
|
|
|
/**
|
|
* This is a minimally invasive approach to shutdown on LevelDB read errors from the
|
|
* chainstate, while keeping user interface out of the common library, which is shared
|
|
* between bitcoind, and bitcoin-qt and non-server tools.
|
|
*
|
|
* Writes do not need similar protection, as failure to write is handled by the caller.
|
|
*/
|
|
class CCoinsViewErrorCatcher final : public CCoinsViewBacked
|
|
{
|
|
public:
|
|
explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
|
|
|
|
void AddReadErrCallback(std::function<void()> f) {
|
|
m_err_callbacks.emplace_back(std::move(f));
|
|
}
|
|
|
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
|
|
|
private:
|
|
/** A list of callbacks to execute upon leveldb read error. */
|
|
std::vector<std::function<void()>> m_err_callbacks;
|
|
|
|
};
|
|
|
|
#endif // BITCOIN_COINS_H
|