diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 09361229..6aa41b70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,6 +127,7 @@ Existing maintainers can resign, or be subject to a vote for removal at the behe * [legleux](https://github.com/legleux) (Ripple) * [undertome](https://github.com/undertome) (Ripple) * [godexsoft](https://github.com/godexsoft) (Ripple) +* [officialfrancismendoza](https://github.com/officialfrancismendoza) (Ripple) ## Honorable ex-Maintainers diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..1da44b70 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,3 @@ +PROJECT_NAME = "Clio" +INPUT = src +RECURSIVE = YES \ No newline at end of file diff --git a/src/backend/BackendInterface.h b/src/backend/BackendInterface.h index ee0a8987..a26f3e55 100644 --- a/src/backend/BackendInterface.h +++ b/src/backend/BackendInterface.h @@ -18,6 +18,12 @@ namespace Backend { +/** + * @brief Throws an error when database read time limit is exceeded. + * + * This class is throws an error when read time limit is exceeded but + * is also paired with a separate class to retry the connection. + */ class DatabaseTimeout : public std::exception { public: @@ -28,6 +34,14 @@ public: } }; +/** + * @brief Separate class that reattempts connection after time limit. + * + * @tparam F Represents a class of handlers for Cassandra database. + * @param func Instance of Cassandra database handler class. + * @param waitMs Is the arbitrary time limit of 500ms. + * @return auto + */ template auto retryOnTimeout(F func, size_t waitMs = 500) @@ -48,19 +62,48 @@ retryOnTimeout(F func, size_t waitMs = 500) } } +/** + * @brief Passes in serialized handlers in an asynchronous fashion. + * + * Note that the synchronous auto passes handlers critical to supporting + * the Clio backend. The coroutine types are checked if same/different. + * + * @tparam F Represents a class of handlers for Cassandra database. + * @param f R-value instance of Cassandra handler class. + * @return auto + */ template auto synchronous(F&& f) { + /** @brief Serialized handlers and their execution. + * + * The ctx class is converted into a serialized handler, also named + * ctx, and is used to pass a stream of data into the method. + */ boost::asio::io_context ctx; boost::asio::io_context::strand strand(ctx); std::optional work; + /*! @brief Place the ctx within the vector of serialized handlers. */ work.emplace(ctx); + /** + * @brief If/else statements regarding coroutine type matching. + * + * R is the currently executing coroutine that is about to get passed. + * If corountine types do not match, the current one's type is stored. + */ using R = typename std::result_of::type; if constexpr (!std::is_same::value) { + /** + * @brief When the coroutine type is the same + * + * The spawn function enables programs to implement asynchronous logic + * in a synchronous manner. res stores the instance of the currently + * executing coroutine, yield. The different type is returned. + */ R res; boost::asio::spawn( strand, [&f, &work, &res](boost::asio::yield_context yield) { @@ -73,6 +116,7 @@ synchronous(F&& f) } else { + /*! @brief When the corutine type is different, run as normal. */ boost::asio::spawn( strand, [&f, &work](boost::asio::yield_context yield) { f(yield); @@ -83,6 +127,13 @@ synchronous(F&& f) } } +/** + * @brief Reestablishes synchronous connection on timeout. + * + * @tparam Represents a class of handlers for Cassandra database. + * @param f R-value instance of Cassandra database handler class. + * @return auto + */ template auto synchronousAndRetryOnTimeout(F&& f) @@ -90,13 +141,29 @@ synchronousAndRetryOnTimeout(F&& f) return retryOnTimeout([&]() { return synchronous(f); }); } +/*! @brief Handles ledger and transaction backend data. */ class BackendInterface { + /** + * @brief Shared mutexes and a cache for the interface. + * + * rngMutex is a shared mutex. Shared mutexes prevent shared data + * from being accessed by multiple threads and has two levels of + * access: shared and exclusive. + */ protected: mutable std::shared_mutex rngMtx_; std::optional range; SimpleCache cache_; + /** + * @brief Public read methods + * + * All of these reads methods can throw DatabaseTimeout. When writing + * code in an RPC handler, this exception does not need to be caught: + * when an RPC results in a timeout, an error is returned to the client. + */ + public: BackendInterface(clio::Config const& config) { @@ -105,14 +172,16 @@ public: { } - // *** public read methods *** - // All of these reads methods can throw DatabaseTimeout. When writing code - // in an RPC handler, this exception does not need to be caught: when an RPC - // results in a timeout, an error is returned to the client + /*! @brief LEDGER METHODS */ public: - // *** ledger methods - // - + /** + * @brief Cache that holds states of the ledger + * + * const version holds the original cache state; the other tracks + * historical changes. + * + * @return SimpleCache const& + */ SimpleCache const& cache() const { @@ -125,19 +194,23 @@ public: return cache_; } + /*! @brief Fetches a specific ledger by sequence number. */ virtual std::optional fetchLedgerBySequence( std::uint32_t const sequence, boost::asio::yield_context& yield) const = 0; + /*! @brief Fetches a specific ledger by hash. */ virtual std::optional fetchLedgerByHash( ripple::uint256 const& hash, boost::asio::yield_context& yield) const = 0; + /*! @brief Fetches the latest ledger sequence. */ virtual std::optional fetchLatestLedgerSequence(boost::asio::yield_context& yield) const = 0; + /*! @brief Fetches the current ledger range while locking that process */ std::optional fetchLedgerRange() const { @@ -145,6 +218,14 @@ public: return range; } + /** + * @brief Updates the range of sequences to be tracked. + * + * Function that continues updating the range sliding window or creates + * a new sliding window once the maxSequence limit has been reached. + * + * @param newMax Unsigned 32-bit integer representing new max of range. + */ void updateRange(uint32_t newMax) { @@ -156,20 +237,53 @@ public: range->maxSequence = newMax; } + /** + * @brief Returns the fees for specific transactions. + * + * @param seq Unsigned 32-bit integer reprsenting sequence. + * @param yield The currently executing coroutine. + * @return std::optional + */ std::optional fetchFees(std::uint32_t const seq, boost::asio::yield_context& yield) const; - // *** transaction methods + /*! @brief TRANSACTION METHODS */ + /** + * @brief Fetches a specific transaction. + * + * @param hash Unsigned 256-bit integer representing hash. + * @param yield The currently executing coroutine. + * @return std::optional + */ virtual std::optional fetchTransaction( ripple::uint256 const& hash, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches multiple transactions. + * + * @param hashes Unsigned integer value representing a hash. + * @param yield The currently executing coroutine. + * @return std::vector + */ virtual std::vector fetchTransactions( std::vector const& hashes, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches all transactions for a specific account + * + * @param account A specific XRPL Account, speciifed by unique type + * accountID. + * @param limit Paging limit for how many transactions can be returned per + * page. + * @param forward Boolean whether paging happens forwards or backwards. + * @param cursor Important metadata returned every time paging occurs. + * @param yield Currently executing coroutine. + * @return TransactionsAndCursor + */ virtual TransactionsAndCursor fetchAccountTransactions( ripple::AccountID const& account, @@ -178,23 +292,56 @@ public: std::optional const& cursor, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches all transactions from a specific ledger. + * + * @param ledgerSequence Unsigned 32-bit integer for latest total + * transactions. + * @param yield Currently executing coroutine. + * @return std::vector + */ virtual std::vector fetchAllTransactionsInLedger( std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches all transaction hashes from a specific ledger. + * + * @param ledgerSequence Standard unsigned integer. + * @param yield Currently executing coroutine. + * @return std::vector + */ virtual std::vector fetchAllTransactionHashesInLedger( std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const = 0; - // *** NFT methods + /*! @brief NFT methods */ + /** + * @brief Fetches a specific NFT + * + * @param tokenID Unsigned 256-bit integer. + * @param ledgerSequence Standard unsigned integer. + * @param yield Currently executing coroutine. + * @return std::optional + */ virtual std::optional fetchNFT( ripple::uint256 const& tokenID, std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches all transactions for a specific NFT. + * + * @param tokenID Unsigned 256-bit integer. + * @param limit Paging limit as to how many transactions return per page. + * @param forward Boolean whether paging happens forwards or backwards. + * @param cursorIn Represents transaction number and ledger sequence. + * @param yield Currently executing coroutine is passed in as input. + * @return TransactionsAndCursor + */ virtual TransactionsAndCursor fetchNFTTransactions( ripple::uint256 const& tokenID, @@ -203,38 +350,74 @@ public: std::optional const& cursorIn, boost::asio::yield_context& yield) const = 0; - // *** state data methods + /*! @brief STATE DATA METHODS */ + /** + * @brief Fetches a specific ledger object: vector of unsigned chars + * + * @param key Unsigned 256-bit integer. + * @param sequence Unsigned 32-bit integer. + * @param yield Currently executing coroutine. + * @return std::optional + */ std::optional fetchLedgerObject( ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context& yield) const; + /** + * @brief Fetches all ledger objects: a vector of vectors of unsigned chars. + * + * @param keys Unsigned 256-bit integer. + * @param sequence Unsigned 32-bit integer. + * @param yield Currently executing coroutine. + * @return std::vector + */ std::vector fetchLedgerObjects( std::vector const& keys, std::uint32_t const sequence, boost::asio::yield_context& yield) const; + /*! @brief Virtual function version of fetchLedgerObject */ virtual std::optional doFetchLedgerObject( ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context& yield) const = 0; + /*! @brief Virtual function version of fetchLedgerObjects */ virtual std::vector doFetchLedgerObjects( std::vector const& keys, std::uint32_t const sequence, boost::asio::yield_context& yield) const = 0; + /** + * @brief Returns the difference between ledgers: vector of objects + * + * Objects are made of a key value, vector of unsigned chars (blob), + * and a boolean detailing whether keys and blob match. + * + * @param ledgerSequence Standard unsigned integer. + * @param yield Currently executing coroutine. + * @return std::vector + */ virtual std::vector fetchLedgerDiff( std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const = 0; - // Fetches a page of ledger objects, ordered by key/index. - // Used by ledger_data + /** + * @brief Fetches a page of ledger objects, ordered by key/index. + * + * @param cursor Important metadata returned every time paging occurs. + * @param ledgerSequence Standard unsigned integer. + * @param limit Paging limit as to how many transactions returned per page. + * @param outOfOrder Boolean on whether ledger page is out of order. + * @param yield Currently executing coroutine. + * @return LedgerPage + */ LedgerPage fetchLedgerPage( std::optional const& cursor, @@ -243,26 +426,37 @@ public: bool outOfOrder, boost::asio::yield_context& yield) const; - // Fetches the successor to key/index + /*! @brief Fetches successor object from key/index. */ std::optional fetchSuccessorObject( ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const; + /*! @brief Fetches successor key from key/index. */ std::optional fetchSuccessorKey( ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const; - // Fetches the successor to key/index + /*! @brief Virtual function version of fetchSuccessorKey. */ virtual std::optional doFetchSuccessorKey( ripple::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context& yield) const = 0; + /** + * @brief Fetches book offers. + * + * @param book Unsigned 256-bit integer. + * @param ledgerSequence Standard unsigned integer. + * @param limit Pagaing limit as to how many transactions returned per page. + * @param cursor Important metadata returned every time paging occurs. + * @param yield Currently executing coroutine. + * @return BookOffersPage + */ BookOffersPage fetchBookOffers( ripple::uint256 const& book, @@ -271,6 +465,16 @@ public: std::optional const& cursor, boost::asio::yield_context& yield) const; + /** + * @brief Returns a ledger range + * + * Ledger range is a struct of min and max sequence numbers). Due to + * the use of [&], which denotes a special case of a lambda expression + * where values found outside the scope are passed by reference, wrt the + * currently executing coroutine. + * + * @return std::optional + */ std::optional hardFetchLedgerRange() const { @@ -279,27 +483,52 @@ public: }); } + /*! @brief Virtual function equivalent of hardFetchLedgerRange. */ virtual std::optional hardFetchLedgerRange(boost::asio::yield_context& yield) const = 0; - // Doesn't throw DatabaseTimeout. Should be used with care. + /*! @brief Fetches ledger range but doesn't throw timeout. Use with care. */ std::optional hardFetchLedgerRangeNoThrow() const; - // Doesn't throw DatabaseTimeout. Should be used with care. + /*! @brief Fetches ledger range but doesn't throw timeout. Use with care. */ std::optional hardFetchLedgerRangeNoThrow(boost::asio::yield_context& yield) const; + /** + * @brief Writes to a specific ledger. + * + * @param ledgerInfo Const on ledger information. + * @param ledgerHeader r-value string representing ledger header. + */ virtual void writeLedger( ripple::LedgerInfo const& ledgerInfo, std::string&& ledgerHeader) = 0; + /** + * @brief Writes a new ledger object. + * + * The key and blob are r-value references and do NOT have memory addresses. + * + * @param key String represented as an r-value. + * @param seq Unsigned integer representing a sequence. + * @param blob r-value vector of unsigned characters (blob). + */ virtual void writeLedgerObject( std::string&& key, std::uint32_t const seq, std::string&& blob); + /** + * @brief Writes a new transaction. + * + * @param hash r-value reference. No memory address. + * @param seq Unsigned 32-bit integer. + * @param date Unsigned 32-bit integer. + * @param transaction r-value reference. No memory address. + * @param metadata r-value refrence. No memory address. + */ virtual void writeTransaction( std::string&& hash, @@ -308,53 +537,101 @@ public: std::string&& transaction, std::string&& metadata) = 0; + /** + * @brief Write a new NFT. + * + * @param data Passed in as an r-value reference. + */ virtual void writeNFTs(std::vector&& data) = 0; + /** + * @brief Write a new set of account transactions. + * + * @param data Passed in as an r-value reference. + */ virtual void writeAccountTransactions(std::vector&& data) = 0; + /** + * @brief Write a new transaction for a specific NFT. + * + * @param data Passed in as an r-value reference. + */ virtual void writeNFTTransactions(std::vector&& data) = 0; + /** + * @brief Write a new successor. + * + * @param key Passed in as an r-value reference. + * @param seq Unsigned 32-bit integer. + * @param successor Passed in as an r-value reference. + */ virtual void writeSuccessor( std::string&& key, std::uint32_t const seq, std::string&& successor) = 0; - // Tell the database we are about to begin writing data for a particular - // ledger. + /*! @brief Tells database we will write data for a specific ledger. */ virtual void startWrites() const = 0; - // Tell the database we have finished writing all data for a particular - // ledger - // TODO change the return value to represent different results. committed, - // write conflict, errored, successful but not committed + /** + * @brief Tells database we finished writing all data for a specific ledger. + * + * TODO: change the return value to represent different results: + * Committed, write conflict, errored, successful but not committed + * + * @param ledgerSequence Const unsigned 32-bit integer on ledger sequence. + * @return true + * @return false + */ bool finishWrites(std::uint32_t const ledgerSequence); + /** + * @brief Selectively delets parts of the database. + * + * @param numLedgersToKeep Unsigned 32-bit integer on number of ledgers to + * keep. + * @param yield Currently executing coroutine. + * @return true + * @return false + */ virtual bool doOnlineDelete( std::uint32_t numLedgersToKeep, boost::asio::yield_context& yield) const = 0; - // Open the database. Set up all of the necessary objects and - // datastructures. After this call completes, the database is ready for - // use. + /** + * @brief Opens the database + * + * Open the database. Set up all of the necessary objects and + * datastructures. After this call completes, the database is + * ready for use. + * + * @param readOnly Boolean whether ledger is read only. + */ virtual void open(bool readOnly) = 0; - // Close the database, releasing any resources + /*! @brief Closes the database, releasing any resources. */ virtual void close(){}; virtual bool isTooBusy() const = 0; - // *** private helper methods private: + /** + * @brief Private helper method to write ledger object + * + * @param key r-value string representing key. + * @param seq Unsigned 32-bit integer representing sequence. + * @param blob r-value vector of unsigned chars. + */ virtual void doWriteLedgerObject( std::string&& key,