Files
thehub/libs/utxo/UnspentOutputDatabase.h
tomFlowee 008eb35f95 Make compile faster
The IDE include checker got to the point where it is actually useful and
this removes a lot of unneeded includes.
Naturally, especially for headers like util.h, this may mean we need to
re-add includes in consuming cpp files that bloats the diff a bit.
2026-05-14 13:27:17 +02:00

233 lines
8.3 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2018-2023 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FLOWEE_UNSPENTOUTPUTDATABASE_H
#define FLOWEE_UNSPENTOUTPUTDATABASE_H
#include <uint256.h>
#include <streaming/ConstBuffer.h>
#include <streaming/BufferPool.h>
#include <boost/asio/io_context.hpp>
/**
* @brief The UnspentOutput class is a mem-mappable "leaf" in the UnspentOutputDatabase.
*
* A single unspentOutput instance maps to a single UTXO entry.
* With the information in this class you can open the relevant block and find the transaction
* to iterate over in order to find the output that is being refered to.
* In other words, this is mostly just an index onto the actual block-chain which actually stores
* the real unspent output.
*
*/
class UnspentOutput {
public:
UnspentOutput() = default;
UnspentOutput(const std::shared_ptr<Streaming::BufferPool> &pool, const uint256 &txid, int outIndex, int blockHeight, int offsetInBlock);
UnspentOutput(uint64_t cheapHash, const Streaming::ConstBuffer &buffer);
UnspentOutput(const UnspentOutput &other) = default;
inline bool isValid() const {
return m_data.size() >= 33;
}
uint256 prevTxId() const;
inline int outIndex() const {
return m_outIndex;
}
/// return the offset in the block. In bytes. Notice that offsetInBlock < 91 implies this is a coinbase.
inline int offsetInBlock() const {
return m_offsetInBlock;
}
inline int blockHeight() const {
return m_blockHeight;
}
bool isCoinbase() const;
inline const Streaming::ConstBuffer &data() const {
return m_data;
}
/// return the UnspentOutputDatabase internal to make remove faster
/// pass in to UnspentOutputDatabase::remove() if available
uint64_t rmHint() const {
return m_privData;
}
/// \internal
inline void setRmHint(uint64_t hint) {
m_privData = hint;
}
private:
friend class UnspentOutputDatabase;
Streaming::ConstBuffer m_data;
int m_outIndex = 0;
int m_offsetInBlock = 0; // in bytes. 2GB blocks is enough for a while.
int m_blockHeight = 0;
uint64_t m_cheapHash = 0;
uint64_t m_privData = 0; // used by the database for cache
};
class SpentOutput {
public:
int blockHeight = -1;
int offsetInBlock = -1;
bool isValid() const { return blockHeight > 0; }
};
class UODBPrivate;
/// The unspent outputs database. Also known as the UTXO
class UnspentOutputDatabase
{
public:
UnspentOutputDatabase(boost::asio::io_context &context, const boost::filesystem::path &basedir);
UnspentOutputDatabase(UODBPrivate *priv);
UnspentOutputDatabase(const UnspentOutputDatabase &other) = delete;
~UnspentOutputDatabase();
static UnspentOutputDatabase *createMemOnlyDB(const boost::filesystem::path &basedir);
/// Change limits to be smaller, for instance for regtest setups
static void setSmallLimits();
/**
* Set the amount of changes (inserts/deletes) that should trigger a save.
* The UTXO gathers all changes in memory and has processes to push those slowly
* to disk for permanent storage.
* If we store too often the system slows down and we end up saving data that might
* have been deleted in the next block.
* If we store too little we may run out of memory.
*
* This method allows you to set when a save is to be started, based on the amount
* of changes stored in memory. Note that the standard UTXO usage shows that we save around half
* the amount of record vs changes.
* But when this database is used as a TXID-DB we store about 120% of the records vs changes.
*
* This is much more about usecase than it is about how much memory you have.
*/
static void setChangeCountCausesStore(int count);
struct BlockData {
struct TxOutputs { // can hold all the data for a single transaction
TxOutputs(const uint256 &id, int offsetInBlock, int firstOutput, int lastOutput = -1)
: txid(id),
firstOutput(firstOutput),
lastOutput(lastOutput < firstOutput ? firstOutput : lastOutput),
offsetInBlock(offsetInBlock) {
}
uint256 txid;
int firstOutput = 0, lastOutput = 0;
int offsetInBlock = 0;
};
int blockHeight = -1;
std::vector<TxOutputs> outputs;
};
void insertAll(const BlockData &data);
/**
* @brief insert a new spendable output.
* The 'key' in this database is the combination of the \a txid and the \a outIndex,
* the value that that pair maps to are the two integers \a blockheight and
* \a offsetInBlock.
*
* @param txid the (prev) transaction id.
* @param outIndex Index the index of the output.
* @param blockHeight which block the output is in.
* @param offsetInBlock the amount of bytes into the block the tx is positioned.
*/
void insert(const uint256 &txid, int outIndex, int blockHeight, int offsetInBlock);
/**
* @brief find an output by (prev) txid and output-index.
* @param txid the previous transaction index. The one that created the output.
* @param outIndex the output index inside the prev-tx.
* @return A filled UnspentOutput if found, or an invalid one otherwise.
*/
UnspentOutput find(const uint256 &txid, int outIndex) const;
/**
* @brief remove an output from the unspent database.
* This spends an output, forgetting it from the latest tree of the DB.
* @param txid the previous transaction index. The one that created the output.
* @param outIndex the output index inside the prev-tx.
* @return valid SpendOutput if something was found to remove
*/
SpentOutput remove(const uint256 &txid, int outIndex, uint64_t rmHint = 0);
/**
* The blockFinished should be called after every block to update the UnspentOutput DB
* about which block we just finished.
*
* It is essential that this happens in a synchronous manner so we know that if we
* need to restart from this point, we can start from the next block and the UTXO is
* consistent with the full block passed in via the args.
*
* @see rollback
* @returns true if the database did a garbage-collect
*/
bool blockFinished(int blockheight, const uint256 &blockId);
/**
* Changes made since the last blockFinished() call are reverted.
*/
void rollback();
/**
* Save (some) caches to disk.
* The DB triggers saving of caches to disk based on how many changes
* have been made, which means that if nothing happens then we won't save.
* This method can be called periodically to use a time-based saving.
*/
void saveCaches();
bool loadOlderState();
/**
* @brief setFailedBlockId remembers for this UTXO's view of the world the block-id that was invalid.
*
* This method is expected to be called from the same thread as the blockFinished() is called, to
* avoid race-conditions.
* @param blockId the hash of the block-header of the block which the validation engine rejected.
*/
void setFailedBlockId(const uint256 &blockId);
/**
* @brief blockIdHasFailed returns true if the \a blockId has been registered using setFailedBlockId().
*/
bool blockIdHasFailed(const uint256 &blockId) const;
void clearFailedBlockId(const uint256 &blockId);
/// return the last committed blockHeight
int blockheight() const;
/// return the last committed blockId
uint256 blockId() const;
/// \internal
inline UODBPrivate *priv() { return d; }
UnspentOutputDatabase& operator=(const UnspentOutputDatabase &other) = delete;
private:
UODBPrivate *d;
};
#endif