Files

323 lines
12 KiB
C++
Raw Permalink Normal View History

2018-01-15 15:26:12 +00:00
/*
* This file is part of the flowee project
2024-03-20 19:08:32 +01:00
* Copyright (C) 2017-2024 Tom Zander <tom@flowee.org>
2018-01-15 15:26:12 +00:00
*
* 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 BLOCKVALIDATION_P_H
#define BLOCKVALIDATION_P_H
2019-04-06 12:19:13 +02:00
/*
* WARNING USAGE OF THIS HEADER IS RESTRICTED.
* This Header file is part of the private API and is meant to be used solely by the validation component.
*
* Usage of this API will likely mean your code will break in interesting ways in the future,
* or even stop to compile.
*
* YOU HAVE BEEN WARNED!!
*/
2018-01-15 15:26:12 +00:00
#include "Engine.h"
#include "ValidationSettings_p.h"
#include "ValidationException.h"
2026-05-13 17:55:43 +02:00
#include <UnspentOutputData.h>
#include <txmempool.h>
#include <chain.h>
#include <primitives/Block.h>
#include <primitives/FastUndoBlock.h>
2026-05-13 16:43:23 +02:00
#include <utxo/UnspentOutputDatabase.h>
2018-01-15 15:26:12 +00:00
#include <bloom.h>
2018-01-15 15:26:12 +00:00
#include <boost/thread.hpp>
#include <boost/unordered_map.hpp>
2020-03-28 22:49:53 +01:00
#include <boost/asio/io_context_strand.hpp>
2018-01-15 15:26:12 +00:00
// #define ENABLE_BENCHMARKS
struct ValidationFlags {
ValidationFlags();
bool strictPayToScriptHash;
bool enforceBIP34;
2018-01-15 15:26:12 +00:00
bool enableValidation;
bool scriptVerifyDerSig;
bool scriptVerifyLockTimeVerify;
bool scriptVerifySequenceVerify;
bool nLocktimeVerifySequence;
2018-11-02 22:28:50 +01:00
bool hf201708Active;
bool hf201805Active;
2018-11-08 13:57:22 +01:00
bool hf201811Active;
2019-04-17 16:00:22 +02:00
bool hf201905Active;
2019-10-08 13:51:01 +02:00
bool hf201911Active;
2020-04-10 17:28:02 +02:00
bool hf202005Active;
2025-04-12 12:11:01 +02:00
bool hf202205Active;
bool hf202305Active;
bool hf202405Active;
bool hf202505Active;
bool hf202605Active;
2018-01-15 15:26:12 +00:00
2019-04-17 23:21:14 +02:00
uint32_t scriptValidationFlags(bool requireStandard) const;
2018-01-15 15:26:12 +00:00
/// based on the assumption that the index is after this Flags object, update it based on chain properties
void updateForBlock(CBlockIndex *index);
2018-01-15 15:26:12 +00:00
};
// implemented in TxValidation.cpp
namespace ValidationPrivate {
2026-05-14 12:07:30 +02:00
void validateTransactionInputs(const CTransaction &tx, const Tx &newTx, const std::vector<UnspentOutputData> &unspents, int blockHeight,
2019-04-17 23:21:14 +02:00
ValidationFlags flags, int64_t &fees, uint32_t &txSigops, bool &spendsCoinbase, bool requireStandard);
void checkContextualTransaction(const Tx &tx, ValidationFlags flags);
2018-01-15 15:26:12 +00:00
}
struct Output {
2018-11-13 14:59:31 +01:00
Output(int index, int offsetInBlock) : index(index), offsetInBlock(offsetInBlock) {}
uint256 txid;
int index = -1;
int offsetInBlock = 0;
};
2018-01-15 15:26:12 +00:00
class BlockValidationState : public std::enable_shared_from_this<BlockValidationState>
{
public:
/// A bit field of validations that have succeeded so far. Or a simple BlockInvalid if one of them did not.
enum BlockValidationStatus {
BlockValidityUnknown = 0,
//! Parsed just the header, checked basics.
//! Set at successful completion of checks1NoContext()
BlockValidHeader = 1,
//! Block has a parent state or CBlockIndex, fully validatable leading back to genesis.
//! This block has full data (not just a header) or is on the main-chain. Same with all its parents.
//! When this is set it is allowed for the block to start checks2HaveParentHeaders()
BlockValidTree = 2,
//! Block has a valid header, parsable transactions and we did contextual checks.
//! Implies BlockValidTree to be set.
//! set at the successful completion of checks2HaveParentHeaders();
BlockValidChainHeaders = 4,
//! Parent block is accepted on the chain, allowing this block to be offered as well.
//! When this and BlockValidChainHeaders are set the block is allowed to start updateUtxoAndStartValidation()
BlockValidParent = 8,
//! At least one of the items didn't pass validation.
BlockInvalid = 0x20
};
2021-11-02 10:18:24 +01:00
BlockValidationState(const std::weak_ptr<ValidationEnginePrivate> &parent, const Block &block, std::uint32_t onResultFlags = 0, int originatingNodeId = -1);
2018-01-15 15:26:12 +00:00
~BlockValidationState();
void load();
void checks1NoContext();
void checks2HaveParentHeaders();
2019-03-15 19:10:47 +01:00
void checkSignaturesChunk();
2018-01-15 15:26:12 +00:00
void blockFailed(int punishment, const std::string &error, Validation::RejectCodes code, bool corruptionPossible = false);
/**
* When a block is accepted as the new chain-tip, check and schedule child-blocks that are next in line to be validated.
*/
void signalChildren() const;
enum RecursiveOption {
AddFlag,
RemoveFlag
};
void recursivelyMark(BlockValidationStatus value, RecursiveOption option = AddFlag);
/// schedules a call to our BlockValidationPrivate processNewBlock()
void finishUp();
/// When the previous block's transactions are added to the UTXO, we start our validation.
void updateUtxoAndStartValidation();
/**
* @brief calculateTxCheckChunks returns the amount of 'chunks' we split the transaction pool into for parallel validation.
2018-10-08 22:29:08 +02:00
* @param[out] chunks the chunk-count.
* @param[out] itemsPerChunk the number of transactions to be processed in a single chunk
2018-01-15 15:26:12 +00:00
*/
inline void calculateTxCheckChunks(int &chunks, int &itemsPerChunk) const {
size_t txCount = m_block.transactions().size();
2026-04-11 14:38:11 +02:00
// we remove some cores from hardware_concurrency because that avoids the problem
// that we'll end up waiting on other jobs finishing before we start validating our
// block, slowing down the entire block's throughput.
2026-04-11 19:00:23 +02:00
chunks = std::min<int>((txCount+9) / 10, std::max<int>(boost::thread::hardware_concurrency() - 4, 3));
itemsPerChunk = std::lrint(std::ceil(txCount / static_cast<float>(chunks)));
2018-01-15 15:26:12 +00:00
}
2021-11-02 10:18:24 +01:00
Block m_block;
2018-01-15 15:26:12 +00:00
CDiskBlockPos m_blockPos;
CBlockIndex *m_blockIndex;
uint256 blockId;
2018-01-15 15:26:12 +00:00
const std::uint8_t m_onResultFlags;
std::uint8_t punishment = 100;
bool m_ownsIndex = false;
bool m_checkingHeader = true;
bool m_checkPow = true;
bool m_checkMerkleRoot = true;
bool m_checkValidityOnly = false;
bool m_checkTransactionValidity = true;
2021-03-15 12:54:58 +01:00
bool m_fetchFees = false;
2018-01-15 15:26:12 +00:00
ValidationFlags flags;
const std::int32_t m_originatingNodeId;
std::string error;
Validation::RejectCodes errorCode = Validation::NotRejected;
bool isCorruptionPossible = false; //< true if failure could be result of a block-corruption in-transit
mutable std::atomic<int> m_txChunkLeftToStart;
mutable std::atomic<int> m_txChunkLeftToFinish;
mutable std::atomic<int> m_validationStatus;
mutable std::atomic<std::int64_t> m_blockFees;
2020-04-12 23:48:32 +02:00
mutable std::atomic<std::uint32_t> m_sigChecksCounted;
std::vector<std::unique_ptr<std::deque<FastUndoBlock::Item> > > m_undoItems;
std::vector<std::unique_ptr<std::deque<std::int32_t> > > m_perTxFees;
2018-01-15 15:26:12 +00:00
std::weak_ptr<ValidationEnginePrivate> m_parent;
std::weak_ptr<ValidationSettingsPrivate> m_settings;
// These children are waiting to be notified when I reach the conclusion
// my block is likely on the main chain since that means they might be as well.
std::vector<std::weak_ptr<BlockValidationState> > m_chainChildren;
2019-03-17 13:20:09 +01:00
// ---------- only used when m_validateOnly is true.
2019-03-15 19:10:47 +01:00
// for validateOnly style we omit changing the UTXO. As such we need to
// allow some way to do in-block tx spending. This structure does that.
2019-03-17 13:20:09 +01:00
// we map txid to a pair of ints. The first int is the output index. The second int is the tx's offset in block.
2019-04-01 15:34:13 +02:00
typedef boost::unordered_map<uint256, std::deque<std::pair<int, int> >, HashShortener> UnspentMap;
2019-03-17 13:20:09 +01:00
UnspentMap m_txMap;
2019-03-15 19:10:47 +01:00
// when a block is being checked for validity only (not appended) we store changes
// in this map to detect double-spends.
2019-04-01 15:34:13 +02:00
typedef boost::unordered_map<uint256, std::deque<int>, HashShortener> SpentMap;
SpentMap m_spentMap;
2019-03-17 13:20:09 +01:00
// ---------- only used when m_validateOnly is true.
2018-01-15 15:26:12 +00:00
};
struct MapHashShortener
{
inline size_t operator()(const uint256& hash) const {
return hash.GetCheapHash();
}
};
class ValidationEnginePrivate
{
public:
ValidationEnginePrivate(Validation::EngineType type);
void setSpvValidationMode(bool spv);
2018-01-15 15:26:12 +00:00
void blockHeaderValidated(std::shared_ptr<BlockValidationState> state);
void processNewBlock(std::shared_ptr<BlockValidationState> state);
2026-04-20 19:43:55 +02:00
void startOrphanWithParent(std::vector<std::shared_ptr<BlockValidationState> > &adoptees, const std::shared_ptr<BlockValidationState> &state);
2018-01-15 15:26:12 +00:00
void prepareChain();
2018-10-21 21:31:56 +02:00
void prepareChain_priv();
2018-01-15 15:26:12 +00:00
void createBlockIndexFor(const std::shared_ptr<BlockValidationState> &state);
/// called (from strand) to speed up shutdown
void cleanup();
void handleFailedBlock(const std::shared_ptr<BlockValidationState> &state);
2026-05-18 11:55:31 +02:00
// check for data from node and potentially clean it.
// ONLY CALL FROM STRAND!
void nodeBanned(int nodeId);
2019-10-14 19:22:54 +02:00
[[noreturn]] void fatal(const char *error);
2018-01-15 15:26:12 +00:00
enum ProcessingType {
CheckingHeader,
CheckingBlock
};
/// reduce blocks-in-flight counters
void blockLanded(ProcessingType type);
/// Find out if there are unscheduled blocks left to validate and schedule them.
void findMoreJobs();
inline int blocksInFlightLimit() {
2019-10-14 19:22:54 +02:00
return (int(boost::thread::hardware_concurrency()));
2018-01-15 15:26:12 +00:00
}
2020-11-18 22:08:00 +01:00
bool disconnectTip(CBlockIndex *index, bool *userClean = nullptr, bool *error = nullptr);
2018-01-15 15:26:12 +00:00
2020-03-28 22:49:53 +01:00
boost::asio::io_context::strand strand;
2018-01-15 15:26:12 +00:00
std::atomic<bool> shuttingDown;
std::mutex lock;
std::condition_variable waitVariable;
/* We have some *InFlight limits here.
* First of all, they are only loosly controlled, not very strict.
* So, we share a threadPool with the entire application and as such we should not overwhelm it with jobs.
* The fact that there are two checks here is because blocksInFlight is used for blocks that end up in the sequence of
* * checks2HaveParentHeaders
* * updateUtxoAndStartValidation
* ** checkSignaturesChunk
*
* The step to go from check2 to the utxo method is serialized, meaning only one at a time is doing an utxo check.
* This would hinder the total thoughput if we made this stop the headersInFlight additions, and as such there are
* two counters.
*/
std::atomic<int> headersInFlight; // indicates headers being checked, can grow upto, but not including blocksInFlightLimit()
std::atomic<int> blocksInFlight; // indicates blocks being checked, can grow upto, but not including blocksInFlightLimit()
CChain *blockchain;
std::atomic<CBlockIndex*> tip; ///< Since blockchain is only usable from the strand, copy this here for cross-thread usage.
ValidationFlags tipFlags; ///< validation flags representative of the tip.
CTxMemPool *mempool;
uint256 hashPrevBestCoinBase;
std::list<std::shared_ptr<BlockValidationState> > orphanBlocks;
2020-02-27 14:43:30 +01:00
typedef boost::unordered_map<uint256, std::shared_ptr<BlockValidationState>, MapHashShortener> StatesMap;
StatesMap blocksBeingValidated;
2018-01-15 15:26:12 +00:00
std::mutex recentRejectsLock;
CRollingBloomFilter recentTxRejects;
std::weak_ptr<ValidationEnginePrivate> me;
const Validation::EngineType engineType;
2021-03-09 16:45:16 +01:00
bool fetchFeeForMetaBlocks = false;
2026-05-14 15:21:44 +02:00
bool spvValidationMode = false;
2021-03-09 16:45:16 +01:00
2018-01-15 15:26:12 +00:00
private:
int lastFullBlockScheduled;
int previousPrintedHeaderHeight = 0;
2018-01-15 15:26:12 +00:00
#ifdef ENABLE_BENCHMARKS
public:
std::atomic<long> m_headerCheckTime;
std::atomic<long> m_basicValidityChecks;
std::atomic<long> m_contextCheckTime;
std::atomic<long> m_utxoTime;
std::atomic<long> m_validationTime;
std::atomic<long> m_loadingTime;
std::atomic<long> m_mempoolTime;
std::atomic<long> m_walletTime;
#endif
};
#endif