008eb35f95
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.
233 lines
8.3 KiB
C++
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
|