#pragma once // Copyright (c) 2018-2019 David Burkett // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include /// /// An interface for interacting with MMRs. /// class IMMR { public: using Ptr = std::shared_ptr; virtual ~IMMR() = default; /// /// Adds a new leaf with the given data to the end of the MMR. /// /// The serialized leaf data. /// The LeafIndex where the data was added. virtual mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) = 0; mmr::LeafIndex Add(const std::vector& data) { return AddLeaf(mmr::Leaf::Create(GetNextLeafIdx(), data)); } mmr::LeafIndex Add(const Traits::ISerializable& serializable) { return AddLeaf(mmr::Leaf::Create(GetNextLeafIdx(), serializable.Serialized())); } /// /// Retrieves the leaf at the given leaf index. /// /// The leaf index. /// The retrieved Leaf. /// std::exception if index is beyond the end of the MMR. /// std::exception if leaf at given index has been pruned. virtual mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const = 0; /// /// Retrieves the hash at the given MMR index. /// /// The index, which may or may not be a leaf. /// The hash of the leaf or node at the index. /// std::exception if index is beyond the end of the MMR. /// std::exception if node at the given index has been pruned. virtual mw::Hash GetHash(const mmr::Index& idx) const = 0; /// /// Retrieves the index of the next leaf to be added to the MMR. /// eg. If the MMR contains 3 leaves (0, 1, 2), this will return LeafIndex 3. /// /// The next leaf index. virtual mmr::LeafIndex GetNextLeafIdx() const noexcept = 0; /// /// Gets the number of nodes in the MMR, including those that have been pruned. /// eg. If the MMR contains leaves 0, 1, and 2, this will return 4. /// /// The number of (pruned and unpruned) nodes in the MMR. uint64_t GetNumNodes() const noexcept { return GetNextLeafIdx().GetPosition(); } /// /// Gets the number of leaves in the MMR, including those that have been pruned. /// eg. If the MMR contains leaves 0, 1, and 2, this will return 3. /// /// The number of (pruned and unpruned) leaves in the MMR. virtual uint64_t GetNumLeaves() const noexcept = 0; /// /// "Rewinds" the MMR to the given number of leaves. /// In other words, it shrinks the MMR to the given size. /// /// The total number of (pruned and unpruned) leaves in the MMR. virtual void Rewind(const uint64_t numLeaves) = 0; void Rewind(const mmr::LeafIndex& nextLeaf) { Rewind(nextLeaf.Get()); } /// /// Unlike a Merkle tree, an MMR generally has no single root so we need a method to compute one. /// The process we use is called "bagging the peaks." We first identify the peaks (nodes with no parents). /// We then "bag" them by hashing them iteratively from the right, using the total size of the MMR as prefix. /// /// The root hash of the MMR. mw::Hash Root() const; /// /// Adds the given leaves to the MMR. /// This also updates the database and MMR files when the MMR is not a cache. /// Typically, this is called from a derived PMMRCache when its changes are being flushed/committed. /// /// The index of the MMR files. This should be incremented with each write. /// The LeafIndex of the first leaf being added. /// The leaves being added to the MMR. /// A wrapper around a DB Batch. Required when called for an MMR (ie, non-cache). virtual void BatchWrite( const uint32_t file_index, const mmr::LeafIndex& firstLeafIdx, const std::vector& leaves, const std::unique_ptr& pBatch ) = 0; }; /// /// Memory-only MMR with no pruning or file I/O. /// class MemMMR : public IMMR { public: using Ptr = std::shared_ptr; MemMMR() = default; virtual ~MemMMR() = default; mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final; mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final; mw::Hash GetHash(const mmr::Index& idx) const final; mmr::LeafIndex GetNextLeafIdx() const noexcept final; uint64_t GetNumLeaves() const noexcept final; void Rewind(const uint64_t numLeaves) final; void BatchWrite( const uint32_t, const mmr::LeafIndex&, const std::vector&, const std::unique_ptr&) final {} private: std::vector m_hashes; std::vector m_leaves; }; class PMMR : public IMMR { public: using Ptr = std::shared_ptr; static PMMR::Ptr Open( const char dbPrefix, const FilePath& mmr_dir, const uint32_t file_index, const mw::DBWrapper::Ptr& pDBWrapper, const std::shared_ptr& pPruneList ); PMMR(const char dbPrefix, const FilePath& mmr_dir, const AppendOnlyFile::Ptr& pHashFile, const std::shared_ptr& pDBWrapper, const PruneList::CPtr& pPruneList ) : m_dbPrefix(dbPrefix), m_dir(mmr_dir), m_pHashFile(pHashFile), m_pDatabase(pDBWrapper), m_pPruneList(pPruneList) { } virtual ~PMMR() = default; static FilePath GetPath(const FilePath& dir, const char prefix, const uint32_t file_index); mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final; mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final; mw::Hash GetHash(const mmr::Index& idx) const final; mmr::LeafIndex GetNextLeafIdx() const noexcept final { return mmr::LeafIndex::At(GetNumLeaves()); } uint64_t GetNumLeaves() const noexcept final; uint64_t GetNumNodes() const noexcept; void Rewind(const uint64_t numLeaves) final; void BatchWrite( const uint32_t file_index, const mmr::LeafIndex& firstLeafIdx, const std::vector& leaves, const std::unique_ptr& pBatch ) final; void Cleanup(const uint32_t current_file_index) const; private: char m_dbPrefix; FilePath m_dir; AppendOnlyFile::Ptr m_pHashFile; std::vector m_leaves; std::map m_leafMap; std::shared_ptr m_pDatabase; PruneList::CPtr m_pPruneList; }; class PMMRCache : public IMMR { public: using Ptr = std::shared_ptr; PMMRCache(const IMMR::Ptr& pBacked) : m_pBase(pBacked), m_firstLeaf(pBacked->GetNextLeafIdx()){ } virtual ~PMMRCache() = default; mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final; mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final; mmr::LeafIndex GetNextLeafIdx() const noexcept final; uint64_t GetNumLeaves() const noexcept final { return GetNextLeafIdx().Get(); } mw::Hash GetHash(const mmr::Index& idx) const final; void Rewind(const uint64_t numLeaves) final; void BatchWrite( const uint32_t file_index, const mmr::LeafIndex& firstLeafIdx, const std::vector& leaves, const std::unique_ptr& pBatch ) final; void Flush(const uint32_t index, const std::unique_ptr& pBatch); private: IMMR::Ptr m_pBase; mmr::LeafIndex m_firstLeaf; std::vector m_leaves; std::vector m_nodes; };