/* * This file is part of the Flowee project * Copyright (C) 2020-2026 Tom Zander * * 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 . */ #ifndef FLOWEE_BLOCKCHAIN_H #define FLOWEE_BLOCKCHAIN_H #include "BlockHeader.h" #include "P2PNet.h" #include #include #include #include #include #include #include class DownloadManager; namespace Streaming { class P2PBuilder; } class Blockchain { public: Blockchain(DownloadManager *downloadManager, const boost::filesystem::path &basedir, P2PNet::Chain chain); /** * The p2p message 'getheaders' asks the remote peer to identify and append information * on blockchain information. This call generates a current message requesting extra * headers we don't know about yet. * * Notice that if we are pretty sure we are at the tip, this request is legally answerable * with a one-byte message. Namely stating that it knows no headers we know. * As a result it may be useful to provide skipHeaders with a low number that will then * trigger the responding peer to actually provide that number of headers since we pretend * we don't know them. * * @param skipHeaders if non-zero, effectively ask the * peer to provide that number of headers. */ Message createGetHeadersRequest(int skipHeaders = 0); void processBlockHeaders(Message message, int peerId); /** * Set a raw mapped blockchain data, a simple list of headers. * Block headers, as they are stored in the blockchain, at 80 bytes each can be provided * here as a lost in order to avoid downloading them from peers. * We require that the blocks are in-order (will not be checked), starting with the * geneesis block (which is checked). * * This setter should be used BEFORE the creation of the Blockchain instance, which means * before the creation of the DownloadManager instance. * * Notice that after setting this, the save() will skip saving any headers provided by * the static data. */ static void setStaticChain(const unsigned char *data, int64_t size, const std::string &infoFile); /** * In order to promote a standard blockheaders file to be useful as a static chain, * we need to create an extra metadata file by doing the expensive operations on them * and saving the results. * This method is meant to create the file 'meta' as a result of the input 'blockchain' file. */ static bool createStaticHeaders(const std::string &blockchain, const std::string &meta); /** * This is a specialized function that allows a checkpoint based static chain (so * not starting at genesis) to be extended back in time. To an earlier checkpoint. */ void replaceStaticChain(const unsigned char *data, int64_t size, const std::string &infoFile); /** * Return the chain-height that we actually are at, based on validated headers. */ int height() const; /** * Return the chain-height that based on the date/time we expect to be at. */ int expectedBlockHeight() const; /** * Returns true if the block-id is part of the main chain (so far). */ bool isKnown(const uint256 &blockId) const; /** * Returns the block height for a certain block. * Please notice the isKnown which is a cheaper version if all you need is a yes/no. */ int blockHeightFor(const uint256 &blockId) const; /** * This returns the block that was created just after the requested timestamp. * * If the timestamp is further in the future then 'Tip' then tip's height + 1 is given. * If the timestamp is before oldestKnownBlockHeight's time, we return that height. */ int blockHeightAtTime(uint32_t timestamp) const; /// In case of starting from a checkpoint, this returns the checkpoint height. Otherwise 0 int oldestKnownBlockHeight() const; /** * Return the block header for a block at a certain height. * Height 0 is the genesis block. */ BlockHeader block(int height) const; /// re-load the chain. Also called from the constructor. void load(); /// save the chain void save(); /// The origin of header data. enum DataSourceType { StaticHeadersSource, ///< From a Flowee provided file. DataFileSource ///< From the Internet. }; struct DataSource { DataSourceType source; int startBlock = 0; int endBlock = 0; }; /** * Return the specific sources of our blockchain-data. * This exports the explicit amount of block-headers available and their source. */ std::vector dataSources() const; /** * A checkpoint. * This is a historically significant point in the blockchain that has happened * far enough in the past to simply ship this in the sources of Flowee and * that way have certainty that the publicly available data matches what we * expect. */ struct CheckPoint { int height = 0; uint256 hash; arith_uint256 chainWork; }; /** * Return the known checkpoints. */ std::vector checkpoints() const; private: void createMainchainGenesis(); void loadMainchainCheckpoints(); void createTestnet4Genesis(); void loadTestnet4Checkpoints(); void loadStaticChain(const unsigned char *data, int64_t dataSize, std::ifstream &infoFile); void createGenericGenesis(BlockHeader genesis); /// replace all the saved files with one that has all the data. void compressSaveFiles(const boost::system::error_code &error); void startSaveTimer(); /** * Load a file that has no index (meta file) by actually parsing and doing math * on each individual header. * It also automatically creates a meta file for faster load next time, if the * \a 'meta' is not empty. */ void loadSlow(std::ifstream &in, const std::string &meta); // helper method to access m_longestChain BlockHeader blockAt_priv(size_t i) const; // helper method to access m_longestChain int blockHeight_priv() const; // private helper function to determine how many block-headers we should skip loading for a file starting with \a bh int skipBlocksOfFile(const BlockHeader &bh) const; mutable std::recursive_mutex m_lock; const boost::filesystem::path m_basedir; std::vector m_longestChain; struct ChainTip { uint256 tip; int height; arith_uint256 chainWork; }; ChainTip m_tip; typedef std::unordered_map BlockHeightMap; BlockHeightMap m_blockHeight; DownloadManager *m_dlmanager; boost::asio::steady_timer m_saveTimer; bool m_saveTimerStarted = false; // consensus const uint256 powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); std::vector m_checkpoints; int m_chainStartPoint = 0; const unsigned char *m_staticChain = nullptr; int m_numStaticHeaders = 0; // including genesis, so this is height + 1 int m_fileCount = 0; // number of 'blockchainN' files we own int m_lastSavedHeader = 0; }; #endif