mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
* Fix: Don't flag consensus as stalled prematurely (#5658) Fix stalled consensus detection to prevent false positives in situations where there are no disputed transactions. Stalled consensus detection was added to 2.5.0 in response to a network consensus halt that caused a round to run for over an hour. However, it has a flaw that makes it very easy to have false positives. Those false positives are usually mitigated by other checks that prevent them from having an effect, but there have been several instances of validators "running ahead" because there are circumstances where the other checks are "successful", allowing the stall state to be checked. * Set version to 2.5.1 * fix: Skip processing transaction batch if the batch is empty (#5670) Avoids an assertion failure in NetworkOPsImp::apply in the unlikely event that all incoming transactions are invalid. * Fix: EscrowTokenV1 (#5571) * resolves an accounting inconsistency in MPT escrows where transfer fees were not properly handled when unlocking escrowed tokens. * refactor: Wrap GitHub CI conditionals in curly braces (#5796) This change wraps all GitHub conditionals in `${{ .. }}`, both for consistency and to reduce unexpected failures, because it was previously noticed that not all conditionals work without those curly braces. * Only notify clio for PRs targeting the release and master branches (#5794) Clio should only be notified when releases are about to be made, instead of for all PR, so this change only notifies Clio when a PR targets the release or master branch. * Support DynamicMPT XLS-94d (#5705) * extends the functionality of the MPTokenIssuanceSet transaction, allowing the issuer to update fields or flags that were explicitly marked as mutable during creation. * Bugfix: Adds graceful peer disconnection (#5669) The XRPL establishes connections in three stages: first a TCP connection, then a TLS/SSL handshake to secure the connection, and finally an upgrade to the bespoke XRP Ledger peer-to-peer protocol. During connection termination, xrpld directly closes the TCP connection, bypassing the TLS/SSL shutdown handshake. This makes peer disconnection diagnostics more difficult - abrupt TCP termination appears as if the peer crashed rather than disconnected gracefully. This change refactors the connection lifecycle with the following changes: - Enhanced outgoing connection logic with granular timeouts for each connection stage (TCP, TLS, XRPL handshake) to improve diagnostic capabilities - Updated both PeerImp and ConnectAttempt to use proper asynchronous TLS shutdown procedures for graceful connection termination * Downgrade to boost 1.83 * Set version to 2.6.1-rc1 * chore: Use self hosted windows runners (#5780) This changes switches from the GitHub-managed Windows runners to self-hosted runners to significantly reduce build time. * Rename mutable flags (#5797) This is a minor change on top of #5705 * fix(amendment): Add missing fields for keylets to ledger objects (#5646) This change adds a fix amendment (`fixIncludeKeyletFields`) that adds: * `sfSequence` to `Escrow` and `PayChannel` * `sfOwner` to `SignerList` * `sfOracleDocumentID` to `Oracle` This ensures that all ledger entries hold all the information needed to determine their keylet. * chore: Limits CI build and test parallelism to reduce resource contention (#5799) GitHub runners have a limit on how many concurrent jobs they can actually process (even though they will try to run them all at the same time), and similarly the Conan remote cannot handle hundreds of concurrent requests. Previously, the Conan dependency uploading was already limited to max 10 jobs running in parallel, and this change makes the same change to the build+test workflow. * chore: Build and test all configs for daily scheduled run (#5801) This change re-enables building and testing all configurations, but only for the daily scheduled run. Previously all configurations were run for each merge into the develop branch, but that overwhelmed both the GitHub runners and the Conan remote, and thus they were limited to just a subset of configurations. Now that the number of jobs is limited via `max-parallel: 10`, we should be able to safely enable building all configurations again. However, building them all once a day instead of for each PR merge should be sufficient. * chore: Add unit tests dir to code coverage excludes (#5803) This change excludes unit test code from code coverage reporting. * refactor: Modularise ledger (#5493) This change moves the ledger code to libxrpl. * Mark PermissionDelegation as unsupported * Set version to 2.6.1-rc2 * Miscellaneous refactors and updates (#5590) - Added a new Invariant: `ValidPseudoAccounts` which checks that all pseudo-accounts behave consistently through creation and updates, and that no "real" accounts look like pseudo-accounts (which means they don't have a 0 sequence). - `to_short_string(base_uint)`. Like `to_string`, but only returns the first 8 characters. (Similar to how a git commit ID can be abbreviated.) Used as a wrapped sink to prefix most transaction-related messages. More can be added later. - `XRPL_ASSERT_PARTS`. Convenience wrapper for `XRPL_ASSERT`, which takes the `function` and `description` as separate parameters. - `SField::sMD_PseudoAccount`. Metadata option for `SField` definitions to indicate that the field, if set in an `AccountRoot` indicates that account is a pseudo-account. Removes the need for hard-coded field lists all over the place. Added the flag to `AMMID` and `VaultID`. - Added functionality to `SField` ctor to detect both code and name collisions using asserts. And require all SFields to have a name - Convenience type aliases `STLedgerEntry::const_pointer` and `STLedgerEntry::const_ref`. (`SLE` is an alias to `STLedgerEntry`.) - Generalized `feeunit.h` (`TaggedFee`) into `unit.h` (`ValueUnit`) and added new "BIPS"-related tags for future use. Also refactored the type restrictions to use Concepts. - Restructured `transactions.macro` to do two big things 1. Include the `#include` directives for transactor header files directly in the macro file. Removes the need to update `applySteps.cpp` and the resulting conflicts. 2. Added a `privileges` parameter to the `TRANSACTION` macro, which specifies some of the operations a transaction is allowed to do. These `privileges` are enforced by invariant checks. Again, removed the need to update scattered lists of transaction types in various checks. - Unit tests: 1. Moved more helper functions into `TestHelpers.h` and `.cpp`. 2. Cleaned up the namespaces to prevent / mitigate random collisions and ambiguous symbols, particularly in unity builds. 3. Generalized `Env::balance` to add support for `MPTIssue` and `Asset`. 4. Added a set of helper classes to simplify `Env` transaction parameter classes: `JTxField`, `JTxFieldWrapper`, and a bunch of classes derived or aliased from it. For an example of how awesome it is, check the changes `src/test/jtx/escrow.h` for how much simpler the definitions are for `finish_time`, `cancel_time`, `condition`, and `fulfillment`. 5. Generalized several of the amount-related helper classes to understand `Asset`s. 6. `env.balance` for an MPT issuer will return a negative number (or 0) for consistency with IOUs. * refactor: Simplify STParsedJSON with some helper functions (#5591) - Add code coverage for STParsedJSON edge cases Co-authored-by: Denis Angell <dangell@transia.co> * test: Add STInteger and STParsedJSON tests (#5726) This change is to improve code coverage (and to simplify #5720 and #5725); there is otherwise no change in functionality. The change adds basic tests for `STInteger` and `STParsedJSON`, so it becomes easier to test smaller changes to the types, as well as removes `STParsedJSONArray`, since it is not used anywhere (including in Clio). * Revert "Update Conan dependencies: OpenSSL" (#5807) This change reverts #5617, because it will require extensive testing that will take up more time than we have before the next scheduled release. Reverting this change does not mean we are abandoning it. We aim to pick it back up once there's a sufficient time window to allow for testing on multiple distros running a mixture of OpenSSL 1.x and 3.x. * docs: Add warning about using std::counting_semaphore (#5595) This adds a comment to avoid using `std::counting_semaphore` until the minimum compiler versions of GCC and Clang have been updated to no longer contain the bug that is present in older compilers. * Improve ValidatorList invalid UNL manifest logging (#5804) This change raises logging severity from `INFO` to `WARN` when handling UNL manifest signed with an unexpected / invalid key. It also changes the internal error code for an invalid format of UNL manifest to `invalid` (from `untrusted`). This is a follow up to problems experienced by an UNL node due to old manifest key configured in `validators.txt`, which would be easier to diagnose with improved logging. It also replaces a log line with `UNREACHABLE` for an impossible situation when we match UNL manifest key against a configured key which has an invalid type (we cannot configure such a key because of checks when loading configured keys). * chore: Pin all CI Docker tags (#5813) To avoid surprises and ensure reproducibility, this change pins all CI Docker image tags to the latest version in the XRPLF/CI repo. * change `fixPriceOracleOrder` to `Supported::yes` (#5749) * fix: Address http header case sensitivity (#5767) This change makes the regex in `HttpClient.cpp` that matches the content-length http header case insensitive to improve compatibility, as http headers are case insensitive. * test: add more comprehensive tests for `FeeVote` (#5746) This change adds more comprehensive tests for the `FeeVote` module, which previously only checked the basics, and not the more comprehensive flows in that class. * ci: Call all reusable workflows reusable (#5818) * Add `STInt32` as a new `SType` (#5788) This change adds `STInt32` as a new `SType` under the `STInteger` umbrella, with `SType` value `12`. This is the first and only `STInteger` type that supports negative values. * switch `fixIncludeKeyletFields` to `Supported::yes` (#5819) * refactor: Restructure Transactor::preflight to reduce boilerplate (#5592) * Restructures `Transactor::preflight` to create several functions that will remove the need for error-prone boilerplate code in derived classes' implementations of `preflight`. * refactor: Add support for extra transaction signatures (#5594) * Restructures Transactor signature checking code to be able to handle a `sigObject`, which may be the full transaction, or may be an object field containing a separate signature. Either way, the `sigObject` can be a single- or multi-sign signature. * ci: Upload artifacts during build and test in a separate job (#5817) * chore: Set free-form CI inputs as env vars (#5822) This change moves CI values that could be user-provided into environment variables. * Rename flags for DynamicMPT (#5820) * Set version to 2.6.1 * fix: FD/handle guarding + exponential backoff (#5823) * fix: Transaction sig checking functions do not get a full context (#5829) Fixes a (currently harmless) bug introduced by PR #5594 * Remove bogus coverage warning (#5838) * fix return type --------- Co-authored-by: Ed Hennis <ed@ripple.com> Co-authored-by: Jingchen <a1q123456@users.noreply.github.com> Co-authored-by: Denis Angell <dangell@transia.co> Co-authored-by: Bart <bthomee@users.noreply.github.com> Co-authored-by: yinyiqian1 <yqian@ripple.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Co-authored-by: Bronek Kozicki <brok@incorrekt.com> Co-authored-by: Mayukha Vadari <mvadari@ripple.com> Co-authored-by: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com> Co-authored-by: tequ <git@tequ.dev> Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
983 lines
30 KiB
C++
983 lines
30 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_LEDGER_VIEW_H_INCLUDED
|
|
#define RIPPLE_LEDGER_VIEW_H_INCLUDED
|
|
|
|
#include <xrpl/beast/utility/Journal.h>
|
|
#include <xrpl/ledger/ApplyView.h>
|
|
#include <xrpl/ledger/OpenView.h>
|
|
#include <xrpl/ledger/ReadView.h>
|
|
#include <xrpl/protocol/Indexes.h>
|
|
#include <xrpl/protocol/MPTIssue.h>
|
|
#include <xrpl/protocol/Protocol.h>
|
|
#include <xrpl/protocol/Rate.h>
|
|
#include <xrpl/protocol/STLedgerEntry.h>
|
|
#include <xrpl/protocol/STObject.h>
|
|
#include <xrpl/protocol/Serializer.h>
|
|
#include <xrpl/protocol/TER.h>
|
|
|
|
#include <functional>
|
|
#include <initializer_list>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
namespace ripple {
|
|
|
|
enum class WaiveTransferFee : bool { No = false, Yes };
|
|
enum class SkipEntry : bool { No = false, Yes };
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// Observers
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** Determines whether the given expiration time has passed.
|
|
|
|
In the XRP Ledger, expiration times are defined as the number of whole
|
|
seconds after the "Ripple Epoch" which, for historical reasons, is set
|
|
to January 1, 2000 (00:00 UTC).
|
|
|
|
This is like the way the Unix epoch works, except the Ripple Epoch is
|
|
precisely 946,684,800 seconds after the Unix Epoch.
|
|
|
|
See https://xrpl.org/basic-data-types.html#specifying-time
|
|
|
|
Expiration is defined in terms of the close time of the parent ledger,
|
|
because we definitively know the time that it closed (since consensus
|
|
agrees on time) but we do not know the closing time of the ledger that
|
|
is under construction.
|
|
|
|
@param view The ledger whose parent time is used as the clock.
|
|
@param exp The optional expiration time we want to check.
|
|
|
|
@returns `true` if `exp` is in the past; `false` otherwise.
|
|
*/
|
|
[[nodiscard]] bool
|
|
hasExpired(ReadView const& view, std::optional<std::uint32_t> const& exp);
|
|
|
|
/** Controls the treatment of frozen account balances */
|
|
enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN };
|
|
|
|
/** Controls the treatment of unauthorized MPT balances */
|
|
enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED };
|
|
|
|
[[nodiscard]] bool
|
|
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
|
|
|
|
[[nodiscard]] bool
|
|
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
|
|
|
|
[[nodiscard]] bool
|
|
isGlobalFrozen(ReadView const& view, Asset const& asset);
|
|
|
|
// Note, depth parameter is used to limit the recursion depth
|
|
[[nodiscard]] bool
|
|
isVaultPseudoAccountFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
MPTIssue const& mptShare,
|
|
int depth);
|
|
|
|
[[nodiscard]] bool
|
|
isIndividualFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Currency const& currency,
|
|
AccountID const& issuer);
|
|
|
|
[[nodiscard]] inline bool
|
|
isIndividualFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Issue const& issue)
|
|
{
|
|
return isIndividualFrozen(view, account, issue.currency, issue.account);
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
isIndividualFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
MPTIssue const& mptIssue);
|
|
|
|
[[nodiscard]] inline bool
|
|
isIndividualFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Asset const& asset)
|
|
{
|
|
return std::visit(
|
|
[&](auto const& issue) {
|
|
return isIndividualFrozen(view, account, issue);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
isFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Currency const& currency,
|
|
AccountID const& issuer);
|
|
|
|
[[nodiscard]] inline bool
|
|
isFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Issue const& issue,
|
|
int = 0 /*ignored*/)
|
|
{
|
|
return isFrozen(view, account, issue.currency, issue.account);
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
isFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
MPTIssue const& mptIssue,
|
|
int depth = 0);
|
|
|
|
/**
|
|
* isFrozen check is recursive for MPT shares in a vault, descending to
|
|
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
|
* purely defensive, as we currently do not allow such vaults to be created.
|
|
*/
|
|
[[nodiscard]] inline bool
|
|
isFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Asset const& asset,
|
|
int depth = 0)
|
|
{
|
|
return std::visit(
|
|
[&](auto const& issue) {
|
|
return isFrozen(view, account, issue, depth);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] inline TER
|
|
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
|
|
{
|
|
return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
|
|
}
|
|
|
|
[[nodiscard]] inline TER
|
|
checkFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
MPTIssue const& mptIssue)
|
|
{
|
|
return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
|
|
}
|
|
|
|
[[nodiscard]] inline TER
|
|
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
|
|
{
|
|
return std::visit(
|
|
[&](auto const& issue) { return checkFrozen(view, account, issue); },
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
isAnyFrozen(
|
|
ReadView const& view,
|
|
std::initializer_list<AccountID> const& accounts,
|
|
MPTIssue const& mptIssue,
|
|
int depth = 0);
|
|
|
|
[[nodiscard]] inline bool
|
|
isAnyFrozen(
|
|
ReadView const& view,
|
|
std::initializer_list<AccountID> const& accounts,
|
|
Issue const& issue)
|
|
{
|
|
for (auto const& account : accounts)
|
|
{
|
|
if (isFrozen(view, account, issue.currency, issue.account))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] inline bool
|
|
isAnyFrozen(
|
|
ReadView const& view,
|
|
std::initializer_list<AccountID> const& accounts,
|
|
Asset const& asset,
|
|
int depth = 0)
|
|
{
|
|
return std::visit(
|
|
[&]<ValidIssueType TIss>(TIss const& issue) {
|
|
if constexpr (std::is_same_v<TIss, Issue>)
|
|
return isAnyFrozen(view, accounts, issue);
|
|
else
|
|
return isAnyFrozen(view, accounts, issue, depth);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
isDeepFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Currency const& currency,
|
|
AccountID const& issuer);
|
|
|
|
[[nodiscard]] bool
|
|
isLPTokenFrozen(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Issue const& asset,
|
|
Issue const& asset2);
|
|
|
|
// Returns the amount an account can spend without going into debt.
|
|
//
|
|
// <-- saAmount: amount of currency held by account. May be negative.
|
|
[[nodiscard]] STAmount
|
|
accountHolds(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Currency const& currency,
|
|
AccountID const& issuer,
|
|
FreezeHandling zeroIfFrozen,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] STAmount
|
|
accountHolds(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Issue const& issue,
|
|
FreezeHandling zeroIfFrozen,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] STAmount
|
|
accountHolds(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
MPTIssue const& mptIssue,
|
|
FreezeHandling zeroIfFrozen,
|
|
AuthHandling zeroIfUnauthorized,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] STAmount
|
|
accountHolds(
|
|
ReadView const& view,
|
|
AccountID const& account,
|
|
Asset const& asset,
|
|
FreezeHandling zeroIfFrozen,
|
|
AuthHandling zeroIfUnauthorized,
|
|
beast::Journal j);
|
|
|
|
// Returns the amount an account can spend of the currency type saDefault, or
|
|
// returns saDefault if this account is the issuer of the currency in
|
|
// question. Should be used in favor of accountHolds when questioning how much
|
|
// an account can spend while also allowing currency issuers to spend
|
|
// unlimited amounts of their own currency (since they can always issue more).
|
|
[[nodiscard]] STAmount
|
|
accountFunds(
|
|
ReadView const& view,
|
|
AccountID const& id,
|
|
STAmount const& saDefault,
|
|
FreezeHandling freezeHandling,
|
|
beast::Journal j);
|
|
|
|
// Return the account's liquid (not reserved) XRP. Generally prefer
|
|
// calling accountHolds() over this interface. However, this interface
|
|
// allows the caller to temporarily adjust the owner count should that be
|
|
// necessary.
|
|
//
|
|
// @param ownerCountAdj positive to add to count, negative to reduce count.
|
|
[[nodiscard]] XRPAmount
|
|
xrpLiquid(
|
|
ReadView const& view,
|
|
AccountID const& id,
|
|
std::int32_t ownerCountAdj,
|
|
beast::Journal j);
|
|
|
|
/** Iterate all items in the given directory. */
|
|
void
|
|
forEachItem(
|
|
ReadView const& view,
|
|
Keylet const& root,
|
|
std::function<void(std::shared_ptr<SLE const> const&)> const& f);
|
|
|
|
/** Iterate all items after an item in the given directory.
|
|
@param after The key of the item to start after
|
|
@param hint The directory page containing `after`
|
|
@param limit The maximum number of items to return
|
|
@return `false` if the iteration failed
|
|
*/
|
|
bool
|
|
forEachItemAfter(
|
|
ReadView const& view,
|
|
Keylet const& root,
|
|
uint256 const& after,
|
|
std::uint64_t const hint,
|
|
unsigned int limit,
|
|
std::function<bool(std::shared_ptr<SLE const> const&)> const& f);
|
|
|
|
/** Iterate all items in an account's owner directory. */
|
|
inline void
|
|
forEachItem(
|
|
ReadView const& view,
|
|
AccountID const& id,
|
|
std::function<void(std::shared_ptr<SLE const> const&)> const& f)
|
|
{
|
|
return forEachItem(view, keylet::ownerDir(id), f);
|
|
}
|
|
|
|
/** Iterate all items after an item in an owner directory.
|
|
@param after The key of the item to start after
|
|
@param hint The directory page containing `after`
|
|
@param limit The maximum number of items to return
|
|
@return `false` if the iteration failed
|
|
*/
|
|
inline bool
|
|
forEachItemAfter(
|
|
ReadView const& view,
|
|
AccountID const& id,
|
|
uint256 const& after,
|
|
std::uint64_t const hint,
|
|
unsigned int limit,
|
|
std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
|
|
{
|
|
return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f);
|
|
}
|
|
|
|
/** Returns IOU issuer transfer fee as Rate. Rate specifies
|
|
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
|
* is represented as 1,010,000,000.
|
|
* @param issuer The IOU issuer
|
|
*/
|
|
[[nodiscard]] Rate
|
|
transferRate(ReadView const& view, AccountID const& issuer);
|
|
|
|
/** Returns MPT transfer fee as Rate. Rate specifies
|
|
* the fee as fractions of 1 billion. For example, 1% transfer rate
|
|
* is represented as 1,010,000,000.
|
|
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
|
|
*/
|
|
[[nodiscard]] Rate
|
|
transferRate(ReadView const& view, MPTID const& issuanceID);
|
|
|
|
/** Returns the transfer fee as Rate based on the type of token
|
|
* @param view The ledger view
|
|
* @param amount The amount to transfer
|
|
*/
|
|
[[nodiscard]] Rate
|
|
transferRate(ReadView const& view, STAmount const& amount);
|
|
|
|
/** Returns `true` if the directory is empty
|
|
@param key The key of the directory
|
|
*/
|
|
[[nodiscard]] bool
|
|
dirIsEmpty(ReadView const& view, Keylet const& k);
|
|
|
|
// Return the list of enabled amendments
|
|
[[nodiscard]] std::set<uint256>
|
|
getEnabledAmendments(ReadView const& view);
|
|
|
|
// Return a map of amendments that have achieved majority
|
|
using majorityAmendments_t = std::map<uint256, NetClock::time_point>;
|
|
[[nodiscard]] majorityAmendments_t
|
|
getMajorityAmendments(ReadView const& view);
|
|
|
|
/** Return the hash of a ledger by sequence.
|
|
The hash is retrieved by looking up the "skip list"
|
|
in the passed ledger. As the skip list is limited
|
|
in size, if the requested ledger sequence number is
|
|
out of the range of ledgers represented in the skip
|
|
list, then std::nullopt is returned.
|
|
@return The hash of the ledger with the
|
|
given sequence number or std::nullopt.
|
|
*/
|
|
[[nodiscard]] std::optional<uint256>
|
|
hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal);
|
|
|
|
/** Find a ledger index from which we could easily get the requested ledger
|
|
|
|
The index that we return should meet two requirements:
|
|
1) It must be the index of a ledger that has the hash of the ledger
|
|
we are looking for. This means that its sequence must be equal to
|
|
greater than the sequence that we want but not more than 256 greater
|
|
since each ledger contains the hashes of the 256 previous ledgers.
|
|
|
|
2) Its hash must be easy for us to find. This means it must be 0 mod 256
|
|
because every such ledger is permanently enshrined in a LedgerHashes
|
|
page which we can easily retrieve via the skip list.
|
|
*/
|
|
inline LedgerIndex
|
|
getCandidateLedger(LedgerIndex requested)
|
|
{
|
|
return (requested + 255) & (~255);
|
|
}
|
|
|
|
/** Return false if the test ledger is provably incompatible
|
|
with the valid ledger, that is, they could not possibly
|
|
both be valid. Use the first form if you have both ledgers,
|
|
use the second form if you have not acquired the valid ledger yet
|
|
*/
|
|
[[nodiscard]] bool
|
|
areCompatible(
|
|
ReadView const& validLedger,
|
|
ReadView const& testLedger,
|
|
beast::Journal::Stream& s,
|
|
char const* reason);
|
|
|
|
[[nodiscard]] bool
|
|
areCompatible(
|
|
uint256 const& validHash,
|
|
LedgerIndex validIndex,
|
|
ReadView const& testLedger,
|
|
beast::Journal::Stream& s,
|
|
char const* reason);
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// Modifiers
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** Adjust the owner count up or down. */
|
|
void
|
|
adjustOwnerCount(
|
|
ApplyView& view,
|
|
std::shared_ptr<SLE> const& sle,
|
|
std::int32_t amount,
|
|
beast::Journal j);
|
|
|
|
/** @{ */
|
|
/** Returns the first entry in the directory, advancing the index
|
|
|
|
@deprecated These are legacy function that are considered deprecated
|
|
and will soon be replaced with an iterator-based model
|
|
that is easier to use. You should not use them in new code.
|
|
|
|
@param view The view against which to operate
|
|
@param root The root (i.e. first page) of the directory to iterate
|
|
@param page The current page
|
|
@param index The index inside the current page
|
|
@param entry The entry at the current index
|
|
|
|
@return true if the directory isn't empty; false otherwise
|
|
*/
|
|
bool
|
|
cdirFirst(
|
|
ReadView const& view,
|
|
uint256 const& root,
|
|
std::shared_ptr<SLE const>& page,
|
|
unsigned int& index,
|
|
uint256& entry);
|
|
|
|
bool
|
|
dirFirst(
|
|
ApplyView& view,
|
|
uint256 const& root,
|
|
std::shared_ptr<SLE>& page,
|
|
unsigned int& index,
|
|
uint256& entry);
|
|
/** @} */
|
|
|
|
/** @{ */
|
|
/** Returns the next entry in the directory, advancing the index
|
|
|
|
@deprecated These are legacy function that are considered deprecated
|
|
and will soon be replaced with an iterator-based model
|
|
that is easier to use. You should not use them in new code.
|
|
|
|
@param view The view against which to operate
|
|
@param root The root (i.e. first page) of the directory to iterate
|
|
@param page The current page
|
|
@param index The index inside the current page
|
|
@param entry The entry at the current index
|
|
|
|
@return true if the directory isn't empty; false otherwise
|
|
*/
|
|
bool
|
|
cdirNext(
|
|
ReadView const& view,
|
|
uint256 const& root,
|
|
std::shared_ptr<SLE const>& page,
|
|
unsigned int& index,
|
|
uint256& entry);
|
|
|
|
bool
|
|
dirNext(
|
|
ApplyView& view,
|
|
uint256 const& root,
|
|
std::shared_ptr<SLE>& page,
|
|
unsigned int& index,
|
|
uint256& entry);
|
|
/** @} */
|
|
|
|
[[nodiscard]] std::function<void(SLE::ref)>
|
|
describeOwnerDir(AccountID const& account);
|
|
|
|
[[nodiscard]] TER
|
|
dirLink(ApplyView& view, AccountID const& owner, std::shared_ptr<SLE>& object);
|
|
|
|
AccountID
|
|
pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
|
|
|
|
/**
|
|
*
|
|
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
|
|
*
|
|
* The list of valid ownerField is maintained in View.cpp and the caller to
|
|
* this function must perform necessary amendment check(s) before using a
|
|
* field. The amendment check is **not** performed in createPseudoAccount.
|
|
*/
|
|
[[nodiscard]] Expected<std::shared_ptr<SLE>, TER>
|
|
createPseudoAccount(
|
|
ApplyView& view,
|
|
uint256 const& pseudoOwnerKey,
|
|
SField const& ownerField);
|
|
|
|
// Returns true iff sleAcct is a pseudo-account.
|
|
//
|
|
// Returns false if sleAcct is
|
|
// * NOT a pseudo-account OR
|
|
// * NOT a ltACCOUNT_ROOT OR
|
|
// * null pointer
|
|
[[nodiscard]] bool
|
|
isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
|
|
|
|
// Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if
|
|
// set
|
|
// Pseudo-account designator fields MUST be maintained by including the
|
|
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
|
|
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
|
|
// since a non-active amendment will not set any field, by definition.
|
|
// Specific properties of a pseudo-account are NOT checked here, that's what
|
|
// InvariantCheck is for.
|
|
[[nodiscard]] std::vector<SField const*> const&
|
|
getPseudoAccountFields();
|
|
|
|
[[nodiscard]] inline bool
|
|
isPseudoAccount(ReadView const& view, AccountID accountId)
|
|
{
|
|
return isPseudoAccount(view.read(keylet::account(accountId)));
|
|
}
|
|
|
|
[[nodiscard]] TER
|
|
canAddHolding(ReadView const& view, Asset const& asset);
|
|
|
|
/// Any transactors that call addEmptyHolding() in doApply must call
|
|
/// canAddHolding() in preflight with the same View and Asset
|
|
[[nodiscard]] TER
|
|
addEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
XRPAmount priorBalance,
|
|
Issue const& issue,
|
|
beast::Journal journal);
|
|
|
|
[[nodiscard]] TER
|
|
addEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
XRPAmount priorBalance,
|
|
MPTIssue const& mptIssue,
|
|
beast::Journal journal);
|
|
|
|
[[nodiscard]] inline TER
|
|
addEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
XRPAmount priorBalance,
|
|
Asset const& asset,
|
|
beast::Journal journal)
|
|
{
|
|
return std::visit(
|
|
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
|
return addEmptyHolding(
|
|
view, accountID, priorBalance, issue, journal);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] TER
|
|
authorizeMPToken(
|
|
ApplyView& view,
|
|
XRPAmount const& priorBalance,
|
|
MPTID const& mptIssuanceID,
|
|
AccountID const& account,
|
|
beast::Journal journal,
|
|
std::uint32_t flags = 0,
|
|
std::optional<AccountID> holderID = std::nullopt);
|
|
|
|
// VFALCO NOTE Both STAmount parameters should just
|
|
// be "Amount", a unit-less number.
|
|
//
|
|
/** Create a trust line
|
|
|
|
This can set an initial balance.
|
|
*/
|
|
[[nodiscard]] TER
|
|
trustCreate(
|
|
ApplyView& view,
|
|
bool const bSrcHigh,
|
|
AccountID const& uSrcAccountID,
|
|
AccountID const& uDstAccountID,
|
|
uint256 const& uIndex, // --> ripple state entry
|
|
SLE::ref sleAccount, // --> the account being set.
|
|
bool const bAuth, // --> authorize account.
|
|
bool const bNoRipple, // --> others cannot ripple through
|
|
bool const bFreeze, // --> funds cannot leave
|
|
bool bDeepFreeze, // --> can neither receive nor send funds
|
|
STAmount const& saBalance, // --> balance of account being set.
|
|
// Issuer should be noAccount()
|
|
STAmount const& saLimit, // --> limit for account being set.
|
|
// Issuer should be the account being set.
|
|
std::uint32_t uSrcQualityIn,
|
|
std::uint32_t uSrcQualityOut,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] TER
|
|
removeEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
Issue const& issue,
|
|
beast::Journal journal);
|
|
|
|
[[nodiscard]] TER
|
|
removeEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
MPTIssue const& mptIssue,
|
|
beast::Journal journal);
|
|
|
|
[[nodiscard]] inline TER
|
|
removeEmptyHolding(
|
|
ApplyView& view,
|
|
AccountID const& accountID,
|
|
Asset const& asset,
|
|
beast::Journal journal)
|
|
{
|
|
return std::visit(
|
|
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
|
return removeEmptyHolding(view, accountID, issue, journal);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
[[nodiscard]] TER
|
|
trustDelete(
|
|
ApplyView& view,
|
|
std::shared_ptr<SLE> const& sleRippleState,
|
|
AccountID const& uLowAccountID,
|
|
AccountID const& uHighAccountID,
|
|
beast::Journal j);
|
|
|
|
/** Delete an offer.
|
|
|
|
Requirements:
|
|
The passed `sle` be obtained from a prior
|
|
call to view.peek()
|
|
*/
|
|
// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile.
|
|
TER
|
|
offerDelete(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j);
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
//
|
|
// Money Transfers
|
|
//
|
|
|
|
// Direct send w/o fees:
|
|
// - Redeeming IOUs and/or sending sender's own IOUs.
|
|
// - Create trust line of needed.
|
|
// --> bCheckIssuer : normally require issuer to be involved.
|
|
// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles.
|
|
|
|
/** Calls static rippleCreditIOU if saAmount represents Issue.
|
|
* Calls static rippleCreditMPT if saAmount represents MPTIssue.
|
|
*/
|
|
TER
|
|
rippleCredit(
|
|
ApplyView& view,
|
|
AccountID const& uSenderID,
|
|
AccountID const& uReceiverID,
|
|
STAmount const& saAmount,
|
|
bool bCheckIssuer,
|
|
beast::Journal j);
|
|
|
|
TER
|
|
rippleLockEscrowMPT(
|
|
ApplyView& view,
|
|
AccountID const& uGrantorID,
|
|
STAmount const& saAmount,
|
|
beast::Journal j);
|
|
|
|
TER
|
|
rippleUnlockEscrowMPT(
|
|
ApplyView& view,
|
|
AccountID const& uGrantorID,
|
|
AccountID const& uGranteeID,
|
|
STAmount const& netAmount,
|
|
STAmount const& grossAmount,
|
|
beast::Journal j);
|
|
|
|
/** Calls static accountSendIOU if saAmount represents Issue.
|
|
* Calls static accountSendMPT if saAmount represents MPTIssue.
|
|
*/
|
|
[[nodiscard]] TER
|
|
accountSend(
|
|
ApplyView& view,
|
|
AccountID const& from,
|
|
AccountID const& to,
|
|
STAmount const& saAmount,
|
|
beast::Journal j,
|
|
WaiveTransferFee waiveFee = WaiveTransferFee::No);
|
|
|
|
[[nodiscard]] TER
|
|
issueIOU(
|
|
ApplyView& view,
|
|
AccountID const& account,
|
|
STAmount const& amount,
|
|
Issue const& issue,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] TER
|
|
redeemIOU(
|
|
ApplyView& view,
|
|
AccountID const& account,
|
|
STAmount const& amount,
|
|
Issue const& issue,
|
|
beast::Journal j);
|
|
|
|
[[nodiscard]] TER
|
|
transferXRP(
|
|
ApplyView& view,
|
|
AccountID const& from,
|
|
AccountID const& to,
|
|
STAmount const& amount,
|
|
beast::Journal j);
|
|
|
|
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
|
|
* - StrongAuth - before checking if authorization is required
|
|
* - WeakAuth
|
|
* for MPT - after checking lsfMPTRequireAuth flag
|
|
* for IOU - do not check if trust line exists
|
|
* - Legacy
|
|
* for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth
|
|
* for IOU - do not check if trust line exists i.e. same as WeakAuth
|
|
*/
|
|
enum class AuthType { StrongAuth, WeakAuth, Legacy };
|
|
|
|
/** Check if the account lacks required authorization.
|
|
*
|
|
* Return tecNO_AUTH or tecNO_LINE if it does
|
|
* and tesSUCCESS otherwise.
|
|
*
|
|
* If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return
|
|
* tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the
|
|
* RippleState does exist, and the RippleState is not authorized.
|
|
*
|
|
* If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the
|
|
* RippleState exists, and is not authorized. Return tecNO_LINE if
|
|
* lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if
|
|
* WeakAuth and lsfRequireAuth is *not* set, this function will return
|
|
* tesSUCCESS even if RippleState does *not* exist.
|
|
*
|
|
* The default "Legacy" auth type is equivalent to WeakAuth.
|
|
*/
|
|
[[nodiscard]] TER
|
|
requireAuth(
|
|
ReadView const& view,
|
|
Issue const& issue,
|
|
AccountID const& account,
|
|
AuthType authType = AuthType::Legacy);
|
|
|
|
/** Check if the account lacks required authorization.
|
|
*
|
|
* This will also check for expired credentials. If it is called directly
|
|
* from preclaim, the user should convert result tecEXPIRED to tesSUCCESS and
|
|
* proceed to also check permissions with enforceMPTokenAuthorization inside
|
|
* doApply. This will ensure that any expired credentials are deleted.
|
|
*
|
|
* requireAuth check is recursive for MPT shares in a vault, descending to
|
|
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
|
* purely defensive, as we currently do not allow such vaults to be created.
|
|
*
|
|
* If StrongAuth then return tecNO_AUTH if MPToken doesn't exist or
|
|
* lsfMPTRequireAuth is set and MPToken is not authorized.
|
|
*
|
|
* If WeakAuth then return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken
|
|
* doesn't exist or is not authorized (explicitly or via credentials, if
|
|
* DomainID is set in MPTokenIssuance). Consequently, if WeakAuth and
|
|
* lsfMPTRequireAuth is *not* set, this function will return true even if
|
|
* MPToken does *not* exist.
|
|
*
|
|
* The default "Legacy" auth type is equivalent to StrongAuth.
|
|
*/
|
|
[[nodiscard]] TER
|
|
requireAuth(
|
|
ReadView const& view,
|
|
MPTIssue const& mptIssue,
|
|
AccountID const& account,
|
|
AuthType authType = AuthType::Legacy,
|
|
int depth = 0);
|
|
|
|
[[nodiscard]] TER inline requireAuth(
|
|
ReadView const& view,
|
|
Asset const& asset,
|
|
AccountID const& account,
|
|
AuthType authType = AuthType::Legacy)
|
|
{
|
|
return std::visit(
|
|
[&]<ValidIssueType TIss>(TIss const& issue_) {
|
|
return requireAuth(view, issue_, account, authType);
|
|
},
|
|
asset.value());
|
|
}
|
|
|
|
/** Enforce account has MPToken to match its authorization.
|
|
*
|
|
* Called from doApply - it will check for expired (and delete if found any)
|
|
* credentials matching DomainID set in MPTokenIssuance. Must be called if
|
|
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim,
|
|
* which implies that preclaim should replace `tecEXPIRED` with `tesSUCCESS`
|
|
* in order for the transactor to proceed to doApply.
|
|
*
|
|
* This function will create MPToken (if needed) on the basis of any
|
|
* non-expired credentials and will delete any expired credentials, indirectly
|
|
* via verifyValidDomain, as per DomainID (if set in MPTokenIssuance).
|
|
*
|
|
* The caller does NOT need to ensure that DomainID is actually set - this
|
|
* function handles gracefully both cases when DomainID is set and when not.
|
|
*
|
|
* The caller does NOT need to look for existing MPToken to match
|
|
* mptIssue/account - this function checks lsfMPTAuthorized of an existing
|
|
* MPToken iff DomainID is not set.
|
|
*
|
|
* Do not use for accounts which hold implied permission e.g. object owners or
|
|
* if MPTokenIssuance does not require authorization. In both cases use
|
|
* MPTokenAuthorize::authorize if MPToken does not yet exist.
|
|
*/
|
|
[[nodiscard]] TER
|
|
enforceMPTokenAuthorization(
|
|
ApplyView& view,
|
|
MPTID const& mptIssuanceID,
|
|
AccountID const& account,
|
|
XRPAmount const& priorBalance,
|
|
beast::Journal j);
|
|
|
|
/** Check if the destination account is allowed
|
|
* to receive MPT. Return tecNO_AUTH if it doesn't
|
|
* and tesSUCCESS otherwise.
|
|
*/
|
|
[[nodiscard]] TER
|
|
canTransfer(
|
|
ReadView const& view,
|
|
MPTIssue const& mptIssue,
|
|
AccountID const& from,
|
|
AccountID const& to);
|
|
|
|
/** Deleter function prototype. Returns the status of the entry deletion
|
|
* (if should not be skipped) and if the entry should be skipped. The status
|
|
* is always tesSUCCESS if the entry should be skipped.
|
|
*/
|
|
using EntryDeleter = std::function<std::pair<TER, SkipEntry>(
|
|
LedgerEntryType,
|
|
uint256 const&,
|
|
std::shared_ptr<SLE>&)>;
|
|
/** Cleanup owner directory entries on account delete.
|
|
* Used for a regular and AMM accounts deletion. The caller
|
|
* has to provide the deleter function, which handles details of
|
|
* specific account-owned object deletion.
|
|
* @return tecINCOMPLETE indicates maxNodesToDelete
|
|
* are deleted and there remains more nodes to delete.
|
|
*/
|
|
[[nodiscard]] TER
|
|
cleanupOnAccountDelete(
|
|
ApplyView& view,
|
|
Keylet const& ownerDirKeylet,
|
|
EntryDeleter const& deleter,
|
|
beast::Journal j,
|
|
std::optional<std::uint16_t> maxNodesToDelete = std::nullopt);
|
|
|
|
/** Delete trustline to AMM. The passed `sle` must be obtained from a prior
|
|
* call to view.peek(). Fail if neither side of the trustline is AMM or
|
|
* if ammAccountID is seated and is not one of the trustline's side.
|
|
*/
|
|
[[nodiscard]] TER
|
|
deleteAMMTrustLine(
|
|
ApplyView& view,
|
|
std::shared_ptr<SLE> sleState,
|
|
std::optional<AccountID> const& ammAccountID,
|
|
beast::Journal j);
|
|
|
|
// From the perspective of a vault, return the number of shares to give the
|
|
// depositor when they deposit a fixed amount of assets. Since shares are MPT
|
|
// this number is integral and always truncated in this calculation.
|
|
[[nodiscard]] std::optional<STAmount>
|
|
assetsToSharesDeposit(
|
|
std::shared_ptr<SLE const> const& vault,
|
|
std::shared_ptr<SLE const> const& issuance,
|
|
STAmount const& assets);
|
|
|
|
// From the perspective of a vault, return the number of assets to take from
|
|
// depositor when they receive a fixed amount of shares. Note, since shares are
|
|
// MPT, they are always an integral number.
|
|
[[nodiscard]] std::optional<STAmount>
|
|
sharesToAssetsDeposit(
|
|
std::shared_ptr<SLE const> const& vault,
|
|
std::shared_ptr<SLE const> const& issuance,
|
|
STAmount const& shares);
|
|
|
|
enum class TruncateShares : bool { no = false, yes = true };
|
|
|
|
// From the perspective of a vault, return the number of shares to demand from
|
|
// the depositor when they ask to withdraw a fixed amount of assets. Since
|
|
// shares are MPT this number is integral, and it will be rounded to nearest
|
|
// unless explicitly requested to be truncated instead.
|
|
[[nodiscard]] std::optional<STAmount>
|
|
assetsToSharesWithdraw(
|
|
std::shared_ptr<SLE const> const& vault,
|
|
std::shared_ptr<SLE const> const& issuance,
|
|
STAmount const& assets,
|
|
TruncateShares truncate = TruncateShares::no);
|
|
|
|
// From the perspective of a vault, return the number of assets to give the
|
|
// depositor when they redeem a fixed amount of shares. Note, since shares are
|
|
// MPT, they are always an integral number.
|
|
[[nodiscard]] std::optional<STAmount>
|
|
sharesToAssetsWithdraw(
|
|
std::shared_ptr<SLE const> const& vault,
|
|
std::shared_ptr<SLE const> const& issuance,
|
|
STAmount const& shares);
|
|
|
|
/** Has the specified time passed?
|
|
|
|
@param now the current time
|
|
@param mark the cutoff point
|
|
@return true if \a now refers to a time strictly after \a mark, else false.
|
|
*/
|
|
bool
|
|
after(NetClock::time_point now, std::uint32_t mark);
|
|
|
|
} // namespace ripple
|
|
|
|
#endif
|