mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'tapanito/lending-fix-amendment' into tapanito/vault-block-deposit
This commit is contained in:
33
include/xrpl/core/NetworkIDService.h
Normal file
33
include/xrpl/core/NetworkIDService.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Service that provides access to the network ID.
|
||||
|
||||
This service provides read-only access to the network ID configured
|
||||
for this server. The network ID identifies which network (mainnet,
|
||||
testnet, devnet, or custom network) this server is configured to
|
||||
connect to.
|
||||
|
||||
Well-known network IDs:
|
||||
- 0: Mainnet
|
||||
- 1: Testnet
|
||||
- 2: Devnet
|
||||
- 1025+: Custom networks (require NetworkID field in transactions)
|
||||
*/
|
||||
class NetworkIDService
|
||||
{
|
||||
public:
|
||||
virtual ~NetworkIDService() = default;
|
||||
|
||||
/** Get the configured network ID
|
||||
*
|
||||
* @return The network ID this server is configured for
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
getNetworkID() const noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -41,6 +41,7 @@ class LoadFeeTrack;
|
||||
class LoadManager;
|
||||
class ManifestCache;
|
||||
class NetworkOPs;
|
||||
class NetworkIDService;
|
||||
class OpenLedger;
|
||||
class OrderBookDB;
|
||||
class Overlay;
|
||||
@@ -99,6 +100,9 @@ public:
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
|
||||
virtual NetworkIDService&
|
||||
getNetworkIDService() = 0;
|
||||
|
||||
// Protocol and validation services
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
|
||||
175
include/xrpl/ledger/AmendmentTable.h
Normal file
175
include/xrpl/ledger/AmendmentTable.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STValidation.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ServiceRegistry;
|
||||
|
||||
/** The amendment table stores the list of enabled and potential amendments.
|
||||
Individuals amendments are voted on by validators during the consensus
|
||||
process.
|
||||
*/
|
||||
class AmendmentTable
|
||||
{
|
||||
public:
|
||||
struct FeatureInfo
|
||||
{
|
||||
FeatureInfo() = delete;
|
||||
FeatureInfo(std::string const& n, uint256 const& f, VoteBehavior v) : name(n), feature(f), vote(v)
|
||||
{
|
||||
}
|
||||
|
||||
std::string const name;
|
||||
uint256 const feature;
|
||||
VoteBehavior const vote;
|
||||
};
|
||||
|
||||
virtual ~AmendmentTable() = default;
|
||||
|
||||
virtual uint256
|
||||
find(std::string const& name) const = 0;
|
||||
|
||||
virtual bool
|
||||
veto(uint256 const& amendment) = 0;
|
||||
virtual bool
|
||||
unVeto(uint256 const& amendment) = 0;
|
||||
|
||||
virtual bool
|
||||
enable(uint256 const& amendment) = 0;
|
||||
|
||||
virtual bool
|
||||
isEnabled(uint256 const& amendment) const = 0;
|
||||
virtual bool
|
||||
isSupported(uint256 const& amendment) const = 0;
|
||||
|
||||
/**
|
||||
* @brief returns true if one or more amendments on the network
|
||||
* have been enabled that this server does not support
|
||||
*
|
||||
* @return true if an unsupported feature is enabled on the network
|
||||
*/
|
||||
virtual bool
|
||||
hasUnsupportedEnabled() const = 0;
|
||||
|
||||
virtual std::optional<NetClock::time_point>
|
||||
firstUnsupportedExpected() const = 0;
|
||||
|
||||
virtual Json::Value
|
||||
getJson(bool isAdmin) const = 0;
|
||||
|
||||
/** Returns a Json::objectValue. */
|
||||
virtual Json::Value
|
||||
getJson(uint256 const& amendment, bool isAdmin) const = 0;
|
||||
|
||||
/** Called when a new fully-validated ledger is accepted. */
|
||||
void
|
||||
doValidatedLedger(std::shared_ptr<ReadView const> const& lastValidatedLedger)
|
||||
{
|
||||
if (needValidatedLedger(lastValidatedLedger->seq()))
|
||||
doValidatedLedger(
|
||||
lastValidatedLedger->seq(),
|
||||
getEnabledAmendments(*lastValidatedLedger),
|
||||
getMajorityAmendments(*lastValidatedLedger));
|
||||
}
|
||||
|
||||
/** Called to determine whether the amendment logic needs to process
|
||||
a new validated ledger. (If it could have changed things.)
|
||||
*/
|
||||
virtual bool
|
||||
needValidatedLedger(LedgerIndex seq) const = 0;
|
||||
|
||||
virtual void
|
||||
doValidatedLedger(
|
||||
LedgerIndex ledgerSeq,
|
||||
std::set<uint256> const& enabled,
|
||||
majorityAmendments_t const& majority) = 0;
|
||||
|
||||
// Called when the set of trusted validators changes.
|
||||
virtual void
|
||||
trustChanged(hash_set<PublicKey> const& allTrusted) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// inject pseudo-transactions
|
||||
virtual std::map<uint256, std::uint32_t>
|
||||
doVoting(
|
||||
Rules const& rules,
|
||||
NetClock::time_point closeTime,
|
||||
std::set<uint256> const& enabledAmendments,
|
||||
majorityAmendments_t const& majorityAmendments,
|
||||
std::vector<std::shared_ptr<STValidation>> const& valSet) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// add feature entries to a validation
|
||||
virtual std::vector<uint256>
|
||||
doValidation(std::set<uint256> const& enabled) const = 0;
|
||||
|
||||
// The set of amendments to enable in the genesis ledger
|
||||
// This will return all known, non-vetoed amendments.
|
||||
// If we ever have two amendments that should not both be
|
||||
// enabled at the same time, we should ensure one is vetoed.
|
||||
virtual std::vector<uint256>
|
||||
getDesired() const = 0;
|
||||
|
||||
// The function below adapts the API callers expect to the
|
||||
// internal amendment table API. This allows the amendment
|
||||
// table implementation to be independent of the ledger
|
||||
// implementation. These APIs will merge when the view code
|
||||
// supports a full ledger API
|
||||
|
||||
void
|
||||
doVoting(
|
||||
std::shared_ptr<ReadView const> const& lastClosedLedger,
|
||||
std::vector<std::shared_ptr<STValidation>> const& parentValidations,
|
||||
std::shared_ptr<SHAMap> const& initialPosition,
|
||||
beast::Journal j)
|
||||
{
|
||||
// Ask implementation what to do
|
||||
auto actions = doVoting(
|
||||
lastClosedLedger->rules(),
|
||||
lastClosedLedger->parentCloseTime(),
|
||||
getEnabledAmendments(*lastClosedLedger),
|
||||
getMajorityAmendments(*lastClosedLedger),
|
||||
parentValidations);
|
||||
|
||||
// Inject appropriate pseudo-transactions
|
||||
for (auto const& it : actions)
|
||||
{
|
||||
STTx amendTx(ttAMENDMENT, [&it, seq = lastClosedLedger->seq() + 1](auto& obj) {
|
||||
obj.setAccountID(sfAccount, AccountID());
|
||||
obj.setFieldH256(sfAmendment, it.first);
|
||||
obj.setFieldU32(sfLedgerSequence, seq);
|
||||
|
||||
if (it.second != 0)
|
||||
obj.setFieldU32(sfFlags, it.second);
|
||||
});
|
||||
|
||||
Serializer s;
|
||||
amendTx.add(s);
|
||||
|
||||
JLOG(j.debug()) << "Amendments: Adding pseudo-transaction: " << amendTx.getTransactionID() << ": "
|
||||
<< strHex(s.slice()) << ": " << amendTx;
|
||||
|
||||
initialPosition->addGiveItem(
|
||||
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(amendTx.getTransactionID(), s.slice()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AmendmentTable>
|
||||
make_AmendmentTable(
|
||||
ServiceRegistry& registry,
|
||||
std::chrono::seconds majorityTime,
|
||||
std::vector<AmendmentTable::FeatureInfo> const& supported,
|
||||
Section const& enabled,
|
||||
Section const& vetoed,
|
||||
beast::Journal journal);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -253,6 +253,16 @@ std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||
/** A ledger index. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 256;
|
||||
|
||||
/** Returns true if the given ledgerIndex is a voting ledgerIndex */
|
||||
bool
|
||||
isVotingLedger(LedgerIndex seq);
|
||||
|
||||
/** Returns true if the given ledgerIndex is a flag ledgerIndex */
|
||||
bool
|
||||
isFlagLedger(LedgerIndex seq);
|
||||
|
||||
/** A transaction identifier.
|
||||
The value is computed as the hash of the
|
||||
canonicalized, serialized transaction object.
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* To ease maintenance, you may replace any unneeded values with "..."
|
||||
* e.g. #define TRANSACTION(tag, value, name, ...)
|
||||
*
|
||||
* You must define a transactor class in the `ripple` namespace named `name`,
|
||||
* You must define a transactor class in the `xrpl` namespace named `name`,
|
||||
* and include its header alongside the TRANSACTOR definition using this
|
||||
* format:
|
||||
* #if TRANSACTION_INCLUDE
|
||||
* # include <xrpld/app/tx/detail/HEADER.h>
|
||||
* # include <xrpl/tx/transactors/HEADER.h>
|
||||
* #endif
|
||||
*
|
||||
* The `privileges` parameter of the TRANSACTION macro is a bitfield
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
/** This transaction type executes a payment. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Payment.h>
|
||||
# include <xrpl/tx/transactors/Payment.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
Delegation::delegable,
|
||||
@@ -42,7 +42,7 @@ TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
|
||||
/** This transaction type creates an escrow object. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
# include <xrpl/tx/transactors/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
Delegation::delegable,
|
||||
@@ -73,7 +73,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||
|
||||
/** This transaction type adjusts various account settings. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetAccount.h>
|
||||
# include <xrpl/tx/transactors/SetAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -94,7 +94,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
|
||||
/** This transaction type cancels an existing escrow. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
# include <xrpl/tx/transactors/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
Delegation::delegable,
|
||||
@@ -107,7 +107,7 @@ TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
|
||||
/** This transaction type sets or clears an account's "regular key". */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetRegularKey.h>
|
||||
# include <xrpl/tx/transactors/SetRegularKey.h>
|
||||
#endif
|
||||
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
Delegation::notDelegable,
|
||||
@@ -121,7 +121,7 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
|
||||
/** This transaction type creates an offer to trade one asset for another. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateOffer.h>
|
||||
# include <xrpl/tx/transactors/Offer/CreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
Delegation::delegable,
|
||||
@@ -137,7 +137,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
|
||||
/** This transaction type cancels existing offers to trade one asset for another. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelOffer.h>
|
||||
# include <xrpl/tx/transactors/Offer/CancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||
Delegation::delegable,
|
||||
@@ -151,7 +151,7 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||
|
||||
/** This transaction type creates a new set of tickets. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateTicket.h>
|
||||
# include <xrpl/tx/transactors/CreateTicket.h>
|
||||
#endif
|
||||
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||
Delegation::delegable,
|
||||
@@ -167,7 +167,7 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||
// The SignerEntries are optional because a SignerList is deleted by
|
||||
// setting the SignerQuorum to zero and omitting SignerEntries.
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetSignerList.h>
|
||||
# include <xrpl/tx/transactors/SetSignerList.h>
|
||||
#endif
|
||||
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -180,7 +180,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
|
||||
/** This transaction type creates a new unidirectional XRP payment channel. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PayChan.h>
|
||||
# include <xrpl/tx/transactors/PayChan.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||
Delegation::delegable,
|
||||
@@ -222,7 +222,7 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
||||
|
||||
/** This transaction type creates a new check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CreateCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
Delegation::delegable,
|
||||
@@ -238,7 +238,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
|
||||
/** This transaction type cashes an existing check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CashCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CashCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
Delegation::delegable,
|
||||
@@ -252,7 +252,7 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
|
||||
/** This transaction type cancels an existing check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CancelCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||
Delegation::delegable,
|
||||
@@ -264,7 +264,7 @@ TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||
|
||||
/** This transaction type grants or revokes authorization to transfer funds. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DepositPreauth.h>
|
||||
# include <xrpl/tx/transactors/DepositPreauth.h>
|
||||
#endif
|
||||
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||
Delegation::delegable,
|
||||
@@ -279,7 +279,7 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||
|
||||
/** This transaction type modifies a trustline between two accounts. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetTrust.h>
|
||||
# include <xrpl/tx/transactors/SetTrust.h>
|
||||
#endif
|
||||
TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||
Delegation::delegable,
|
||||
@@ -293,7 +293,7 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||
|
||||
/** This transaction type deletes an existing account. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteAccount.h>
|
||||
# include <xrpl/tx/transactors/DeleteAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||
Delegation::notDelegable,
|
||||
@@ -309,7 +309,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||
|
||||
/** This transaction mints a new NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenMint.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenMint.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||
Delegation::delegable,
|
||||
@@ -327,7 +327,7 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||
|
||||
/** This transaction burns (i.e. destroys) an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenBurn.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenBurn.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||
Delegation::delegable,
|
||||
@@ -340,7 +340,7 @@ TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||
|
||||
/** This transaction creates a new offer to buy or sell an NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCreateOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenCreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||
Delegation::delegable,
|
||||
@@ -356,7 +356,7 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||
|
||||
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCancelOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenCancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||
Delegation::delegable,
|
||||
@@ -368,7 +368,7 @@ TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||
|
||||
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
Delegation::delegable,
|
||||
@@ -382,7 +382,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
|
||||
/** This transaction claws back issued tokens. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Clawback.h>
|
||||
# include <xrpl/tx/transactors/Clawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
Delegation::delegable,
|
||||
@@ -395,7 +395,7 @@ TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
|
||||
/** This transaction claws back tokens from an AMM pool. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMClawback.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
Delegation::delegable,
|
||||
@@ -410,7 +410,7 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
|
||||
/** This transaction type creates an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMCreate.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
Delegation::delegable,
|
||||
@@ -424,7 +424,7 @@ TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
|
||||
/** This transaction type deposits into an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDeposit.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -442,7 +442,7 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
|
||||
/** This transaction type withdraws from an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMWithdraw.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -459,7 +459,7 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
|
||||
/** This transaction type votes for the trading fee */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMVote.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMVote.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
Delegation::delegable,
|
||||
@@ -473,7 +473,7 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
|
||||
/** This transaction type bids for the auction slot */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMBid.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMBid.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
Delegation::delegable,
|
||||
@@ -489,7 +489,7 @@ TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
|
||||
/** This transaction type deletes AMM in the empty state */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDelete.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
Delegation::delegable,
|
||||
@@ -502,7 +502,7 @@ TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
|
||||
/** This transactions creates a crosschain sequence number */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/XChainBridge.h>
|
||||
# include <xrpl/tx/transactors/XChainBridge.h>
|
||||
#endif
|
||||
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
||||
Delegation::delegable,
|
||||
@@ -617,7 +617,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
||||
|
||||
/** This transaction type creates or updates a DID */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DID.h>
|
||||
# include <xrpl/tx/transactors/DID.h>
|
||||
#endif
|
||||
TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||
Delegation::delegable,
|
||||
@@ -638,7 +638,7 @@ TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
||||
|
||||
/** This transaction type creates an Oracle instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetOracle.h>
|
||||
# include <xrpl/tx/transactors/SetOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||
Delegation::delegable,
|
||||
@@ -655,7 +655,7 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||
|
||||
/** This transaction type deletes an Oracle instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteOracle.h>
|
||||
# include <xrpl/tx/transactors/DeleteOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||
Delegation::delegable,
|
||||
@@ -667,7 +667,7 @@ TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||
|
||||
/** This transaction type fixes a problem in the ledger state */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LedgerStateFix.h>
|
||||
# include <xrpl/tx/transactors/LedgerStateFix.h>
|
||||
#endif
|
||||
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
Delegation::delegable,
|
||||
@@ -680,7 +680,7 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
|
||||
/** This transaction type creates a MPTokensIssuance instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||
Delegation::delegable,
|
||||
@@ -697,7 +697,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||
|
||||
/** This transaction type destroys a MPTokensIssuance instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||
Delegation::delegable,
|
||||
@@ -709,7 +709,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||
|
||||
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
Delegation::delegable,
|
||||
@@ -726,7 +726,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenAuthorize.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
Delegation::delegable,
|
||||
@@ -739,7 +739,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
|
||||
/** This transaction type create an Credential instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Credentials.h>
|
||||
# include <xrpl/tx/transactors/Credentials.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||
Delegation::delegable,
|
||||
@@ -775,7 +775,7 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
||||
|
||||
/** This transaction type modify a NFToken */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenModify.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenModify.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||
Delegation::delegable,
|
||||
@@ -789,7 +789,7 @@ TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||
|
||||
/** This transaction type creates or modifies a Permissioned Domain */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainSet.h>
|
||||
# include <xrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||
Delegation::delegable,
|
||||
@@ -802,7 +802,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||
|
||||
/** This transaction type deletes a Permissioned Domain */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainDelete.h>
|
||||
# include <xrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||
Delegation::delegable,
|
||||
@@ -814,7 +814,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||
|
||||
/** This transaction type delegates authorized account specified permissions */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DelegateSet.h>
|
||||
# include <xrpl/tx/transactors/Delegate/DelegateSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -827,7 +827,7 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
|
||||
/** This transaction creates a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultCreate.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
Delegation::delegable,
|
||||
@@ -845,7 +845,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
|
||||
/** This transaction updates a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultSet.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
Delegation::delegable,
|
||||
@@ -860,7 +860,7 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
|
||||
/** This transaction deletes a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDelete.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
Delegation::delegable,
|
||||
@@ -872,7 +872,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
|
||||
/** This transaction trades assets for shares with a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDeposit.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -885,7 +885,7 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
|
||||
/** This transaction trades shares for assets with a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultWithdraw.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -900,7 +900,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
|
||||
/** This transaction claws back tokens from a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultClawback.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
Delegation::delegable,
|
||||
@@ -914,7 +914,7 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
|
||||
/** This transaction type batches together transactions. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Batch.h>
|
||||
# include <xrpl/tx/transactors/Batch.h>
|
||||
#endif
|
||||
TRANSACTION(ttBATCH, 71, Batch,
|
||||
Delegation::notDelegable,
|
||||
@@ -929,7 +929,7 @@ TRANSACTION(ttBATCH, 71, Batch,
|
||||
|
||||
/** This transaction creates and updates a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerSet.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
Delegation::delegable,
|
||||
@@ -946,7 +946,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
|
||||
/** This transaction deletes a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerDelete.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
Delegation::delegable,
|
||||
@@ -957,7 +957,7 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
|
||||
/** This transaction deposits First Loss Capital into a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverDeposit.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -969,7 +969,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
|
||||
/** This transaction withdraws First Loss Capital from a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -984,7 +984,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
/** This transaction claws back First Loss Capital from a Loan Broker to
|
||||
the issuer of the capital */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverClawback.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
Delegation::delegable,
|
||||
@@ -996,7 +996,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
|
||||
/** This transaction creates a Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanSet.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
Delegation::delegable,
|
||||
@@ -1023,7 +1023,7 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
|
||||
/** This transaction deletes an existing Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanDelete.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
Delegation::delegable,
|
||||
@@ -1034,7 +1034,7 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
|
||||
/** This transaction is used to change the delinquency status of an existing Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanManage.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanManage.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
Delegation::delegable,
|
||||
@@ -1048,7 +1048,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
|
||||
/** The Borrower uses this transaction to make a Payment on the Loan. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanPay.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanPay.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
Delegation::delegable,
|
||||
@@ -1063,7 +1063,7 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
For details, see: https://xrpl.org/amendments.html
|
||||
*/
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Change.h>
|
||||
# include <xrpl/tx/transactors/Change.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
||||
Delegation::notDelegable,
|
||||
|
||||
137
include/xrpl/server/LoadFeeTrack.h
Normal file
137
include/xrpl/server/LoadFeeTrack.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct Fees;
|
||||
|
||||
/** Manages the current fee schedule.
|
||||
|
||||
The "base" fee is the cost to send a reference transaction under no load,
|
||||
expressed in millionths of one XRP.
|
||||
|
||||
The "load" fee is how much the local server currently charges to send a
|
||||
reference transaction. This fee fluctuates based on the load of the
|
||||
server.
|
||||
*/
|
||||
class LoadFeeTrack final
|
||||
{
|
||||
public:
|
||||
explicit LoadFeeTrack(beast::Journal journal = beast::Journal(beast::Journal::getNullSink()))
|
||||
: j_(journal)
|
||||
, localTxnLoadFee_(lftNormalFee)
|
||||
, remoteTxnLoadFee_(lftNormalFee)
|
||||
, clusterTxnLoadFee_(lftNormalFee)
|
||||
, raiseCount_(0)
|
||||
{
|
||||
}
|
||||
|
||||
~LoadFeeTrack() = default;
|
||||
|
||||
void
|
||||
setRemoteFee(std::uint32_t f)
|
||||
{
|
||||
JLOG(j_.trace()) << "setRemoteFee: " << f;
|
||||
std::lock_guard sl(lock_);
|
||||
remoteTxnLoadFee_ = f;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getRemoteFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return remoteTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLocalFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return localTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getClusterFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return clusterTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLoadBase() const
|
||||
{
|
||||
return lftNormalFee;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLoadFactor() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return std::max({clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_});
|
||||
}
|
||||
|
||||
std::pair<std::uint32_t, std::uint32_t>
|
||||
getScalingFactors() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
|
||||
return std::make_pair(
|
||||
std::max(localTxnLoadFee_, remoteTxnLoadFee_), std::max(remoteTxnLoadFee_, clusterTxnLoadFee_));
|
||||
}
|
||||
|
||||
void
|
||||
setClusterFee(std::uint32_t fee)
|
||||
{
|
||||
JLOG(j_.trace()) << "setClusterFee: " << fee;
|
||||
std::lock_guard sl(lock_);
|
||||
clusterTxnLoadFee_ = fee;
|
||||
}
|
||||
|
||||
bool
|
||||
raiseLocalFee();
|
||||
bool
|
||||
lowerLocalFee();
|
||||
|
||||
bool
|
||||
isLoadedLocal() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee);
|
||||
}
|
||||
|
||||
bool
|
||||
isLoadedCluster() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee) || (clusterTxnLoadFee_ != lftNormalFee);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::uint32_t constexpr lftNormalFee = 256; // 256 is the minimum/normal load factor
|
||||
static std::uint32_t constexpr lftFeeIncFraction = 4; // increase fee by 1/4
|
||||
static std::uint32_t constexpr lftFeeDecFraction = 4; // decrease fee by 1/4
|
||||
static std::uint32_t constexpr lftFeeMax = lftNormalFee * 1000000;
|
||||
|
||||
beast::Journal const j_;
|
||||
std::mutex mutable lock_;
|
||||
|
||||
std::uint32_t localTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t remoteTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t clusterTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t raiseCount_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Scale using load as well as base rate
|
||||
XRPAmount
|
||||
scaleFeeLoad(XRPAmount fee, LoadFeeTrack const& feeTrack, Fees const& fees, bool bUnlimited);
|
||||
|
||||
} // namespace xrpl
|
||||
129
include/xrpl/tx/ApplyContext.h
Normal file
129
include/xrpl/tx/ApplyContext.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** State information when applying a tx. */
|
||||
class ApplyContext
|
||||
{
|
||||
public:
|
||||
explicit ApplyContext(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& base,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
STTx const& tx,
|
||||
TER preclaimResult,
|
||||
XRPAmount baseFee,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
explicit ApplyContext(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& base,
|
||||
STTx const& tx,
|
||||
TER preclaimResult,
|
||||
XRPAmount baseFee,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
|
||||
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
|
||||
{
|
||||
XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
TER const preclaimResult;
|
||||
XRPAmount const baseFee;
|
||||
beast::Journal const journal;
|
||||
|
||||
ApplyView&
|
||||
view()
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
ApplyView const&
|
||||
view() const
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
// VFALCO Unfortunately this is necessary
|
||||
RawView&
|
||||
rawView()
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
ApplyFlags const&
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Sets the DeliveredAmount field in the metadata */
|
||||
void
|
||||
deliver(STAmount const& amount)
|
||||
{
|
||||
view_->deliver(amount);
|
||||
}
|
||||
|
||||
/** Discard changes and start fresh. */
|
||||
void
|
||||
discard();
|
||||
|
||||
/** Apply the transaction result to the base. */
|
||||
std::optional<TxMeta> apply(TER);
|
||||
|
||||
/** Get the number of unapplied changes. */
|
||||
std::size_t
|
||||
size();
|
||||
|
||||
/** Visit unapplied changes. */
|
||||
void
|
||||
visit(
|
||||
std::function<void(
|
||||
uint256 const& key,
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)> const& func);
|
||||
|
||||
void
|
||||
destroyXRP(XRPAmount const& fee)
|
||||
{
|
||||
view_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
/** Applies all invariant checkers one by one.
|
||||
|
||||
@param result the result generated by processing this transaction.
|
||||
@param fee the fee charged for this transaction
|
||||
@return the result code that should be returned for this transaction.
|
||||
*/
|
||||
TER
|
||||
checkInvariants(TER const result, XRPAmount const fee);
|
||||
|
||||
private:
|
||||
TER
|
||||
failInvariantCheck(TER const result);
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence<Is...>);
|
||||
|
||||
OpenView& base_;
|
||||
ApplyFlags flags_;
|
||||
std::optional<ApplyViewImpl> view_;
|
||||
|
||||
// The ID of the batch transaction we are executing under, if seated.
|
||||
std::optional<uint256 const> parentBatchId_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
723
include/xrpl/tx/InvariantCheck.h
Normal file
723
include/xrpl/tx/InvariantCheck.h
Normal file
@@ -0,0 +1,723 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ReadView;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/**
|
||||
* @brief Prototype for invariant check implementations.
|
||||
*
|
||||
* __THIS CLASS DOES NOT EXIST__ - or rather it exists in documentation only to
|
||||
* communicate the interface required of any invariant checker. Any invariant
|
||||
* check implementation should implement the public methods documented here.
|
||||
*
|
||||
*/
|
||||
class InvariantChecker_PROTOTYPE
|
||||
{
|
||||
public:
|
||||
explicit InvariantChecker_PROTOTYPE() = default;
|
||||
|
||||
/**
|
||||
* @brief called for each ledger entry in the current transaction.
|
||||
*
|
||||
* @param isDelete true if the SLE is being deleted
|
||||
* @param before ledger entry before modification by the transaction
|
||||
* @param after ledger entry after modification by the transaction
|
||||
*/
|
||||
void
|
||||
visitEntry(bool isDelete, std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
|
||||
|
||||
/**
|
||||
* @brief called after all ledger entries have been visited to determine
|
||||
* the final status of the check
|
||||
*
|
||||
* @param tx the transaction being applied
|
||||
* @param tec the current TER result of the transaction
|
||||
* @param fee the fee actually charged for this transaction
|
||||
* @param view a ReadView of the ledger being modified
|
||||
* @param j journal for logging
|
||||
*
|
||||
* @return true if check passes, false if it fails
|
||||
*/
|
||||
bool
|
||||
finalize(STTx const& tx, TER const tec, XRPAmount const fee, ReadView const& view, beast::Journal const& j);
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Invariant: We should never charge a transaction a negative fee or a
|
||||
* fee that is larger than what the transaction itself specifies.
|
||||
*
|
||||
* We can, in some circumstances, charge less.
|
||||
*/
|
||||
class TransactionFeeCheck
|
||||
{
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: A transaction must not create XRP and should only destroy
|
||||
* the XRP fee.
|
||||
*
|
||||
* We iterate through all account roots, payment channels and escrow entries
|
||||
* that were modified and calculate the net change in XRP caused by the
|
||||
* transactions.
|
||||
*/
|
||||
class XRPNotCreated
|
||||
{
|
||||
std::int64_t drops_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: we cannot remove an account ledger entry
|
||||
*
|
||||
* We iterate all account roots that were modified, and ensure that any that
|
||||
* were present before the transaction was applied continue to be present
|
||||
* afterwards unless they were explicitly deleted by a successful
|
||||
* AccountDelete transaction.
|
||||
*/
|
||||
class AccountRootsNotDeleted
|
||||
{
|
||||
std::uint32_t accountsDeleted_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: a deleted account must not have any objects left
|
||||
*
|
||||
* We iterate all deleted account roots, and ensure that there are no
|
||||
* objects left that are directly accessible with that account's ID.
|
||||
*
|
||||
* There should only be one deleted account, but that's checked by
|
||||
* AccountRootsNotDeleted. This invariant will handle multiple deleted account
|
||||
* roots without a problem.
|
||||
*/
|
||||
class AccountRootsDeletedClean
|
||||
{
|
||||
// Pair is <before, after>. Before is used for most of the checks, so that
|
||||
// if, for example, an object ID field is cleared, but the object is not
|
||||
// deleted, it can still be found. After is used specifically for any checks
|
||||
// that are expected as part of the deletion, such as zeroing out the
|
||||
// balance.
|
||||
std::vector<std::pair<std::shared_ptr<SLE const>, std::shared_ptr<SLE const>>> accountsDeleted_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: An account XRP balance must be in XRP and take a value
|
||||
* between 0 and INITIAL_XRP drops, inclusive.
|
||||
*
|
||||
* We iterate all account roots modified by the transaction and ensure that
|
||||
* their XRP balances are reasonable.
|
||||
*/
|
||||
class XRPBalanceChecks
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: corresponding modified ledger entries should match in type
|
||||
* and added entries should be a valid type.
|
||||
*/
|
||||
class LedgerEntryTypesMatch
|
||||
{
|
||||
bool typeMismatch_ = false;
|
||||
bool invalidTypeAdded_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Trust lines using XRP are not allowed.
|
||||
*
|
||||
* We iterate all the trust lines created by this transaction and ensure
|
||||
* that they are against a valid issuer.
|
||||
*/
|
||||
class NoXRPTrustLines
|
||||
{
|
||||
bool xrpTrustLine_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Trust lines with deep freeze flag are not allowed if normal
|
||||
* freeze flag is not set.
|
||||
*
|
||||
* We iterate all the trust lines created by this transaction and ensure
|
||||
* that they don't have deep freeze flag set without normal freeze flag set.
|
||||
*/
|
||||
class NoDeepFreezeTrustLinesWithoutFreeze
|
||||
{
|
||||
bool deepFreezeWithoutFreeze_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: frozen trust line balance change is not allowed.
|
||||
*
|
||||
* We iterate all affected trust lines and ensure that they don't have
|
||||
* unexpected change of balance if they're frozen.
|
||||
*/
|
||||
class TransfersNotFrozen
|
||||
{
|
||||
struct BalanceChange
|
||||
{
|
||||
std::shared_ptr<SLE const> const line;
|
||||
int const balanceChangeSign;
|
||||
};
|
||||
|
||||
struct IssuerChanges
|
||||
{
|
||||
std::vector<BalanceChange> senders;
|
||||
std::vector<BalanceChange> receivers;
|
||||
};
|
||||
|
||||
using ByIssuer = std::map<Issue, IssuerChanges>;
|
||||
ByIssuer balanceChanges_;
|
||||
|
||||
std::map<AccountID, std::shared_ptr<SLE const> const> possibleIssuers_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
|
||||
private:
|
||||
bool
|
||||
isValidEntry(std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
|
||||
|
||||
STAmount
|
||||
calculateBalanceChange(
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after,
|
||||
bool isDelete);
|
||||
|
||||
void
|
||||
recordBalance(Issue const& issue, BalanceChange change);
|
||||
|
||||
void
|
||||
recordBalanceChanges(std::shared_ptr<SLE const> const& after, STAmount const& balanceChange);
|
||||
|
||||
std::shared_ptr<SLE const>
|
||||
findIssuer(AccountID const& issuerID, ReadView const& view);
|
||||
|
||||
bool
|
||||
validateIssuerChanges(
|
||||
std::shared_ptr<SLE const> const& issuer,
|
||||
IssuerChanges const& changes,
|
||||
STTx const& tx,
|
||||
beast::Journal const& j,
|
||||
bool enforce);
|
||||
|
||||
bool
|
||||
validateFrozenState(
|
||||
BalanceChange const& change,
|
||||
bool high,
|
||||
STTx const& tx,
|
||||
beast::Journal const& j,
|
||||
bool enforce,
|
||||
bool globalFreeze);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: offers should be for non-negative amounts and must not
|
||||
* be XRP to XRP.
|
||||
*
|
||||
* Examine all offers modified by the transaction and ensure that there are
|
||||
* no offers which contain negative amounts or which exchange XRP for XRP.
|
||||
*/
|
||||
class NoBadOffers
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: an escrow entry must take a value between 0 and
|
||||
* INITIAL_XRP drops exclusive.
|
||||
*/
|
||||
class NoZeroEscrow
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: a new account root must be the consequence of a payment,
|
||||
* must have the right starting sequence, and the payment
|
||||
* may not create more than one new account root.
|
||||
*/
|
||||
class ValidNewAccountRoot
|
||||
{
|
||||
std::uint32_t accountsCreated_ = 0;
|
||||
std::uint32_t accountSeq_ = 0;
|
||||
bool pseudoAccount_ = false;
|
||||
std::uint32_t flags_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Validates several invariants for NFToken pages.
|
||||
*
|
||||
* The following checks are made:
|
||||
* - The page is correctly associated with the owner.
|
||||
* - The page is correctly ordered between the next and previous links.
|
||||
* - The page contains at least one and no more than 32 NFTokens.
|
||||
* - The NFTokens on this page do not belong on a lower or higher page.
|
||||
* - The NFTokens are correctly sorted on the page.
|
||||
* - Each URI, if present, is not empty.
|
||||
*/
|
||||
class ValidNFTokenPage
|
||||
{
|
||||
bool badEntry_ = false;
|
||||
bool badLink_ = false;
|
||||
bool badSort_ = false;
|
||||
bool badURI_ = false;
|
||||
bool invalidSize_ = false;
|
||||
bool deletedFinalPage_ = false;
|
||||
bool deletedLink_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Validates counts of NFTokens after all transaction types.
|
||||
*
|
||||
* The following checks are made:
|
||||
* - The number of minted or burned NFTokens can only be changed by
|
||||
* NFTokenMint or NFTokenBurn transactions.
|
||||
* - A successful NFTokenMint must increase the number of NFTokens.
|
||||
* - A failed NFTokenMint must not change the number of minted NFTokens.
|
||||
* - An NFTokenMint transaction cannot change the number of burned NFTokens.
|
||||
* - A successful NFTokenBurn must increase the number of burned NFTokens.
|
||||
* - A failed NFTokenBurn must not change the number of burned NFTokens.
|
||||
* - An NFTokenBurn transaction cannot change the number of minted NFTokens.
|
||||
*/
|
||||
class NFTokenCountTracking
|
||||
{
|
||||
std::uint32_t beforeMintedTotal = 0;
|
||||
std::uint32_t beforeBurnedTotal = 0;
|
||||
std::uint32_t afterMintedTotal = 0;
|
||||
std::uint32_t afterBurnedTotal = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Token holder's trustline balance cannot be negative after
|
||||
* Clawback.
|
||||
*
|
||||
* We iterate all the trust lines affected by this transaction and ensure
|
||||
* that no more than one trustline is modified, and also holder's balance is
|
||||
* non-negative.
|
||||
*/
|
||||
class ValidClawback
|
||||
{
|
||||
std::uint32_t trustlinesChanged = 0;
|
||||
std::uint32_t mptokensChanged = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidMPTIssuance
|
||||
{
|
||||
std::uint32_t mptIssuancesCreated_ = 0;
|
||||
std::uint32_t mptIssuancesDeleted_ = 0;
|
||||
|
||||
std::uint32_t mptokensCreated_ = 0;
|
||||
std::uint32_t mptokensDeleted_ = 0;
|
||||
// non-MPT transactions may attempt to create
|
||||
// MPToken by an issuer
|
||||
bool mptCreatedByIssuer_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Permissioned Domains must have some rules and
|
||||
* AcceptedCredentials must have length between 1 and 10 inclusive.
|
||||
*
|
||||
* Since only permissions constitute rules, an empty credentials list
|
||||
* means that there are no rules and the invariant is violated.
|
||||
*
|
||||
* Credentials must be sorted and no duplicates allowed
|
||||
*
|
||||
*/
|
||||
class ValidPermissionedDomain
|
||||
{
|
||||
struct SleStatus
|
||||
{
|
||||
std::size_t credentialsSize_{0};
|
||||
bool isSorted_ = false;
|
||||
bool isUnique_ = false;
|
||||
bool isDelete_ = false;
|
||||
};
|
||||
std::vector<SleStatus> sleStatus_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Pseudo-accounts have valid and consistent properties
|
||||
*
|
||||
* Pseudo-accounts have certain properties, and some of those properties are
|
||||
* unique to pseudo-accounts. Check that all pseudo-accounts are following the
|
||||
* rules, and that only pseudo-accounts look like pseudo-accounts.
|
||||
*
|
||||
*/
|
||||
class ValidPseudoAccounts
|
||||
{
|
||||
std::vector<std::string> errors_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidPermissionedDEX
|
||||
{
|
||||
bool regularOffers_ = false;
|
||||
bool badHybrids_ = false;
|
||||
hash_set<uint256> domains_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidAMM
|
||||
{
|
||||
std::optional<AccountID> ammAccount_;
|
||||
std::optional<STAmount> lptAMMBalanceAfter_;
|
||||
std::optional<STAmount> lptAMMBalanceBefore_;
|
||||
bool ammPoolChanged_;
|
||||
|
||||
public:
|
||||
enum class ZeroAllowed : bool { No = false, Yes = true };
|
||||
|
||||
ValidAMM() : ammPoolChanged_{false}
|
||||
{
|
||||
}
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
|
||||
private:
|
||||
bool
|
||||
finalizeBid(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeVote(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeCreate(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDelete(bool enforce, TER res, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDeposit(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
// Includes clawback
|
||||
bool
|
||||
finalizeWithdraw(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDEX(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
generalInvariant(STTx const&, ReadView const&, ZeroAllowed zeroAllowed, beast::Journal const&) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Some fields are unmodifiable
|
||||
*
|
||||
* Check that any fields specified as unmodifiable are not modified when the
|
||||
* object is modified. Creation and deletion are ignored.
|
||||
*
|
||||
*/
|
||||
class NoModifiedUnmodifiableFields
|
||||
{
|
||||
// Pair is <before, after>.
|
||||
std::set<std::pair<SLE::const_pointer, SLE::const_pointer>> changedEntries_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loan brokers are internally consistent
|
||||
*
|
||||
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
|
||||
* node (the root), which will only hold entries for `RippleState` or
|
||||
* `MPToken` objects.
|
||||
*
|
||||
*/
|
||||
class ValidLoanBroker
|
||||
{
|
||||
// Not all of these elements will necessarily be populated. Remaining items
|
||||
// will be looked up as needed.
|
||||
struct BrokerInfo
|
||||
{
|
||||
SLE::const_pointer brokerBefore = nullptr;
|
||||
// After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
SLE::const_pointer brokerAfter = nullptr;
|
||||
};
|
||||
// Collect all the LoanBrokers found directly or indirectly through
|
||||
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||
std::map<uint256, BrokerInfo> brokers_;
|
||||
// Collect all the modified trust lines. Their high and low accounts will be
|
||||
// loaded to look for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> lines_;
|
||||
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||
// for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> mpts_;
|
||||
|
||||
bool
|
||||
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loans are internally consistent
|
||||
*
|
||||
* 1. If `Loan.PaymentRemaining = 0` then `Loan.PrincipalOutstanding = 0`
|
||||
*
|
||||
*/
|
||||
class ValidLoan
|
||||
{
|
||||
// Pair is <before, after>. After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
std::vector<std::pair<SLE::const_pointer, SLE::const_pointer>> loans_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Invariants: Vault object and MPTokenIssuance for vault shares
|
||||
*
|
||||
* - vault deleted and vault created is empty
|
||||
* - vault created must be linked to pseudo-account for shares and assets
|
||||
* - vault must have MPTokenIssuance for shares
|
||||
* - vault without shares outstanding must have no shares
|
||||
* - loss unrealized does not exceed the difference between assets total and
|
||||
* assets available
|
||||
* - assets available do not exceed assets total
|
||||
* - vault deposit increases assets and share issuance, and adds to:
|
||||
* total assets, assets available, shares outstanding
|
||||
* - vault withdrawal and clawback reduce assets and share issuance, and
|
||||
* subtracts from: total assets, assets available, shares outstanding
|
||||
* - vault set must not alter the vault assets or shares balance
|
||||
* - no vault transaction can change loss unrealized (it's updated by loan
|
||||
* transactions)
|
||||
*
|
||||
*/
|
||||
class ValidVault
|
||||
{
|
||||
Number static constexpr zero{};
|
||||
|
||||
struct Vault final
|
||||
{
|
||||
uint256 key = beast::zero;
|
||||
Asset asset = {};
|
||||
AccountID pseudoId = {};
|
||||
AccountID owner = {};
|
||||
uint192 shareMPTID = beast::zero;
|
||||
Number assetsTotal = 0;
|
||||
Number assetsAvailable = 0;
|
||||
Number assetsMaximum = 0;
|
||||
Number lossUnrealized = 0;
|
||||
|
||||
Vault static make(SLE const&);
|
||||
};
|
||||
|
||||
struct Shares final
|
||||
{
|
||||
MPTIssue share = {};
|
||||
std::uint64_t sharesTotal = 0;
|
||||
std::uint64_t sharesMaximum = 0;
|
||||
|
||||
Shares static make(SLE const&);
|
||||
};
|
||||
|
||||
std::vector<Vault> afterVault_ = {};
|
||||
std::vector<Shares> afterMPTs_ = {};
|
||||
std::vector<Vault> beforeVault_ = {};
|
||||
std::vector<Shares> beforeMPTs_ = {};
|
||||
std::unordered_map<uint256, Number> deltas_ = {};
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
// additional invariant checks can be declared above and then added to this
|
||||
// tuple
|
||||
using InvariantChecks = std::tuple<
|
||||
TransactionFeeCheck,
|
||||
AccountRootsNotDeleted,
|
||||
AccountRootsDeletedClean,
|
||||
LedgerEntryTypesMatch,
|
||||
XRPBalanceChecks,
|
||||
XRPNotCreated,
|
||||
NoXRPTrustLines,
|
||||
NoDeepFreezeTrustLinesWithoutFreeze,
|
||||
TransfersNotFrozen,
|
||||
NoBadOffers,
|
||||
NoZeroEscrow,
|
||||
ValidNewAccountRoot,
|
||||
ValidNFTokenPage,
|
||||
NFTokenCountTracking,
|
||||
ValidClawback,
|
||||
ValidMPTIssuance,
|
||||
ValidPermissionedDomain,
|
||||
ValidPermissionedDEX,
|
||||
ValidAMM,
|
||||
NoModifiedUnmodifiableFields,
|
||||
ValidPseudoAccounts,
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault>;
|
||||
|
||||
/**
|
||||
* @brief get a tuple of all invariant checks
|
||||
*
|
||||
* @return std::tuple of instances that implement the required invariant check
|
||||
* methods
|
||||
*
|
||||
* @see xrpl::InvariantChecker_PROTOTYPE
|
||||
*/
|
||||
inline InvariantChecks
|
||||
getInvariantChecks()
|
||||
{
|
||||
return InvariantChecks{};
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
64
include/xrpl/tx/SignerEntries.h
Normal file
64
include/xrpl/tx/SignerEntries.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h> //
|
||||
#include <xrpl/beast/utility/Journal.h> // beast::Journal
|
||||
#include <xrpl/protocol/TER.h> // temMALFORMED
|
||||
#include <xrpl/protocol/UintTypes.h> // AccountID
|
||||
#include <xrpl/tx/Transactor.h> // NotTEC
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
class STObject;
|
||||
|
||||
// Support for SignerEntries that is needed by a few Transactors.
|
||||
//
|
||||
// SignerEntries is represented as a std::vector<SignerEntries::SignerEntry>.
|
||||
// There is no direct constructor for SignerEntries.
|
||||
//
|
||||
// o A std::vector<SignerEntries::SignerEntry> is a SignerEntries.
|
||||
// o More commonly, SignerEntries are extracted from an STObject by
|
||||
// calling SignerEntries::deserialize().
|
||||
class SignerEntries
|
||||
{
|
||||
public:
|
||||
explicit SignerEntries() = delete;
|
||||
|
||||
struct SignerEntry
|
||||
{
|
||||
AccountID account;
|
||||
std::uint16_t weight;
|
||||
std::optional<uint256> tag;
|
||||
|
||||
SignerEntry(AccountID const& inAccount, std::uint16_t inWeight, std::optional<uint256> inTag)
|
||||
: account(inAccount), weight(inWeight), tag(inTag)
|
||||
{
|
||||
}
|
||||
|
||||
// For sorting to look for duplicate accounts
|
||||
friend bool
|
||||
operator<(SignerEntry const& lhs, SignerEntry const& rhs)
|
||||
{
|
||||
return lhs.account < rhs.account;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator==(SignerEntry const& lhs, SignerEntry const& rhs)
|
||||
{
|
||||
return lhs.account == rhs.account;
|
||||
}
|
||||
};
|
||||
|
||||
// Deserialize a SignerEntries array from the network or from the ledger.
|
||||
//
|
||||
// obj Contains a SignerEntries field that is an STArray.
|
||||
// journal For reporting error conditions.
|
||||
// annotation Source of SignerEntries, like "ledger" or "transaction".
|
||||
static Expected<std::vector<SignerEntry>, NotTEC>
|
||||
deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
419
include/xrpl/tx/Transactor.h
Normal file
419
include/xrpl/tx/Transactor.h
Normal file
@@ -0,0 +1,419 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/WrappedSink.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** State information when preflighting a tx. */
|
||||
struct PreflightContext
|
||||
{
|
||||
public:
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
Rules const rules;
|
||||
ApplyFlags flags;
|
||||
std::optional<uint256 const> parentBatchId;
|
||||
beast::Journal const j;
|
||||
|
||||
PreflightContext(
|
||||
ServiceRegistry& registry_,
|
||||
STTx const& tx_,
|
||||
uint256 parentBatchId_,
|
||||
Rules const& rules_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_), tx(tx_), rules(rules_), flags(flags_), parentBatchId(parentBatchId_), j(j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set");
|
||||
}
|
||||
|
||||
PreflightContext(
|
||||
ServiceRegistry& registry_,
|
||||
STTx const& tx_,
|
||||
Rules const& rules_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_), tx(tx_), rules(rules_), flags(flags_), j(j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
PreflightContext&
|
||||
operator=(PreflightContext const&) = delete;
|
||||
};
|
||||
|
||||
/** State information when determining if a tx is likely to claim a fee. */
|
||||
struct PreclaimContext
|
||||
{
|
||||
public:
|
||||
ServiceRegistry& registry;
|
||||
ReadView const& view;
|
||||
TER preflightResult;
|
||||
ApplyFlags flags;
|
||||
STTx const& tx;
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
beast::Journal const j;
|
||||
|
||||
PreclaimContext(
|
||||
ServiceRegistry& registry_,
|
||||
ReadView const& view_,
|
||||
TER preflightResult_,
|
||||
STTx const& tx_,
|
||||
ApplyFlags flags_,
|
||||
std::optional<uint256> parentBatchId_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_)
|
||||
, view(view_)
|
||||
, preflightResult(preflightResult_)
|
||||
, flags(flags_)
|
||||
, tx(tx_)
|
||||
, parentBatchId(parentBatchId_)
|
||||
, j(j_)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH),
|
||||
"Parent Batch ID should be set if batch apply flag is set");
|
||||
}
|
||||
|
||||
PreclaimContext(
|
||||
ServiceRegistry& registry_,
|
||||
ReadView const& view_,
|
||||
TER preflightResult_,
|
||||
STTx const& tx_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: PreclaimContext(registry_, view_, preflightResult_, tx_, flags_, std::nullopt, j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
PreclaimContext&
|
||||
operator=(PreclaimContext const&) = delete;
|
||||
};
|
||||
|
||||
class TxConsequences;
|
||||
struct PreflightResult;
|
||||
// Needed for preflight specialization
|
||||
class Change;
|
||||
|
||||
class Transactor
|
||||
{
|
||||
protected:
|
||||
ApplyContext& ctx_;
|
||||
beast::WrappedSink sink_;
|
||||
beast::Journal const j_;
|
||||
|
||||
AccountID const account_;
|
||||
XRPAmount mPriorBalance; // Balance before fees.
|
||||
XRPAmount mSourceBalance; // Balance after fees.
|
||||
|
||||
virtual ~Transactor() = default;
|
||||
Transactor(Transactor const&) = delete;
|
||||
Transactor&
|
||||
operator=(Transactor const&) = delete;
|
||||
|
||||
public:
|
||||
enum ConsequencesFactoryType { Normal, Blocker, Custom };
|
||||
/** Process the transaction. */
|
||||
ApplyResult
|
||||
operator()();
|
||||
|
||||
ApplyView&
|
||||
view()
|
||||
{
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
ApplyView const&
|
||||
view() const
|
||||
{
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/*
|
||||
These static functions are called from invoke_preclaim<Tx>
|
||||
using name hiding to accomplish compile-time polymorphism,
|
||||
so derived classes can override for different or extra
|
||||
functionality. Use with care, as these are not really
|
||||
virtual and so don't have the compiler-time protection that
|
||||
comes with it.
|
||||
*/
|
||||
|
||||
static NotTEC
|
||||
checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j);
|
||||
|
||||
static NotTEC
|
||||
checkPriorTxAndLastLedger(PreclaimContext const& ctx);
|
||||
|
||||
static TER
|
||||
checkFee(PreclaimContext const& ctx, XRPAmount baseFee);
|
||||
|
||||
static NotTEC
|
||||
checkSign(PreclaimContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkBatchSign(PreclaimContext const& ctx);
|
||||
|
||||
// Returns the fee in fee units, not scaled for load.
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/* Do NOT define an invokePreflight function in a derived class.
|
||||
Instead, define:
|
||||
|
||||
// Optional if the transaction is gated on an amendment that
|
||||
// isn't specified in transactions.macro
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
// Optional if the transaction uses any flags other than tfUniversal
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
// Required, even if it just returns tesSUCCESS.
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
// Optional, rarely needed, if the transaction does any expensive
|
||||
// checks after the signature is verified.
|
||||
static NotTEC preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
* Do not try to call preflight1 or preflight2 directly.
|
||||
* Do not check whether relevant amendments are enabled in preflight.
|
||||
Instead, define checkExtraFeatures.
|
||||
* Do not check flags in preflight. Instead, define getFlagsMask.
|
||||
*/
|
||||
template <class T>
|
||||
static NotTEC
|
||||
invokePreflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Most transactors do nothing
|
||||
// after checkSeq/Fee/Sign.
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
ticketDelete(ApplyView& view, AccountID const& account, uint256 const& ticketIndex, beast::Journal j);
|
||||
|
||||
protected:
|
||||
TER
|
||||
apply();
|
||||
|
||||
explicit Transactor(ApplyContext& ctx);
|
||||
|
||||
virtual void
|
||||
preCompute();
|
||||
|
||||
virtual TER
|
||||
doApply() = 0;
|
||||
|
||||
/** Compute the minimum fee required to process a transaction
|
||||
with a given baseFee based on the current server load.
|
||||
|
||||
@param registry The service registry.
|
||||
@param baseFee The base fee of a candidate transaction
|
||||
@see xrpl::calculateBaseFee
|
||||
@param fees Fee settings from the current ledger
|
||||
@param flags Transaction processing fees
|
||||
*/
|
||||
static XRPAmount
|
||||
minimumFee(ServiceRegistry& registry, XRPAmount baseFee, Fees const& fees, ApplyFlags flags);
|
||||
|
||||
// Returns the fee in fee units, not scaled for load.
|
||||
static XRPAmount
|
||||
calculateOwnerReserveFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static NotTEC
|
||||
checkSign(
|
||||
ReadView const& view,
|
||||
ApplyFlags flags,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
AccountID const& idAccount,
|
||||
STObject const& sigObject,
|
||||
beast::Journal const j);
|
||||
|
||||
// Base class always returns true
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
// Base class always returns tfUniversalMask
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
// Base class always returns tesSUCCESS
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
validDataLength(std::optional<Slice> const& slice, std::size_t maxLength);
|
||||
|
||||
template <class T>
|
||||
static bool
|
||||
validNumericRange(std::optional<T> value, T max, T min = T{});
|
||||
|
||||
template <class T, class Unit>
|
||||
static bool
|
||||
validNumericRange(
|
||||
std::optional<T> value,
|
||||
unit::ValueUnit<Unit, T> max,
|
||||
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
||||
|
||||
/// Minimum will usually be zero.
|
||||
template <class T>
|
||||
static bool
|
||||
validNumericMinimum(std::optional<T> value, T min = T{});
|
||||
|
||||
/// Minimum will usually be zero.
|
||||
template <class T, class Unit>
|
||||
static bool
|
||||
validNumericMinimum(std::optional<T> value, unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
||||
|
||||
private:
|
||||
std::pair<TER, XRPAmount>
|
||||
reset(XRPAmount fee);
|
||||
|
||||
TER
|
||||
consumeSeqProxy(SLE::pointer const& sleAccount);
|
||||
TER
|
||||
payFee();
|
||||
static NotTEC
|
||||
checkSingleSign(
|
||||
ReadView const& view,
|
||||
AccountID const& idSigner,
|
||||
AccountID const& idAccount,
|
||||
std::shared_ptr<SLE const> sleAccount,
|
||||
beast::Journal const j);
|
||||
static NotTEC
|
||||
checkMultiSign(
|
||||
ReadView const& view,
|
||||
ApplyFlags flags,
|
||||
AccountID const& id,
|
||||
STObject const& sigObject,
|
||||
beast::Journal const j);
|
||||
|
||||
void trapTransaction(uint256) const;
|
||||
|
||||
/** Performs early sanity checks on the account and fee fields.
|
||||
|
||||
(And passes flagMask to preflight0)
|
||||
|
||||
Do not try to call preflight1 from preflight() in derived classes. See
|
||||
the description of invokePreflight for details.
|
||||
*/
|
||||
static NotTEC
|
||||
preflight1(PreflightContext const& ctx, std::uint32_t flagMask);
|
||||
|
||||
/** Checks whether the signature appears valid
|
||||
|
||||
Do not try to call preflight2 from preflight() in derived classes. See
|
||||
the description of invokePreflight for details.
|
||||
*/
|
||||
static NotTEC
|
||||
preflight2(PreflightContext const& ctx);
|
||||
};
|
||||
|
||||
inline bool
|
||||
Transactor::checkExtraFeatures(PreflightContext const& ctx)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Performs early sanity checks on the txid and flags */
|
||||
NotTEC
|
||||
preflight0(PreflightContext const& ctx, std::uint32_t flagMask);
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** Checks the validity of the transactor signing key.
|
||||
*
|
||||
* Normally called from preflight1 with ctx.tx.
|
||||
*/
|
||||
NotTEC
|
||||
preflightCheckSigningKey(STObject const& sigObject, beast::Journal j);
|
||||
|
||||
/** Checks the special signing key state needed for simulation
|
||||
*
|
||||
* Normally called from preflight2 with ctx.tx.
|
||||
*/
|
||||
std::optional<NotTEC>
|
||||
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j);
|
||||
} // namespace detail
|
||||
|
||||
// Defined in Change.cpp
|
||||
template <>
|
||||
NotTEC
|
||||
Transactor::invokePreflight<Change>(PreflightContext const& ctx);
|
||||
|
||||
template <class T>
|
||||
NotTEC
|
||||
Transactor::invokePreflight(PreflightContext const& ctx)
|
||||
{
|
||||
// Using this lookup does NOT require checking the fixDelegateV1_1. The data
|
||||
// exists regardless of whether it is enabled.
|
||||
auto const feature = Permission::getInstance().getTxFeature(ctx.tx.getTxnType());
|
||||
|
||||
if (feature && !ctx.rules.enabled(*feature))
|
||||
return temDISABLED;
|
||||
|
||||
if (!T::checkExtraFeatures(ctx))
|
||||
return temDISABLED;
|
||||
|
||||
if (auto const ret = preflight1(ctx, T::getFlagsMask(ctx)))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = T::preflight(ctx))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = preflight2(ctx))
|
||||
return ret;
|
||||
|
||||
return T::preflightSigValidated(ctx);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Transactor::validNumericRange(std::optional<T> value, T max, T min)
|
||||
{
|
||||
if (!value)
|
||||
return true;
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
template <class T, class Unit>
|
||||
bool
|
||||
Transactor::validNumericRange(std::optional<T> value, unit::ValueUnit<Unit, T> max, unit::ValueUnit<Unit, T> min)
|
||||
{
|
||||
return validNumericRange(value, max.value(), min.value());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Transactor::validNumericMinimum(std::optional<T> value, T min)
|
||||
{
|
||||
if (!value)
|
||||
return true;
|
||||
return value >= min;
|
||||
}
|
||||
|
||||
template <class T, class Unit>
|
||||
bool
|
||||
Transactor::validNumericMinimum(std::optional<T> value, unit::ValueUnit<Unit, T> min)
|
||||
{
|
||||
return validNumericMinimum(value, min.value());
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
129
include/xrpl/tx/apply.h
Normal file
129
include/xrpl/tx/apply.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class HashRouter;
|
||||
class ServiceRegistry;
|
||||
|
||||
/** Describes the pre-processing validity of a transaction.
|
||||
|
||||
@see checkValidity, forceValidity
|
||||
*/
|
||||
enum class Validity {
|
||||
/// Signature is bad. Didn't do local checks.
|
||||
SigBad,
|
||||
/// Signature is good, but local checks fail.
|
||||
SigGoodOnly,
|
||||
/// Signature and local checks are good / passed.
|
||||
Valid
|
||||
};
|
||||
|
||||
/** Checks transaction signature and local checks.
|
||||
|
||||
@return A `Validity` enum representing how valid the
|
||||
`STTx` is and, if not `Valid`, a reason string.
|
||||
|
||||
@note Results are cached internally, so tests will not be
|
||||
repeated over repeated calls, unless cache expires.
|
||||
|
||||
@return `std::pair`, where `.first` is the status, and
|
||||
`.second` is the reason if appropriate.
|
||||
|
||||
@see Validity
|
||||
*/
|
||||
std::pair<Validity, std::string>
|
||||
checkValidity(HashRouter& router, STTx const& tx, Rules const& rules);
|
||||
|
||||
/** Sets the validity of a given transaction in the cache.
|
||||
|
||||
@warning Use with extreme care.
|
||||
|
||||
@note Can only raise the validity to a more valid state,
|
||||
and can not override anything cached bad.
|
||||
|
||||
@see checkValidity, Validity
|
||||
*/
|
||||
void
|
||||
forceValidity(HashRouter& router, uint256 const& txid, Validity validity);
|
||||
|
||||
/** Apply a transaction to an `OpenView`.
|
||||
|
||||
This function is the canonical way to apply a transaction
|
||||
to a ledger. It rolls the validation and application
|
||||
steps into one function. To do the steps manually, the
|
||||
correct calling order is:
|
||||
@code{.cpp}
|
||||
preflight -> preclaim -> doApply
|
||||
@endcode
|
||||
The result of one function must be passed to the next.
|
||||
The `preflight` result can be safely cached and reused
|
||||
asynchronously, but `preclaim` and `doApply` must be called
|
||||
in the same thread and with the same view.
|
||||
|
||||
@note Does not throw.
|
||||
|
||||
For open ledgers, the `Transactor` will catch exceptions
|
||||
and return `tefEXCEPTION`. For closed ledgers, the
|
||||
`Transactor` will attempt to only charge a fee,
|
||||
and return `tecFAILED_PROCESSING`.
|
||||
|
||||
If the `Transactor` gets an exception while trying
|
||||
to charge the fee, it will be caught and
|
||||
turned into `tefEXCEPTION`.
|
||||
|
||||
For network health, a `Transactor` makes its
|
||||
best effort to at least charge a fee if the
|
||||
ledger is closed.
|
||||
|
||||
@param app The current running `Application`.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
@param tx The transaction to be checked.
|
||||
@param flags `ApplyFlags` describing processing options.
|
||||
@param journal A journal.
|
||||
|
||||
@see preflight, preclaim, doApply
|
||||
|
||||
@return A pair with the `TER` and a `bool` indicating
|
||||
whether or not the transaction was applied.
|
||||
*/
|
||||
ApplyResult
|
||||
apply(ServiceRegistry& registry, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal journal);
|
||||
|
||||
/** Enum class for return value from `applyTransaction`
|
||||
|
||||
@see applyTransaction
|
||||
*/
|
||||
enum class ApplyTransactionResult {
|
||||
/// Applied to this ledger
|
||||
Success,
|
||||
/// Should not be retried in this ledger
|
||||
Fail,
|
||||
/// Should be retried in this ledger
|
||||
Retry
|
||||
};
|
||||
|
||||
/** Transaction application helper
|
||||
|
||||
Provides more detailed logging and decodes the
|
||||
correct behavior based on the `TER` type
|
||||
|
||||
@see ApplyTransactionResult
|
||||
*/
|
||||
ApplyTransactionResult
|
||||
applyTransaction(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& view,
|
||||
STTx const& tx,
|
||||
bool retryAssured,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal);
|
||||
|
||||
} // namespace xrpl
|
||||
338
include/xrpl/tx/applySteps.h
Normal file
338
include/xrpl/tx/applySteps.h
Normal file
@@ -0,0 +1,338 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ServiceRegistry;
|
||||
class STTx;
|
||||
class TxQ;
|
||||
|
||||
struct ApplyResult
|
||||
{
|
||||
TER ter;
|
||||
bool applied;
|
||||
std::optional<TxMeta> metadata;
|
||||
|
||||
ApplyResult(TER t, bool a, std::optional<TxMeta> m = std::nullopt) : ter(t), applied(a), metadata(std::move(m))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Return true if the transaction can claim a fee (tec),
|
||||
and the `ApplyFlags` do not allow soft failures.
|
||||
*/
|
||||
inline bool
|
||||
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
||||
{
|
||||
return isTecClaim(ter) && !(flags & tapRETRY);
|
||||
}
|
||||
|
||||
/** Class describing the consequences to the account
|
||||
of applying a transaction if the transaction consumes
|
||||
the maximum XRP allowed.
|
||||
*/
|
||||
class TxConsequences
|
||||
{
|
||||
public:
|
||||
/// Describes how the transaction affects subsequent
|
||||
/// transactions
|
||||
enum Category {
|
||||
/// Moves currency around, creates offers, etc.
|
||||
normal = 0,
|
||||
/// Affects the ability of subsequent transactions
|
||||
/// to claim a fee. Eg. `SetRegularKey`
|
||||
blocker
|
||||
};
|
||||
|
||||
private:
|
||||
/// Describes how the transaction affects subsequent
|
||||
/// transactions
|
||||
bool isBlocker_;
|
||||
/// Transaction fee
|
||||
XRPAmount fee_;
|
||||
/// Does NOT include the fee.
|
||||
XRPAmount potentialSpend_;
|
||||
/// SeqProxy of transaction.
|
||||
SeqProxy seqProx_;
|
||||
/// Number of sequences consumed.
|
||||
std::uint32_t sequencesConsumed_;
|
||||
|
||||
public:
|
||||
// Constructor if preflight returns a value other than tesSUCCESS.
|
||||
// Asserts if tesSUCCESS is passed.
|
||||
explicit TxConsequences(NotTEC pfResult);
|
||||
|
||||
/// Constructor if the STTx has no notable consequences for the TxQ.
|
||||
explicit TxConsequences(STTx const& tx);
|
||||
|
||||
/// Constructor for a blocker.
|
||||
TxConsequences(STTx const& tx, Category category);
|
||||
|
||||
/// Constructor for an STTx that may consume more XRP than the fee.
|
||||
TxConsequences(STTx const& tx, XRPAmount potentialSpend);
|
||||
|
||||
/// Constructor for an STTx that consumes more than the usual sequences.
|
||||
TxConsequences(STTx const& tx, std::uint32_t sequencesConsumed);
|
||||
|
||||
/// Copy constructor
|
||||
TxConsequences(TxConsequences const&) = default;
|
||||
/// Copy assignment operator
|
||||
TxConsequences&
|
||||
operator=(TxConsequences const&) = default;
|
||||
/// Move constructor
|
||||
TxConsequences(TxConsequences&&) = default;
|
||||
/// Move assignment operator
|
||||
TxConsequences&
|
||||
operator=(TxConsequences&&) = default;
|
||||
|
||||
/// Fee
|
||||
XRPAmount
|
||||
fee() const
|
||||
{
|
||||
return fee_;
|
||||
}
|
||||
|
||||
/// Potential Spend
|
||||
XRPAmount const&
|
||||
potentialSpend() const
|
||||
{
|
||||
return potentialSpend_;
|
||||
}
|
||||
|
||||
/// SeqProxy
|
||||
SeqProxy
|
||||
seqProxy() const
|
||||
{
|
||||
return seqProx_;
|
||||
}
|
||||
|
||||
/// Sequences consumed
|
||||
std::uint32_t
|
||||
sequencesConsumed() const
|
||||
{
|
||||
return sequencesConsumed_;
|
||||
}
|
||||
|
||||
/// Returns true if the transaction is a blocker.
|
||||
bool
|
||||
isBlocker() const
|
||||
{
|
||||
return isBlocker_;
|
||||
}
|
||||
|
||||
// Return the SeqProxy that would follow this.
|
||||
SeqProxy
|
||||
followingSeq() const
|
||||
{
|
||||
SeqProxy following = seqProx_;
|
||||
following.advanceBy(sequencesConsumed());
|
||||
return following;
|
||||
}
|
||||
};
|
||||
|
||||
/** Describes the results of the `preflight` check
|
||||
|
||||
@note All members are const to make it more difficult
|
||||
to "fake" a result without calling `preflight`.
|
||||
@see preflight, preclaim, doApply, apply
|
||||
*/
|
||||
struct PreflightResult
|
||||
{
|
||||
public:
|
||||
/// From the input - the transaction
|
||||
STTx const& tx;
|
||||
/// From the input - the batch identifier, if part of a batch
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
/// From the input - the rules
|
||||
Rules const rules;
|
||||
/// Consequences of the transaction
|
||||
TxConsequences const consequences;
|
||||
/// From the input - the flags
|
||||
ApplyFlags const flags;
|
||||
/// From the input - the journal
|
||||
beast::Journal const j;
|
||||
|
||||
/// Intermediate transaction result
|
||||
NotTEC const ter;
|
||||
|
||||
/// Constructor
|
||||
template <class Context>
|
||||
PreflightResult(Context const& ctx_, std::pair<NotTEC, TxConsequences> const& result)
|
||||
: tx(ctx_.tx)
|
||||
, parentBatchId(ctx_.parentBatchId)
|
||||
, rules(ctx_.rules)
|
||||
, consequences(result.second)
|
||||
, flags(ctx_.flags)
|
||||
, j(ctx_.j)
|
||||
, ter(result.first)
|
||||
{
|
||||
}
|
||||
|
||||
PreflightResult(PreflightResult const&) = default;
|
||||
/// Deleted copy assignment operator
|
||||
PreflightResult&
|
||||
operator=(PreflightResult const&) = delete;
|
||||
};
|
||||
|
||||
/** Describes the results of the `preclaim` check
|
||||
|
||||
@note All members are const to make it more difficult
|
||||
to "fake" a result without calling `preclaim`.
|
||||
@see preflight, preclaim, doApply, apply
|
||||
*/
|
||||
struct PreclaimResult
|
||||
{
|
||||
public:
|
||||
/// From the input - the ledger view
|
||||
ReadView const& view;
|
||||
/// From the input - the transaction
|
||||
STTx const& tx;
|
||||
/// From the input - the batch identifier, if part of a batch
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
/// From the input - the flags
|
||||
ApplyFlags const flags;
|
||||
/// From the input - the journal
|
||||
beast::Journal const j;
|
||||
|
||||
/// Intermediate transaction result
|
||||
TER const ter;
|
||||
|
||||
/// Success flag - whether the transaction is likely to
|
||||
/// claim a fee
|
||||
bool const likelyToClaimFee;
|
||||
|
||||
/// Constructor
|
||||
template <class Context>
|
||||
PreclaimResult(Context const& ctx_, TER ter_)
|
||||
: view(ctx_.view)
|
||||
, tx(ctx_.tx)
|
||||
, parentBatchId(ctx_.parentBatchId)
|
||||
, flags(ctx_.flags)
|
||||
, j(ctx_.j)
|
||||
, ter(ter_)
|
||||
, likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags))
|
||||
{
|
||||
}
|
||||
|
||||
PreclaimResult(PreclaimResult const&) = default;
|
||||
/// Deleted copy assignment operator
|
||||
PreclaimResult&
|
||||
operator=(PreclaimResult const&) = delete;
|
||||
};
|
||||
|
||||
/** Gate a transaction based on static information.
|
||||
|
||||
The transaction is checked against all possible
|
||||
validity constraints that do not require a ledger.
|
||||
|
||||
@param app The current running `Application`.
|
||||
@param rules The `Rules` in effect at the time of the check.
|
||||
@param tx The transaction to be checked.
|
||||
@param flags `ApplyFlags` describing processing options.
|
||||
@param j A journal.
|
||||
|
||||
@see PreflightResult, preclaim, doApply, apply
|
||||
|
||||
@return A `PreflightResult` object containing, among
|
||||
other things, the `TER` code.
|
||||
*/
|
||||
/** @{ */
|
||||
PreflightResult
|
||||
preflight(ServiceRegistry& registry, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j);
|
||||
|
||||
PreflightResult
|
||||
preflight(
|
||||
ServiceRegistry& registry,
|
||||
Rules const& rules,
|
||||
uint256 const& parentBatchId,
|
||||
STTx const& tx,
|
||||
ApplyFlags flags,
|
||||
beast::Journal j);
|
||||
/** @} */
|
||||
|
||||
/** Gate a transaction based on static ledger information.
|
||||
|
||||
The transaction is checked against all possible
|
||||
validity constraints that DO require a ledger.
|
||||
|
||||
If preclaim succeeds, then the transaction is very
|
||||
likely to claim a fee. This will determine if the
|
||||
transaction is safe to relay without being applied
|
||||
to the open ledger.
|
||||
|
||||
"Succeeds" in this case is defined as returning a
|
||||
`tes` or `tec`, since both lead to claiming a fee.
|
||||
|
||||
@pre The transaction has been checked
|
||||
and validated using `preflight`
|
||||
|
||||
@param preflightResult The result of a previous
|
||||
call to `preflight` for the transaction.
|
||||
@param app The current running `Application`.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
|
||||
@see PreclaimResult, preflight, doApply, apply
|
||||
|
||||
@return A `PreclaimResult` object containing, among
|
||||
other things the `TER` code and the base fee value for
|
||||
this transaction.
|
||||
*/
|
||||
PreclaimResult
|
||||
preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view);
|
||||
|
||||
/** Compute only the expected base fee for a transaction.
|
||||
|
||||
Base fees are transaction specific, so any calculation
|
||||
needing them must get the base fee for each transaction.
|
||||
|
||||
No validation is done or implied by this function.
|
||||
|
||||
Caller is responsible for handling any exceptions.
|
||||
Since none should be thrown, that will usually
|
||||
mean terminating.
|
||||
|
||||
@param view The current open ledger.
|
||||
@param tx The transaction to be checked.
|
||||
|
||||
@return The base fee.
|
||||
*/
|
||||
XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/** Return the minimum fee that an "ordinary" transaction would pay.
|
||||
|
||||
When computing the FeeLevel for a transaction the TxQ sometimes needs
|
||||
the know what an "ordinary" or reference transaction would be required
|
||||
to pay.
|
||||
|
||||
@param view The current open ledger.
|
||||
@param tx The transaction so the correct multisigner count is used.
|
||||
|
||||
@return The base fee in XRPAmount.
|
||||
*/
|
||||
XRPAmount
|
||||
calculateDefaultBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/** Apply a prechecked transaction to an OpenView.
|
||||
|
||||
@pre The transaction has been checked
|
||||
and validated using `preflight` and `preclaim`
|
||||
|
||||
@param preclaimResult The result of a previous
|
||||
call to `preclaim` for the transaction.
|
||||
@param registry The service registry.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
|
||||
@see preflight, preclaim, apply
|
||||
|
||||
@return A pair with the `TER` and a `bool` indicating
|
||||
whether or not the transaction was applied.
|
||||
*/
|
||||
ApplyResult
|
||||
doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view);
|
||||
|
||||
} // namespace xrpl
|
||||
63
include/xrpl/tx/paths/BookTip.h
Normal file
63
include/xrpl/tx/paths/BookTip.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Logs;
|
||||
|
||||
/** Iterates and consumes raw offers in an order book.
|
||||
Offers are presented from highest quality to lowest quality. This will
|
||||
return all offers present including missing, invalid, unfunded, etc.
|
||||
*/
|
||||
class BookTip
|
||||
{
|
||||
private:
|
||||
ApplyView& view_;
|
||||
bool m_valid;
|
||||
uint256 m_book;
|
||||
uint256 m_end;
|
||||
uint256 m_dir;
|
||||
uint256 m_index;
|
||||
std::shared_ptr<SLE> m_entry;
|
||||
Quality m_quality;
|
||||
|
||||
public:
|
||||
/** Create the iterator. */
|
||||
BookTip(ApplyView& view, Book const& book);
|
||||
|
||||
uint256 const&
|
||||
dir() const noexcept
|
||||
{
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
index() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
Quality const&
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
SLE::pointer const&
|
||||
entry() const noexcept
|
||||
{
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/** Erases the current offer and advance to the next offer.
|
||||
Complexity: Constant
|
||||
@return `true` if there is a next offer
|
||||
*/
|
||||
bool
|
||||
step(beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
52
include/xrpl/tx/paths/Flow.h
Normal file
52
include/xrpl/tx/paths/Flow.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/tx/paths/RippleCalc.h>
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace path {
|
||||
namespace detail {
|
||||
struct FlowDebugInfo;
|
||||
}
|
||||
} // namespace path
|
||||
|
||||
/**
|
||||
Make a payment from the src account to the dst account
|
||||
|
||||
@param view Trust lines and balances
|
||||
@param deliver Amount to deliver to the dst account
|
||||
@param src Account providing input funds for the payment
|
||||
@param dst Account receiving the payment
|
||||
@param paths Set of paths to explore for liquidity
|
||||
@param defaultPaths Include defaultPaths in the path set
|
||||
@param partialPayment If the payment cannot deliver the entire
|
||||
requested amount, deliver as much as possible, given the constraints
|
||||
@param ownerPaysTransferFee If true then owner, not sender, pays fee
|
||||
@param offerCrossing If Yes or Sell then flow is executing offer crossing, not
|
||||
payments
|
||||
@param limitQuality Do not use liquidity below this quality threshold
|
||||
@param sendMax Do not spend more than this amount
|
||||
@param j Journal to write journal messages to
|
||||
@param flowDebugInfo If non-null a pointer to FlowDebugInfo for debugging
|
||||
@return Actual amount in and out, and the result code
|
||||
*/
|
||||
path::RippleCalc::Output
|
||||
flow(
|
||||
PaymentSandbox& view,
|
||||
STAmount const& deliver,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
STPathSet const& paths,
|
||||
bool defaultPaths,
|
||||
bool partialPayment,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<STAmount> const& sendMax,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo = nullptr);
|
||||
|
||||
} // namespace xrpl
|
||||
302
include/xrpl/tx/paths/Offer.h
Normal file
302
include/xrpl/tx/paths/Offer.h
Normal file
@@ -0,0 +1,302 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class TIn, class TOut>
|
||||
class TOfferBase
|
||||
{
|
||||
protected:
|
||||
Issue issIn_;
|
||||
Issue issOut_;
|
||||
};
|
||||
|
||||
template <>
|
||||
class TOfferBase<STAmount, STAmount>
|
||||
{
|
||||
public:
|
||||
explicit TOfferBase() = default;
|
||||
};
|
||||
|
||||
template <class TIn = STAmount, class TOut = STAmount>
|
||||
class TOffer : private TOfferBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
SLE::pointer m_entry;
|
||||
Quality m_quality;
|
||||
AccountID m_account;
|
||||
|
||||
TAmounts<TIn, TOut> m_amounts;
|
||||
void
|
||||
setFieldAmounts();
|
||||
|
||||
public:
|
||||
TOffer() = default;
|
||||
|
||||
TOffer(SLE::pointer const& entry, Quality quality);
|
||||
|
||||
/** Returns the quality of the offer.
|
||||
Conceptually, the quality is the ratio of output to input currency.
|
||||
The implementation calculates it as the ratio of input to output
|
||||
currency (so it sorts ascending). The quality is computed at the time
|
||||
the offer is placed, and never changes for the lifetime of the offer.
|
||||
This is an important business rule that maintains accuracy when an
|
||||
offer is partially filled; Subsequent partial fills will use the
|
||||
original quality.
|
||||
*/
|
||||
Quality
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
/** Returns the account id of the offer's owner. */
|
||||
AccountID const&
|
||||
owner() const
|
||||
{
|
||||
return m_account;
|
||||
}
|
||||
|
||||
/** Returns the in and out amounts.
|
||||
Some or all of the out amount may be unfunded.
|
||||
*/
|
||||
TAmounts<TIn, TOut> const&
|
||||
amount() const
|
||||
{
|
||||
return m_amounts;
|
||||
}
|
||||
|
||||
/** Returns `true` if no more funds can flow through this offer. */
|
||||
bool
|
||||
fully_consumed() const
|
||||
{
|
||||
if (m_amounts.in <= beast::zero)
|
||||
return true;
|
||||
if (m_amounts.out <= beast::zero)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Adjusts the offer to indicate that we consumed some (or all) of it. */
|
||||
void
|
||||
consume(ApplyView& view, TAmounts<TIn, TOut> const& consumed)
|
||||
{
|
||||
if (consumed.in > m_amounts.in)
|
||||
Throw<std::logic_error>("can't consume more than is available.");
|
||||
|
||||
if (consumed.out > m_amounts.out)
|
||||
Throw<std::logic_error>("can't produce more than is available.");
|
||||
|
||||
m_amounts -= consumed;
|
||||
setFieldAmounts();
|
||||
view.update(m_entry);
|
||||
}
|
||||
|
||||
std::string
|
||||
id() const
|
||||
{
|
||||
return to_string(m_entry->key());
|
||||
}
|
||||
|
||||
std::optional<uint256>
|
||||
key() const
|
||||
{
|
||||
return m_entry->key();
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueIn() const;
|
||||
Issue const&
|
||||
issueOut() const;
|
||||
|
||||
TAmounts<TIn, TOut>
|
||||
limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const;
|
||||
|
||||
TAmounts<TIn, TOut>
|
||||
limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp) const;
|
||||
|
||||
template <typename... Args>
|
||||
static TER
|
||||
send(Args&&... args);
|
||||
|
||||
bool
|
||||
isFunded() const
|
||||
{
|
||||
// Offer owner is issuer; they have unlimited funds
|
||||
return m_account == issueOut().account;
|
||||
}
|
||||
|
||||
static std::pair<std::uint32_t, std::uint32_t>
|
||||
adjustRates(std::uint32_t ofrInRate, std::uint32_t ofrOutRate)
|
||||
{
|
||||
// CLOB offer pays the transfer fee
|
||||
return {ofrInRate, ofrOutRate};
|
||||
}
|
||||
|
||||
/** Check any required invariant. Limit order book offer
|
||||
* always returns true.
|
||||
*/
|
||||
bool
|
||||
checkInvariant(TAmounts<TIn, TOut> const& consumed, beast::Journal j) const
|
||||
{
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
return true;
|
||||
|
||||
if (consumed.in > m_amounts.in || consumed.out > m_amounts.out)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " << to_string(consumed.in) << " "
|
||||
<< to_string(consumed.out) << " amounts " << to_string(m_amounts.in) << " "
|
||||
<< to_string(m_amounts.out);
|
||||
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using Offer = TOffer<>;
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TOffer<TIn, TOut>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
: m_entry(entry), m_quality(quality), m_account(m_entry->getAccountID(sfAccount))
|
||||
{
|
||||
auto const tp = m_entry->getFieldAmount(sfTakerPays);
|
||||
auto const tg = m_entry->getFieldAmount(sfTakerGets);
|
||||
m_amounts.in = toAmount<TIn>(tp);
|
||||
m_amounts.out = toAmount<TOut>(tg);
|
||||
this->issIn_ = tp.issue();
|
||||
this->issOut_ = tg.issue();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TOffer<STAmount, STAmount>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
: m_entry(entry)
|
||||
, m_quality(quality)
|
||||
, m_account(m_entry->getAccountID(sfAccount))
|
||||
, m_amounts(m_entry->getFieldAmount(sfTakerPays), m_entry->getFieldAmount(sfTakerGets))
|
||||
{
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
void
|
||||
TOffer<TIn, TOut>::setFieldAmounts()
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
#ifdef _MSC_VER
|
||||
UNREACHABLE("xrpl::TOffer::setFieldAmounts : must be specialized");
|
||||
#else
|
||||
static_assert(sizeof(TOut) == -1, "Must be specialized");
|
||||
#endif
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const
|
||||
{
|
||||
// It turns out that the ceil_out implementation has some slop in
|
||||
// it, which ceil_out_strict removes.
|
||||
return quality().ceil_out_strict(offerAmount, limit, roundUp);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp) const
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixReducedOffersV2))
|
||||
// It turns out that the ceil_in implementation has some slop in
|
||||
// it. ceil_in_strict removes that slop. But removing that slop
|
||||
// affects transaction outcomes, so the change must be made using
|
||||
// an amendment.
|
||||
return quality().ceil_in_strict(offerAmount, limit, roundUp);
|
||||
return m_quality.ceil_in(offerAmount, limit);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <typename... Args>
|
||||
TER
|
||||
TOffer<TIn, TOut>::send(Args&&... args)
|
||||
{
|
||||
return accountSend(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<STAmount, STAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, m_amounts.in);
|
||||
m_entry->setFieldAmount(sfTakerGets, m_amounts.out);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<IOUAmount, IOUAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, issIn_));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, issOut_));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<IOUAmount, XRPAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, issIn_));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<XRPAmount, IOUAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, issOut_));
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
Issue const&
|
||||
TOffer<TIn, TOut>::issueIn() const
|
||||
{
|
||||
return this->issIn_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Issue const&
|
||||
TOffer<STAmount, STAmount>::issueIn() const
|
||||
{
|
||||
return m_amounts.in.issue();
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
Issue const&
|
||||
TOffer<TIn, TOut>::issueOut() const
|
||||
{
|
||||
return this->issOut_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Issue const&
|
||||
TOffer<STAmount, STAmount>::issueOut() const
|
||||
{
|
||||
return m_amounts.out.issue();
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, TOffer<TIn, TOut> const& offer)
|
||||
{
|
||||
return os << offer.id();
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
174
include/xrpl/tx/paths/OfferStream.h
Normal file
174
include/xrpl/tx/paths/OfferStream.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/tx/paths/BookTip.h>
|
||||
#include <xrpl/tx/paths/Offer.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class TIn, class TOut>
|
||||
class TOfferStreamBase
|
||||
{
|
||||
public:
|
||||
class StepCounter
|
||||
{
|
||||
private:
|
||||
std::uint32_t const limit_;
|
||||
std::uint32_t count_;
|
||||
beast::Journal j_;
|
||||
|
||||
public:
|
||||
StepCounter(std::uint32_t limit, beast::Journal j) : limit_(limit), count_(0), j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
step()
|
||||
{
|
||||
if (count_ >= limit_)
|
||||
{
|
||||
JLOG(j_.debug()) << "Exceeded " << limit_ << " step limit.";
|
||||
return false;
|
||||
}
|
||||
count_++;
|
||||
return true;
|
||||
}
|
||||
std::uint32_t
|
||||
count() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
beast::Journal const j_;
|
||||
ApplyView& view_;
|
||||
ApplyView& cancelView_;
|
||||
Book book_;
|
||||
bool validBook_;
|
||||
NetClock::time_point const expire_;
|
||||
BookTip tip_;
|
||||
TOffer<TIn, TOut> offer_;
|
||||
std::optional<TOut> ownerFunds_;
|
||||
StepCounter& counter_;
|
||||
|
||||
void
|
||||
erase(ApplyView& view);
|
||||
|
||||
virtual void
|
||||
permRmOffer(uint256 const& offerIndex) = 0;
|
||||
|
||||
template <class TTakerPays, class TTakerGets>
|
||||
bool
|
||||
shouldRmSmallIncreasedQOffer() const;
|
||||
|
||||
public:
|
||||
TOfferStreamBase(
|
||||
ApplyView& view,
|
||||
ApplyView& cancelView,
|
||||
Book const& book,
|
||||
NetClock::time_point when,
|
||||
StepCounter& counter,
|
||||
beast::Journal journal);
|
||||
|
||||
virtual ~TOfferStreamBase() = default;
|
||||
|
||||
/** Returns the offer at the tip of the order book.
|
||||
Offers are always presented in decreasing quality.
|
||||
Only valid if step() returned `true`.
|
||||
*/
|
||||
TOffer<TIn, TOut>&
|
||||
tip() const
|
||||
{
|
||||
return const_cast<TOfferStreamBase*>(this)->offer_;
|
||||
}
|
||||
|
||||
/** Advance to the next valid offer.
|
||||
This automatically removes:
|
||||
- Offers with missing ledger entries
|
||||
- Offers found unfunded
|
||||
- expired offers
|
||||
@return `true` if there is a valid offer.
|
||||
*/
|
||||
bool
|
||||
step();
|
||||
|
||||
TOut
|
||||
ownerFunds() const
|
||||
{
|
||||
return *ownerFunds_;
|
||||
}
|
||||
};
|
||||
|
||||
/** Presents and consumes the offers in an order book.
|
||||
|
||||
Two `ApplyView` objects accumulate changes to the ledger. `view`
|
||||
is applied when the calling transaction succeeds. If the calling
|
||||
transaction fails, then `view_cancel` is applied.
|
||||
|
||||
Certain invalid offers are automatically removed:
|
||||
- Offers with missing ledger entries
|
||||
- Offers that expired
|
||||
- Offers found unfunded:
|
||||
An offer is found unfunded when the corresponding balance is zero
|
||||
and the caller has not modified the balance. This is accomplished
|
||||
by also looking up the balance in the cancel view.
|
||||
|
||||
When an offer is removed, it is removed from both views. This grooms the
|
||||
order book regardless of whether or not the transaction is successful.
|
||||
*/
|
||||
class OfferStream : public TOfferStreamBase<STAmount, STAmount>
|
||||
{
|
||||
protected:
|
||||
void
|
||||
permRmOffer(uint256 const& offerIndex) override;
|
||||
|
||||
public:
|
||||
using TOfferStreamBase<STAmount, STAmount>::TOfferStreamBase;
|
||||
};
|
||||
|
||||
/** Presents and consumes the offers in an order book.
|
||||
|
||||
The `view_' ` `ApplyView` accumulates changes to the ledger.
|
||||
The `cancelView_` is used to determine if an offer is found
|
||||
unfunded or became unfunded.
|
||||
The `permToRemove` collection identifies offers that should be
|
||||
removed even if the strand associated with this OfferStream
|
||||
is not applied.
|
||||
|
||||
Certain invalid offers are added to the `permToRemove` collection:
|
||||
- Offers with missing ledger entries
|
||||
- Offers that expired
|
||||
- Offers found unfunded:
|
||||
An offer is found unfunded when the corresponding balance is zero
|
||||
and the caller has not modified the balance. This is accomplished
|
||||
by also looking up the balance in the cancel view.
|
||||
*/
|
||||
template <class TIn, class TOut>
|
||||
class FlowOfferStream : public TOfferStreamBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
boost::container::flat_set<uint256> permToRemove_;
|
||||
|
||||
public:
|
||||
using TOfferStreamBase<TIn, TOut>::TOfferStreamBase;
|
||||
|
||||
// The following interface allows offer crossing to permanently
|
||||
// remove self crossed offers. The motivation is somewhat
|
||||
// unintuitive. See the discussion in the comments for
|
||||
// BookOfferCrossingStep::limitSelfCrossQuality().
|
||||
void
|
||||
permRmOffer(uint256 const& offerIndex) override;
|
||||
|
||||
boost::container::flat_set<uint256> const&
|
||||
permToRemove() const
|
||||
{
|
||||
return permToRemove_;
|
||||
}
|
||||
};
|
||||
} // namespace xrpl
|
||||
109
include/xrpl/tx/paths/RippleCalc.h
Normal file
109
include/xrpl/tx/paths/RippleCalc.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
class Config;
|
||||
namespace path {
|
||||
|
||||
namespace detail {
|
||||
struct FlowDebugInfo;
|
||||
}
|
||||
|
||||
/** RippleCalc calculates the quality of a payment path.
|
||||
|
||||
Quality is the amount of input required to produce a given output along a
|
||||
specified path - another name for this is exchange rate.
|
||||
*/
|
||||
class RippleCalc
|
||||
{
|
||||
public:
|
||||
struct Input
|
||||
{
|
||||
explicit Input() = default;
|
||||
|
||||
bool partialPaymentAllowed = false;
|
||||
bool defaultPathsAllowed = true;
|
||||
bool limitQuality = false;
|
||||
bool isLedgerOpen = true;
|
||||
};
|
||||
struct Output
|
||||
{
|
||||
explicit Output() = default;
|
||||
|
||||
// The computed input amount.
|
||||
STAmount actualAmountIn;
|
||||
|
||||
// The computed output amount.
|
||||
STAmount actualAmountOut;
|
||||
|
||||
// Collection of offers found expired or unfunded. When a payment
|
||||
// succeeds, unfunded and expired offers are removed. When a payment
|
||||
// fails, they are not removed. This vector contains the offers that
|
||||
// could have been removed but were not because the payment fails. It is
|
||||
// useful for offer crossing, which does remove the offers.
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
|
||||
private:
|
||||
TER calculationResult_ = temUNKNOWN;
|
||||
|
||||
public:
|
||||
TER
|
||||
result() const
|
||||
{
|
||||
return calculationResult_;
|
||||
}
|
||||
void
|
||||
setResult(TER const value)
|
||||
{
|
||||
calculationResult_ = value;
|
||||
}
|
||||
};
|
||||
|
||||
static Output
|
||||
rippleCalculate(
|
||||
PaymentSandbox& view,
|
||||
|
||||
// Compute paths using this ledger entry set. Up to caller to actually
|
||||
// apply to ledger.
|
||||
|
||||
// Issuer:
|
||||
// XRP: xrpAccount()
|
||||
// non-XRP: uSrcAccountID (for any issuer) or another account with
|
||||
// trust node.
|
||||
STAmount const& saMaxAmountReq, // --> -1 = no limit.
|
||||
|
||||
// Issuer:
|
||||
// XRP: xrpAccount()
|
||||
// non-XRP: uDstAccountID (for any issuer) or another account with
|
||||
// trust node.
|
||||
STAmount const& saDstAmountReq,
|
||||
|
||||
AccountID const& uDstAccountID,
|
||||
AccountID const& uSrcAccountID,
|
||||
|
||||
// A set of paths that are included in the transaction that we'll
|
||||
// explore for liquidity.
|
||||
STPathSet const& spsPaths,
|
||||
|
||||
std::optional<uint256> const& domainID,
|
||||
Logs& l,
|
||||
Input const* const pInputs = nullptr);
|
||||
|
||||
// The view we are currently working on
|
||||
PaymentSandbox& view;
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete
|
||||
// unfunded offers in a deterministic order (hence the ordered container).
|
||||
//
|
||||
// Offers that were found unfunded.
|
||||
boost::container::flat_set<uint256> permanentlyUnfundedOffers_;
|
||||
};
|
||||
|
||||
} // namespace path
|
||||
} // namespace xrpl
|
||||
198
include/xrpl/tx/paths/detail/AmountSpec.h
Normal file
198
include/xrpl/tx/paths/detail/AmountSpec.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct AmountSpec
|
||||
{
|
||||
explicit AmountSpec() = default;
|
||||
|
||||
bool native;
|
||||
union
|
||||
{
|
||||
XRPAmount xrp;
|
||||
IOUAmount iou = {};
|
||||
};
|
||||
std::optional<AccountID> issuer;
|
||||
std::optional<Currency> currency;
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, AmountSpec const& amt)
|
||||
{
|
||||
if (amt.native)
|
||||
stream << to_string(amt.xrp);
|
||||
else
|
||||
stream << to_string(amt.iou);
|
||||
if (amt.currency)
|
||||
stream << "/(" << *amt.currency << ")";
|
||||
if (amt.issuer)
|
||||
stream << "/" << *amt.issuer << "";
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
struct EitherAmount
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
bool native = false;
|
||||
#endif
|
||||
|
||||
union
|
||||
{
|
||||
IOUAmount iou = {};
|
||||
XRPAmount xrp;
|
||||
};
|
||||
|
||||
EitherAmount() = default;
|
||||
|
||||
explicit EitherAmount(IOUAmount const& a) : iou(a)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
// ignore warning about half of iou amount being uninitialized
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
explicit EitherAmount(XRPAmount const& a) : xrp(a)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
native = true;
|
||||
#endif
|
||||
}
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
explicit EitherAmount(AmountSpec const& a)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
native = a.native;
|
||||
#endif
|
||||
if (a.native)
|
||||
xrp = a.xrp;
|
||||
else
|
||||
iou = a.iou;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, EitherAmount const& amt)
|
||||
{
|
||||
if (amt.native)
|
||||
stream << to_string(amt.xrp);
|
||||
else
|
||||
stream << to_string(amt.iou);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T&
|
||||
get(EitherAmount& amt)
|
||||
{
|
||||
static_assert(sizeof(T) == -1, "Must used specialized function");
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline IOUAmount&
|
||||
get<IOUAmount>(EitherAmount& amt)
|
||||
{
|
||||
XRPL_ASSERT(!amt.native, "xrpl::get<IOUAmount>(EitherAmount&) : is not XRP");
|
||||
return amt.iou;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline XRPAmount&
|
||||
get<XRPAmount>(EitherAmount& amt)
|
||||
{
|
||||
XRPL_ASSERT(amt.native, "xrpl::get<XRPAmount>(EitherAmount&) : is XRP");
|
||||
return amt.xrp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T const&
|
||||
get(EitherAmount const& amt)
|
||||
{
|
||||
static_assert(sizeof(T) == -1, "Must used specialized function");
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline IOUAmount const&
|
||||
get<IOUAmount>(EitherAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(!amt.native, "xrpl::get<IOUAmount>(EitherAmount const&) : is not XRP");
|
||||
return amt.iou;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline XRPAmount const&
|
||||
get<XRPAmount>(EitherAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(amt.native, "xrpl::get<XRPAmount>(EitherAmount const&) : is XRP");
|
||||
return amt.xrp;
|
||||
}
|
||||
|
||||
inline AmountSpec
|
||||
toAmountSpec(STAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amt.mantissa() < std::numeric_limits<std::int64_t>::max(),
|
||||
"xrpl::toAmountSpec(STAmount const&) : maximum mantissa");
|
||||
bool const isNeg = amt.negative();
|
||||
std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
|
||||
AmountSpec result;
|
||||
|
||||
result.native = isXRP(amt);
|
||||
if (result.native)
|
||||
{
|
||||
result.xrp = XRPAmount(sMant);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.iou = IOUAmount(sMant, amt.exponent());
|
||||
result.issuer = amt.issue().account;
|
||||
result.currency = amt.issue().currency;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline EitherAmount
|
||||
toEitherAmount(STAmount const& amt)
|
||||
{
|
||||
if (isXRP(amt))
|
||||
return EitherAmount{amt.xrp()};
|
||||
return EitherAmount{amt.iou()};
|
||||
}
|
||||
|
||||
inline AmountSpec
|
||||
toAmountSpec(EitherAmount const& ea, std::optional<Currency> const& c)
|
||||
{
|
||||
AmountSpec r;
|
||||
r.native = (!c || isXRP(*c));
|
||||
r.currency = c;
|
||||
XRPL_ASSERT(
|
||||
ea.native == r.native,
|
||||
"xrpl::toAmountSpec(EitherAmount const&&, std::optional<Currency>) : "
|
||||
"matching native");
|
||||
if (r.native)
|
||||
{
|
||||
r.xrp = ea.xrp;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.iou = ea.iou;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
24
include/xrpl/tx/paths/detail/FlatSets.h
Normal file
24
include/xrpl/tx/paths/detail/FlatSets.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Given two flat sets dst and src, compute dst = dst union src
|
||||
|
||||
@param dst set to store the resulting union, and also a source of elements
|
||||
for the union
|
||||
@param src second source of elements for the union
|
||||
*/
|
||||
template <class T>
|
||||
void
|
||||
SetUnion(boost::container::flat_set<T>& dst, boost::container::flat_set<T> const& src)
|
||||
{
|
||||
if (src.empty())
|
||||
return;
|
||||
|
||||
dst.reserve(dst.size() + src.size());
|
||||
dst.insert(boost::container::ordered_unique_range_t{}, src.begin(), src.end());
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
329
include/xrpl/tx/paths/detail/FlowDebugInfo.h
Normal file
329
include/xrpl/tx/paths/detail/FlowDebugInfo.h
Normal file
@@ -0,0 +1,329 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
namespace xrpl {
|
||||
namespace path {
|
||||
namespace detail {
|
||||
// Track performance information of a single payment
|
||||
struct FlowDebugInfo
|
||||
{
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
using time_point = clock::time_point;
|
||||
boost::container::flat_map<std::string, std::pair<time_point, time_point>> timePoints;
|
||||
boost::container::flat_map<std::string, std::size_t> counts;
|
||||
|
||||
struct PassInfo
|
||||
{
|
||||
PassInfo() = delete;
|
||||
PassInfo(bool nativeIn_, bool nativeOut_) : nativeIn(nativeIn_), nativeOut(nativeOut_)
|
||||
{
|
||||
}
|
||||
bool const nativeIn;
|
||||
bool const nativeOut;
|
||||
std::vector<EitherAmount> in;
|
||||
std::vector<EitherAmount> out;
|
||||
std::vector<size_t> numActive;
|
||||
|
||||
std::vector<std::vector<EitherAmount>> liquiditySrcIn;
|
||||
std::vector<std::vector<EitherAmount>> liquiditySrcOut;
|
||||
|
||||
void
|
||||
reserve(size_t s)
|
||||
{
|
||||
in.reserve(s);
|
||||
out.reserve(s);
|
||||
liquiditySrcIn.reserve(s);
|
||||
liquiditySrcOut.reserve(s);
|
||||
numActive.reserve(s);
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
return in.size();
|
||||
}
|
||||
|
||||
void
|
||||
push_back(EitherAmount const& in_amt, EitherAmount const& out_amt, std::size_t active)
|
||||
{
|
||||
in.push_back(in_amt);
|
||||
out.push_back(out_amt);
|
||||
numActive.push_back(active);
|
||||
}
|
||||
|
||||
void
|
||||
pushLiquiditySrc(EitherAmount const& eIn, EitherAmount const& eOut)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!liquiditySrcIn.empty(),
|
||||
"xrpl::path::detail::FlowDebugInfo::pushLiquiditySrc : "
|
||||
"non-empty liquidity source");
|
||||
liquiditySrcIn.back().push_back(eIn);
|
||||
liquiditySrcOut.back().push_back(eOut);
|
||||
}
|
||||
|
||||
void
|
||||
newLiquidityPass()
|
||||
{
|
||||
auto const s = liquiditySrcIn.size();
|
||||
size_t const r = !numActive.empty() ? numActive.back() : 16;
|
||||
liquiditySrcIn.resize(s + 1);
|
||||
liquiditySrcIn.back().reserve(r);
|
||||
liquiditySrcOut.resize(s + 1);
|
||||
liquiditySrcOut.back().reserve(r);
|
||||
}
|
||||
};
|
||||
|
||||
PassInfo passInfo;
|
||||
|
||||
FlowDebugInfo() = delete;
|
||||
FlowDebugInfo(bool nativeIn, bool nativeOut) : passInfo(nativeIn, nativeOut)
|
||||
{
|
||||
timePoints.reserve(16);
|
||||
counts.reserve(16);
|
||||
passInfo.reserve(64);
|
||||
}
|
||||
|
||||
auto
|
||||
duration(std::string const& tag) const
|
||||
{
|
||||
auto i = timePoints.find(tag);
|
||||
if (i == timePoints.end())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::path::detail::FlowDebugInfo::duration : timepoint not "
|
||||
"found");
|
||||
return std::chrono::duration<double>(0);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const& t = i->second;
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(t.second - t.first);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
count(std::string const& tag) const
|
||||
{
|
||||
auto i = counts.find(tag);
|
||||
if (i == counts.end())
|
||||
return 0;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
// Time the duration of the existence of the result
|
||||
auto
|
||||
timeBlock(std::string name)
|
||||
{
|
||||
struct Stopper
|
||||
{
|
||||
std::string tag;
|
||||
FlowDebugInfo* info;
|
||||
Stopper(std::string name, FlowDebugInfo& pi) : tag(std::move(name)), info(&pi)
|
||||
{
|
||||
auto const start = FlowDebugInfo::clock::now();
|
||||
info->timePoints.emplace(tag, std::make_pair(start, start));
|
||||
}
|
||||
~Stopper()
|
||||
{
|
||||
auto const end = FlowDebugInfo::clock::now();
|
||||
info->timePoints[tag].second = end;
|
||||
}
|
||||
Stopper(Stopper&&) = default;
|
||||
};
|
||||
return Stopper(std::move(name), *this);
|
||||
}
|
||||
|
||||
void
|
||||
inc(std::string const& tag)
|
||||
{
|
||||
auto i = counts.find(tag);
|
||||
if (i == counts.end())
|
||||
{
|
||||
counts[tag] = 1;
|
||||
}
|
||||
++i->second;
|
||||
}
|
||||
|
||||
void
|
||||
setCount(std::string const& tag, std::size_t c)
|
||||
{
|
||||
counts[tag] = c;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
passCount() const
|
||||
{
|
||||
return passInfo.size();
|
||||
}
|
||||
|
||||
void
|
||||
pushPass(EitherAmount const& in, EitherAmount const& out, std::size_t activeStrands)
|
||||
{
|
||||
passInfo.push_back(in, out, activeStrands);
|
||||
}
|
||||
|
||||
void
|
||||
pushLiquiditySrc(EitherAmount const& in, EitherAmount const& out)
|
||||
{
|
||||
passInfo.pushLiquiditySrc(in, out);
|
||||
}
|
||||
|
||||
void
|
||||
newLiquidityPass()
|
||||
{
|
||||
passInfo.newLiquidityPass();
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(bool writePassInfo) const
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
|
||||
auto const d = duration("main");
|
||||
|
||||
ostr << "duration: " << d.count() << ", pass_count: " << passCount();
|
||||
|
||||
if (writePassInfo)
|
||||
{
|
||||
auto write_list = [&ostr](auto const& vals, auto&& fun, char delim = ';') {
|
||||
ostr << '[';
|
||||
if (!vals.empty())
|
||||
{
|
||||
ostr << fun(vals[0]);
|
||||
for (size_t i = 1, e = vals.size(); i < e; ++i)
|
||||
ostr << delim << fun(vals[i]);
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
auto writeXrpAmtList = [&write_list](std::vector<EitherAmount> const& amts, char delim = ';') {
|
||||
auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.xrp); };
|
||||
write_list(amts, get_val, delim);
|
||||
};
|
||||
auto writeIouAmtList = [&write_list](std::vector<EitherAmount> const& amts, char delim = ';') {
|
||||
auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.iou); };
|
||||
write_list(amts, get_val, delim);
|
||||
};
|
||||
auto writeIntList = [&write_list](std::vector<size_t> const& vals, char delim = ';') {
|
||||
auto get_val = [](size_t const& v) -> size_t const& { return v; };
|
||||
write_list(vals, get_val);
|
||||
};
|
||||
auto writeNestedIouAmtList = [&ostr, &writeIouAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
|
||||
ostr << '[';
|
||||
if (!amts.empty())
|
||||
{
|
||||
writeIouAmtList(amts[0], '|');
|
||||
for (size_t i = 1, e = amts.size(); i < e; ++i)
|
||||
{
|
||||
ostr << ';';
|
||||
writeIouAmtList(amts[i], '|');
|
||||
}
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
auto writeNestedXrpAmtList = [&ostr, &writeXrpAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
|
||||
ostr << '[';
|
||||
if (!amts.empty())
|
||||
{
|
||||
writeXrpAmtList(amts[0], '|');
|
||||
for (size_t i = 1, e = amts.size(); i < e; ++i)
|
||||
{
|
||||
ostr << ';';
|
||||
writeXrpAmtList(amts[i], '|');
|
||||
}
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
|
||||
ostr << ", in_pass: ";
|
||||
if (passInfo.nativeIn)
|
||||
writeXrpAmtList(passInfo.in);
|
||||
else
|
||||
writeIouAmtList(passInfo.in);
|
||||
ostr << ", out_pass: ";
|
||||
if (passInfo.nativeOut)
|
||||
writeXrpAmtList(passInfo.out);
|
||||
else
|
||||
writeIouAmtList(passInfo.out);
|
||||
ostr << ", num_active: ";
|
||||
writeIntList(passInfo.numActive);
|
||||
if (!passInfo.liquiditySrcIn.empty() && !passInfo.liquiditySrcIn.back().empty())
|
||||
{
|
||||
ostr << ", l_src_in: ";
|
||||
if (passInfo.nativeIn)
|
||||
writeNestedXrpAmtList(passInfo.liquiditySrcIn);
|
||||
else
|
||||
writeNestedIouAmtList(passInfo.liquiditySrcIn);
|
||||
ostr << ", l_src_out: ";
|
||||
if (passInfo.nativeOut)
|
||||
writeNestedXrpAmtList(passInfo.liquiditySrcOut);
|
||||
else
|
||||
writeNestedIouAmtList(passInfo.liquiditySrcOut);
|
||||
}
|
||||
}
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
};
|
||||
|
||||
inline void
|
||||
writeDiffElement(std::ostringstream& ostr, std::pair<std::tuple<AccountID, AccountID, Currency>, STAmount> const& elem)
|
||||
{
|
||||
using namespace std;
|
||||
auto const k = elem.first;
|
||||
auto const v = elem.second;
|
||||
ostr << '[' << get<0>(k) << '|' << get<1>(k) << '|' << get<2>(k) << '|' << v << ']';
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
void
|
||||
writeDiffs(std::ostringstream& ostr, Iter begin, Iter end)
|
||||
{
|
||||
ostr << '[';
|
||||
if (begin != end)
|
||||
{
|
||||
writeDiffElement(ostr, *begin);
|
||||
++begin;
|
||||
}
|
||||
for (; begin != end; ++begin)
|
||||
{
|
||||
ostr << ';';
|
||||
writeDiffElement(ostr, *begin);
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
|
||||
using BalanceDiffs = std::pair<std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>, XRPAmount>;
|
||||
|
||||
inline BalanceDiffs
|
||||
balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
|
||||
{
|
||||
return {sb.balanceChanges(rv), sb.xrpDestroyed()};
|
||||
}
|
||||
|
||||
inline std::string
|
||||
balanceDiffsToString(std::optional<BalanceDiffs> const& bd)
|
||||
{
|
||||
if (!bd)
|
||||
return std::string{};
|
||||
auto const& diffs = bd->first;
|
||||
auto const& xrpDestroyed = bd->second;
|
||||
std::ostringstream ostr;
|
||||
ostr << ", xrpDestroyed: " << to_string(xrpDestroyed);
|
||||
ostr << ", balanceDiffs: ";
|
||||
writeDiffs(ostr, diffs.begin(), diffs.end());
|
||||
return ostr.str();
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace path
|
||||
} // namespace xrpl
|
||||
591
include/xrpl/tx/paths/detail/Steps.h
Normal file
591
include/xrpl/tx/paths/detail/Steps.h
Normal file
@@ -0,0 +1,591 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/QualityFunction.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
class PaymentSandbox;
|
||||
class ReadView;
|
||||
class ApplyView;
|
||||
class AMMContext;
|
||||
|
||||
enum class DebtDirection { issues, redeems };
|
||||
enum class QualityDirection { in, out };
|
||||
enum class StrandDirection { forward, reverse };
|
||||
enum OfferCrossing { no = 0, yes = 1, sell = 2 };
|
||||
|
||||
inline bool
|
||||
redeems(DebtDirection dir)
|
||||
{
|
||||
return dir == DebtDirection::redeems;
|
||||
}
|
||||
|
||||
inline bool
|
||||
issues(DebtDirection dir)
|
||||
{
|
||||
return dir == DebtDirection::issues;
|
||||
}
|
||||
|
||||
/**
|
||||
A step in a payment path
|
||||
|
||||
There are five concrete step classes:
|
||||
DirectStepI is an IOU step between accounts
|
||||
BookStepII is an IOU/IOU offer book
|
||||
BookStepIX is an IOU/XRP offer book
|
||||
BookStepXI is an XRP/IOU offer book
|
||||
XRPEndpointStep is the source or destination account for XRP
|
||||
|
||||
Amounts may be transformed through a step in either the forward or the
|
||||
reverse direction. In the forward direction, the function `fwd` is used to
|
||||
find the amount the step would output given an input amount. In the reverse
|
||||
direction, the function `rev` is used to find the amount of input needed to
|
||||
produce the desired output.
|
||||
|
||||
Amounts are always transformed using liquidity with the same quality (quality
|
||||
is the amount out/amount in). For example, a BookStep may use multiple offers
|
||||
when executing `fwd` or `rev`, but all those offers will be from the same
|
||||
quality directory.
|
||||
|
||||
A step may not have enough liquidity to transform the entire requested
|
||||
amount. Both `fwd` and `rev` return a pair of amounts (one for input amount,
|
||||
one for output amount) that show how much of the requested amount the step
|
||||
was actually able to use.
|
||||
*/
|
||||
class Step
|
||||
{
|
||||
public:
|
||||
virtual ~Step() = default;
|
||||
|
||||
/**
|
||||
Find the amount we need to put into the step to get the requested out
|
||||
subject to liquidity limits
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param ofrsToRm offers found unfunded or in an error state are added to
|
||||
this collection
|
||||
@param out requested step output
|
||||
@return actual step input and output
|
||||
*/
|
||||
virtual std::pair<EitherAmount, EitherAmount>
|
||||
rev(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) = 0;
|
||||
|
||||
/**
|
||||
Find the amount we get out of the step given the input
|
||||
subject to liquidity limits
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param ofrsToRm offers found unfunded or in an error state are added to
|
||||
this collection
|
||||
@param in requested step input
|
||||
@return actual step input and output
|
||||
*/
|
||||
virtual std::pair<EitherAmount, EitherAmount>
|
||||
fwd(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) = 0;
|
||||
|
||||
/**
|
||||
Amount of currency computed coming into the Step the last time the
|
||||
step ran in reverse.
|
||||
*/
|
||||
virtual std::optional<EitherAmount>
|
||||
cachedIn() const = 0;
|
||||
|
||||
/**
|
||||
Amount of currency computed coming out of the Step the last time the
|
||||
step ran in reverse.
|
||||
*/
|
||||
virtual std::optional<EitherAmount>
|
||||
cachedOut() const = 0;
|
||||
|
||||
/**
|
||||
If this step is DirectStepI (IOU->IOU direct step), return the src
|
||||
account. This is needed for checkNoRipple.
|
||||
*/
|
||||
virtual std::optional<AccountID>
|
||||
directStepSrcAcct() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// for debugging. Return the src and dst accounts for a direct step
|
||||
// For XRP endpoints, one of src or dst will be the root account
|
||||
virtual std::optional<std::pair<AccountID, AccountID>>
|
||||
directStepAccts() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
If this step is a DirectStepI and the src redeems to the dst, return
|
||||
true, otherwise return false. If this step is a BookStep, return false if
|
||||
the owner pays the transfer fee, otherwise return true.
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param dir reverse -> called from rev(); forward -> called from fwd().
|
||||
*/
|
||||
virtual DebtDirection
|
||||
debtDirection(ReadView const& sb, StrandDirection dir) const = 0;
|
||||
|
||||
/**
|
||||
If this step is a DirectStepI, return the quality in of the dst account.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
lineQualityIn(ReadView const&) const
|
||||
{
|
||||
return QUALITY_ONE;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
Find an upper bound of quality for the step
|
||||
|
||||
@param v view to query the ledger state from
|
||||
@param prevStepDir Set to DebtDirection::redeems if the previous step redeems.
|
||||
@return A pair. The first element is the upper bound of quality for the step, or std::nullopt if the
|
||||
step is dry. The second element will be set to DebtDirection::redeems if this steps redeems,
|
||||
DebtDirection:issues if this step issues.
|
||||
@note it is an upper bound because offers on the books may be unfunded.
|
||||
If there is always a funded offer at the tip of the book, then we could
|
||||
rename this `theoreticalQuality` rather than `qualityUpperBound`. It
|
||||
could still differ from the actual quality, but except for "dust" amounts,
|
||||
it should be a good estimate for the actual quality.
|
||||
*/
|
||||
// clang-format on
|
||||
virtual std::pair<std::optional<Quality>, DebtDirection>
|
||||
qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const = 0;
|
||||
|
||||
/** Get QualityFunction. Used in one path optimization where
|
||||
* the quality function is non-constant (has AMM) and there is
|
||||
* limitQuality. QualityFunction allows calculation of
|
||||
* required path output given requested limitQuality.
|
||||
* All steps, except for BookStep have the default
|
||||
* implementation.
|
||||
*/
|
||||
virtual std::pair<std::optional<QualityFunction>, DebtDirection>
|
||||
getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const;
|
||||
|
||||
/** Return the number of offers consumed or partially consumed the last time
|
||||
the step ran, including expired and unfunded offers.
|
||||
|
||||
N.B. This this not the total number offers consumed by this step for the
|
||||
entire payment, it is only the number the last time it ran. Offers may
|
||||
be partially consumed multiple times during a payment.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
offersUsed() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
If this step is a BookStep, return the book.
|
||||
*/
|
||||
virtual std::optional<Book>
|
||||
bookStepBook() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
Check if amount is zero
|
||||
*/
|
||||
virtual bool
|
||||
isZero(EitherAmount const& out) const = 0;
|
||||
|
||||
/**
|
||||
Return true if the step should be considered inactive.
|
||||
A strand that has additional liquidity may be marked inactive if a step
|
||||
has consumed too many offers.
|
||||
*/
|
||||
virtual bool
|
||||
inactive() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
Return true if Out of lhs == Out of rhs.
|
||||
*/
|
||||
virtual bool
|
||||
equalOut(EitherAmount const& lhs, EitherAmount const& rhs) const = 0;
|
||||
|
||||
/**
|
||||
Return true if In of lhs == In of rhs.
|
||||
*/
|
||||
virtual bool
|
||||
equalIn(EitherAmount const& lhs, EitherAmount const& rhs) const = 0;
|
||||
|
||||
/**
|
||||
Check that the step can correctly execute in the forward direction
|
||||
|
||||
@param sb view with the strands state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param in requested step input
|
||||
@return first element is true if step is valid, second element is out
|
||||
amount
|
||||
*/
|
||||
virtual std::pair<bool, EitherAmount>
|
||||
validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) = 0;
|
||||
|
||||
/** Return true if lhs == rhs.
|
||||
|
||||
@param lhs Step to compare.
|
||||
@param rhs Step to compare.
|
||||
@return true if lhs == rhs.
|
||||
*/
|
||||
friend bool
|
||||
operator==(Step const& lhs, Step const& rhs)
|
||||
{
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
/** Return true if lhs != rhs.
|
||||
|
||||
@param lhs Step to compare.
|
||||
@param rhs Step to compare.
|
||||
@return true if lhs != rhs.
|
||||
*/
|
||||
friend bool
|
||||
operator!=(Step const& lhs, Step const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** Streaming operator for a Step. */
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, Step const& step)
|
||||
{
|
||||
stream << step.logString();
|
||||
return stream;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string
|
||||
logString() const = 0;
|
||||
|
||||
virtual bool
|
||||
equal(Step const& rhs) const = 0;
|
||||
};
|
||||
|
||||
inline std::pair<std::optional<QualityFunction>, DebtDirection>
|
||||
Step::getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const
|
||||
{
|
||||
if (auto const res = qualityUpperBound(v, prevStepDir); res.first)
|
||||
return {QualityFunction{*res.first, QualityFunction::CLOBLikeTag{}}, res.second};
|
||||
else
|
||||
return {std::nullopt, res.second};
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
using Strand = std::vector<std::unique_ptr<Step>>;
|
||||
|
||||
inline std::uint32_t
|
||||
offersUsed(Strand const& strand)
|
||||
{
|
||||
std::uint32_t r = 0;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (step)
|
||||
r += step->offersUsed();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
inline bool
|
||||
operator==(Strand const& lhs, Strand const& rhs)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
for (size_t i = 0, e = lhs.size(); i != e; ++i)
|
||||
if (*lhs[i] != *rhs[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/*
|
||||
Normalize a path by inserting implied accounts and offers
|
||||
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param sendMax Optional asset to send.
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@return error code and normalized path
|
||||
*/
|
||||
std::pair<TER, STPath>
|
||||
normalizePath(
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path);
|
||||
|
||||
/**
|
||||
Create a Strand for the specified path
|
||||
|
||||
@param sb view for trust lines, balances, and attributes like auth and freeze
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param limitQuality Offer crossing BookSteps use this value in an
|
||||
optimization. If, during direct offer crossing, the
|
||||
quality of the tip of the book drops below this value,
|
||||
then evaluating the strand can stop.
|
||||
@param sendMaxIssue Optional asset to send.
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@param ownerPaysTransferFee false -> charge sender; true -> charge offer
|
||||
owner
|
||||
@param offerCrossing false -> payment; true -> offer crossing
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param domainID the domain that order books will use
|
||||
@param j Journal for logging messages
|
||||
@return Error code and constructed Strand
|
||||
*/
|
||||
std::pair<TER, Strand>
|
||||
toStrand(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
AMMContext& ammContext,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
Create a Strand for each specified path (including the default path, if
|
||||
indicated)
|
||||
|
||||
@param sb View for trust lines, balances, and attributes like auth and freeze
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param limitQuality Offer crossing BookSteps use this value in an
|
||||
optimization. If, during direct offer crossing, the
|
||||
quality of the tip of the book drops below this value,
|
||||
then evaluating the strand can stop.
|
||||
@param sendMax Optional asset to send.
|
||||
@param paths Paths to use to fulfill the payment. Each path in the pathset
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@param addDefaultPath Determines if the default path should be included
|
||||
@param ownerPaysTransferFee false -> charge sender; true -> charge offer
|
||||
owner
|
||||
@param offerCrossing false -> payment; true -> offer crossing
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param domainID the domain that order books will use
|
||||
@param j Journal for logging messages
|
||||
@return error code and collection of strands
|
||||
*/
|
||||
std::pair<TER, std::vector<Strand>>
|
||||
toStrands(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMax,
|
||||
STPathSet const& paths,
|
||||
bool addDefaultPath,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
AMMContext& ammContext,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j);
|
||||
|
||||
/// @cond INTERNAL
|
||||
template <class TIn, class TOut, class TDerived>
|
||||
struct StepImp : public Step
|
||||
{
|
||||
explicit StepImp() = default;
|
||||
|
||||
std::pair<EitherAmount, EitherAmount>
|
||||
rev(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) override
|
||||
{
|
||||
auto const r = static_cast<TDerived*>(this)->revImp(sb, afView, ofrsToRm, get<TOut>(out));
|
||||
return {EitherAmount(r.first), EitherAmount(r.second)};
|
||||
}
|
||||
|
||||
// Given the requested amount to consume, compute the amount produced.
|
||||
// Return the consumed/produced
|
||||
std::pair<EitherAmount, EitherAmount>
|
||||
fwd(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) override
|
||||
{
|
||||
auto const r = static_cast<TDerived*>(this)->fwdImp(sb, afView, ofrsToRm, get<TIn>(in));
|
||||
return {EitherAmount(r.first), EitherAmount(r.second)};
|
||||
}
|
||||
|
||||
bool
|
||||
isZero(EitherAmount const& out) const override
|
||||
{
|
||||
return get<TOut>(out) == beast::zero;
|
||||
}
|
||||
|
||||
bool
|
||||
equalOut(EitherAmount const& lhs, EitherAmount const& rhs) const override
|
||||
{
|
||||
return get<TOut>(lhs) == get<TOut>(rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
equalIn(EitherAmount const& lhs, EitherAmount const& rhs) const override
|
||||
{
|
||||
return get<TIn>(lhs) == get<TIn>(rhs);
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
// Thrown when unexpected errors occur
|
||||
class FlowException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
TER ter;
|
||||
|
||||
FlowException(TER t, std::string const& msg) : std::runtime_error(msg), ter(t)
|
||||
{
|
||||
}
|
||||
|
||||
explicit FlowException(TER t) : std::runtime_error(transHuman(t)), ter(t)
|
||||
{
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
// Check equal with tolerance
|
||||
bool
|
||||
checkNear(IOUAmount const& expected, IOUAmount const& actual);
|
||||
bool
|
||||
checkNear(XRPAmount const& expected, XRPAmount const& actual);
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
Context needed to build Strand Steps and for error checking
|
||||
*/
|
||||
struct StrandContext
|
||||
{
|
||||
ReadView const& view; ///< Current ReadView
|
||||
AccountID const strandSrc; ///< Strand source account
|
||||
AccountID const strandDst; ///< Strand destination account
|
||||
Issue const strandDeliver; ///< Issue strand delivers
|
||||
std::optional<Quality> const limitQuality; ///< Worst accepted quality
|
||||
bool const isFirst; ///< true if Step is first in Strand
|
||||
bool const isLast = false; ///< true if Step is last in Strand
|
||||
bool const ownerPaysTransferFee; ///< true if owner, not sender, pays fee
|
||||
OfferCrossing const offerCrossing; ///< Yes/Sell if offer crossing, not payment
|
||||
bool const isDefaultPath; ///< true if Strand is default path
|
||||
size_t const strandSize; ///< Length of Strand
|
||||
/** The previous step in the strand. Needed to check the no ripple
|
||||
constraint
|
||||
*/
|
||||
Step const* const prevStep = nullptr;
|
||||
/** A strand may not include the same account node more than once
|
||||
in the same currency. In a direct step, an account will show up
|
||||
at most twice: once as a src and once as a dst (hence the two element
|
||||
array). The strandSrc and strandDst will only show up once each.
|
||||
*/
|
||||
std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues;
|
||||
/** A strand may not include an offer that output the same issue more
|
||||
than once
|
||||
*/
|
||||
boost::container::flat_set<Issue>& seenBookOuts;
|
||||
AMMContext& ammContext;
|
||||
std::optional<uint256> domainID; // the domain the order book will use
|
||||
beast::Journal const j;
|
||||
|
||||
/** StrandContext constructor. */
|
||||
StrandContext(
|
||||
ReadView const& view_,
|
||||
std::vector<std::unique_ptr<Step>> const& strand_,
|
||||
// A strand may not include an inner node that
|
||||
// replicates the source or destination.
|
||||
AccountID const& strandSrc_,
|
||||
AccountID const& strandDst_,
|
||||
Issue const& strandDeliver_,
|
||||
std::optional<Quality> const& limitQuality_,
|
||||
bool isLast_,
|
||||
bool ownerPaysTransferFee_,
|
||||
OfferCrossing offerCrossing_,
|
||||
bool isDefaultPath_,
|
||||
std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_, ///< For detecting currency loops
|
||||
boost::container::flat_set<Issue>& seenBookOuts_, ///< For detecting book loops
|
||||
AMMContext& ammContext_,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j_); ///< Journal for logging
|
||||
};
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace test {
|
||||
// Needed for testing
|
||||
bool
|
||||
directStepEqual(Step const& step, AccountID const& src, AccountID const& dst, Currency const& currency);
|
||||
|
||||
bool
|
||||
xrpEndpointStepEqual(Step const& step, AccountID const& acc);
|
||||
|
||||
bool
|
||||
bookStepEqual(Step const& step, xrpl::Book const& book);
|
||||
} // namespace test
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_DirectStepI(StrandContext const& ctx, AccountID const& src, AccountID const& dst, Currency const& c);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepIX(StrandContext const& ctx, Issue const& in);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepXI(StrandContext const& ctx, Issue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_XRPEndpointStep(StrandContext const& ctx, AccountID const& acc);
|
||||
|
||||
template <class InAmt, class OutAmt>
|
||||
bool
|
||||
isDirectXrpToXrp(Strand const& strand);
|
||||
/// @endcond
|
||||
|
||||
} // namespace xrpl
|
||||
784
include/xrpl/tx/paths/detail/StrandFlow.h
Normal file
784
include/xrpl/tx/paths/detail/StrandFlow.h
Normal file
@@ -0,0 +1,784 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/Credit.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/paths/Flow.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
#include <xrpl/tx/paths/detail/FlatSets.h>
|
||||
#include <xrpl/tx/paths/detail/FlowDebugInfo.h>
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
#include <xrpl/tx/transactors/AMM/AMMContext.h>
|
||||
#include <xrpl/tx/transactors/AMM/AMMHelpers.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Result of flow() execution of a single Strand. */
|
||||
template <class TInAmt, class TOutAmt>
|
||||
struct StrandResult
|
||||
{
|
||||
bool success; ///< Strand succeeded
|
||||
TInAmt in = beast::zero; ///< Currency amount in
|
||||
TOutAmt out = beast::zero; ///< Currency amount out
|
||||
std::optional<PaymentSandbox> sandbox; ///< Resulting Sandbox state
|
||||
boost::container::flat_set<uint256> ofrsToRm; ///< Offers to remove
|
||||
// Num offers consumed or partially consumed (includes expired and unfunded
|
||||
// offers)
|
||||
std::uint32_t ofrsUsed = 0;
|
||||
// strand can be inactive if there is no more liquidity or too many offers
|
||||
// have been consumed
|
||||
bool inactive = false; ///< Strand should not considered as a further
|
||||
///< source of liquidity (dry)
|
||||
|
||||
/** Strand result constructor */
|
||||
StrandResult() = default;
|
||||
|
||||
StrandResult(
|
||||
Strand const& strand,
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_,
|
||||
boost::container::flat_set<uint256> ofrsToRm_,
|
||||
bool inactive_)
|
||||
: success(true)
|
||||
, in(in_)
|
||||
, out(out_)
|
||||
, sandbox(std::move(sandbox_))
|
||||
, ofrsToRm(std::move(ofrsToRm_))
|
||||
, ofrsUsed(offersUsed(strand))
|
||||
, inactive(inactive_)
|
||||
{
|
||||
}
|
||||
|
||||
StrandResult(Strand const& strand, boost::container::flat_set<uint256> ofrsToRm_)
|
||||
: success(false), ofrsToRm(std::move(ofrsToRm_)), ofrsUsed(offersUsed(strand))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Request `out` amount from a strand
|
||||
|
||||
@param baseView Trust lines and balances
|
||||
@param strand Steps of Accounts to ripple through and offer books to use
|
||||
@param maxIn Max amount of input allowed
|
||||
@param out Amount of output requested from the strand
|
||||
@param j Journal to write log messages to
|
||||
@return Actual amount in and out from the strand, errors, offers to remove,
|
||||
and payment sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
StrandResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
Strand const& strand,
|
||||
std::optional<TInAmt> const& maxIn,
|
||||
TOutAmt const& out,
|
||||
beast::Journal j)
|
||||
{
|
||||
using Result = StrandResult<TInAmt, TOutAmt>;
|
||||
if (strand.empty())
|
||||
{
|
||||
JLOG(j.warn()) << "Empty strand passed to Liquidity";
|
||||
return {};
|
||||
}
|
||||
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
|
||||
if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
|
||||
{
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::size_t const s = strand.size();
|
||||
|
||||
std::size_t limitingStep = strand.size();
|
||||
std::optional<PaymentSandbox> sb(&baseView);
|
||||
// The "all funds" view determines if an offer becomes unfunded or is
|
||||
// found unfunded
|
||||
// These are the account balances before the strand executes
|
||||
std::optional<PaymentSandbox> afView(&baseView);
|
||||
EitherAmount limitStepOut;
|
||||
{
|
||||
EitherAmount stepOut(out);
|
||||
for (auto i = s; i--;)
|
||||
{
|
||||
auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
JLOG(j.trace()) << "Strand found dry in rev";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
|
||||
if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
|
||||
{
|
||||
// limiting - exceeded maxIn
|
||||
// Throw out previous results
|
||||
sb.emplace(&baseView);
|
||||
limitingStep = i;
|
||||
|
||||
// re-execute the limiting step
|
||||
r = strand[i]->fwd(*sb, *afView, ofrsToRm, EitherAmount(*maxIn));
|
||||
limitStepOut = r.second;
|
||||
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
JLOG(j.trace()) << "First step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (get<TInAmt>(r.first) != *maxIn)
|
||||
{
|
||||
// Something is very wrong
|
||||
// throwing out the sandbox can only increase liquidity
|
||||
// yet the limiting is still limiting
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed. r.first: "
|
||||
<< to_string(get<TInAmt>(r.first)) << " maxIn: " << to_string(*maxIn);
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : first step re-executing the "
|
||||
"limiting step failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
else if (!strand[i]->equalOut(r.second, stepOut))
|
||||
{
|
||||
// limiting
|
||||
// Throw out previous results
|
||||
sb.emplace(&baseView);
|
||||
afView.emplace(&baseView);
|
||||
limitingStep = i;
|
||||
|
||||
// re-execute the limiting step
|
||||
stepOut = r.second;
|
||||
r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
|
||||
limitStepOut = r.second;
|
||||
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
// A tiny input amount can cause this step to output
|
||||
// zero. I.e. 10^-80 IOU into an IOU -> XRP offer.
|
||||
JLOG(j.trace()) << "Limiting step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (!strand[i]->equalOut(r.second, stepOut))
|
||||
{
|
||||
// Something is very wrong
|
||||
// throwing out the sandbox can only increase liquidity
|
||||
// yet the limiting is still limiting
|
||||
// LCOV_EXCL_START
|
||||
#ifndef NDEBUG
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed. r.second: " << r.second
|
||||
<< " stepOut: " << stepOut;
|
||||
#else
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed";
|
||||
#endif
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : limiting step re-executing the "
|
||||
"limiting step failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// prev node needs to produce what this node wants to consume
|
||||
stepOut = r.first;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
EitherAmount stepIn(limitStepOut);
|
||||
for (auto i = limitingStep + 1; i < s; ++i)
|
||||
{
|
||||
auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
// A tiny input amount can cause this step to output zero.
|
||||
// I.e. 10^-80 IOU into an IOU -> XRP offer.
|
||||
JLOG(j.trace()) << "Non-limiting step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (!strand[i]->equalIn(r.first, stepIn))
|
||||
{
|
||||
// The limits should already have been found, so executing a
|
||||
// strand forward from the limiting step should not find a
|
||||
// new limit
|
||||
// LCOV_EXCL_START
|
||||
#ifndef NDEBUG
|
||||
JLOG(j.fatal()) << "Re-executed forward pass failed. r.first: " << r.first << " stepIn: " << stepIn;
|
||||
#else
|
||||
JLOG(j.fatal()) << "Re-executed forward pass failed";
|
||||
#endif
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : non-limiting step re-executing the "
|
||||
"forward pass failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
stepIn = r.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto const strandIn = *strand.front()->cachedIn();
|
||||
auto const strandOut = *strand.back()->cachedOut();
|
||||
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
// Check that the strand will execute as intended
|
||||
// Re-executing the strand will change the cached values
|
||||
PaymentSandbox checkSB(&baseView);
|
||||
PaymentSandbox checkAfView(&baseView);
|
||||
EitherAmount stepIn(*strand[0]->cachedIn());
|
||||
for (auto i = 0; i < s; ++i)
|
||||
{
|
||||
bool valid;
|
||||
std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
|
||||
if (!valid)
|
||||
{
|
||||
JLOG(j.warn()) << "Strand re-execute check failed. Step: " << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool const inactive = std::any_of(
|
||||
strand.begin(), strand.end(), [](std::unique_ptr<Step> const& step) { return step->inactive(); });
|
||||
|
||||
return Result(
|
||||
strand, get<TInAmt>(strandIn), get<TOutAmt>(strandOut), std::move(*sb), std::move(ofrsToRm), inactive);
|
||||
}
|
||||
catch (FlowException const&)
|
||||
{
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
template <class TInAmt, class TOutAmt>
|
||||
struct FlowResult
|
||||
{
|
||||
TInAmt in = beast::zero;
|
||||
TOutAmt out = beast::zero;
|
||||
std::optional<PaymentSandbox> sandbox;
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
TER ter = temUNKNOWN;
|
||||
|
||||
FlowResult() = default;
|
||||
|
||||
FlowResult(
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_,
|
||||
boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in(in_), out(out_), sandbox(std::move(sandbox_)), removableOffers(std::move(ofrsToRm)), ter(tesSUCCESS)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm) : removableOffers(std::move(ofrsToRm)), ter(ter_)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult(TER ter_, TInAmt const& in_, TOutAmt const& out_, boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in(in_), out(out_), removableOffers(std::move(ofrsToRm)), ter(ter_)
|
||||
{
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
inline std::optional<Quality>
|
||||
qualityUpperBound(ReadView const& v, Strand const& strand)
|
||||
{
|
||||
Quality q{STAmount::uRateOne};
|
||||
std::optional<Quality> stepQ;
|
||||
DebtDirection dir = DebtDirection::issues;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
|
||||
q = composed_quality(q, *stepQ);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
return q;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
/** Limit remaining out only if one strand and limitQuality is included.
|
||||
* Targets one path payment with AMM where the average quality is linear
|
||||
* and instant quality is quadratic function of output. Calculating quality
|
||||
* function for the whole strand enables figuring out required output
|
||||
* to produce requested strand's limitQuality. Reducing the output,
|
||||
* increases quality of AMM steps, increasing the strand's composite
|
||||
* quality as the result.
|
||||
*/
|
||||
template <typename TOutAmt>
|
||||
inline TOutAmt
|
||||
limitOut(ReadView const& v, Strand const& strand, TOutAmt const& remainingOut, Quality const& limitQuality)
|
||||
{
|
||||
std::optional<QualityFunction> stepQualityFunc;
|
||||
std::optional<QualityFunction> qf;
|
||||
DebtDirection dir = DebtDirection::issues;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir); stepQualityFunc)
|
||||
{
|
||||
if (!qf)
|
||||
qf = stepQualityFunc;
|
||||
else
|
||||
qf->combine(*stepQualityFunc);
|
||||
}
|
||||
else
|
||||
return remainingOut;
|
||||
}
|
||||
|
||||
// QualityFunction is constant
|
||||
if (!qf || qf->isConst())
|
||||
return remainingOut;
|
||||
|
||||
auto const out = [&]() {
|
||||
if (auto const out = qf->outFromAvgQ(limitQuality); !out)
|
||||
return remainingOut;
|
||||
else if constexpr (std::is_same_v<TOutAmt, XRPAmount>)
|
||||
return XRPAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
|
||||
return IOUAmount{*out};
|
||||
else
|
||||
return STAmount{remainingOut.issue(), out->mantissa(), out->exponent()};
|
||||
}();
|
||||
// A tiny difference could be due to the round off
|
||||
if (withinRelativeDistance(out, remainingOut, Number(1, -9)))
|
||||
return remainingOut;
|
||||
return std::min(out, remainingOut);
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
/* Track the non-dry strands
|
||||
|
||||
flow will search the non-dry strands (stored in `cur_`) for the best
|
||||
available liquidity If flow doesn't use all the liquidity of a strand, that
|
||||
strand is added to `next_`. The strands in `next_` are searched after the
|
||||
current best liquidity is used.
|
||||
*/
|
||||
class ActiveStrands
|
||||
{
|
||||
private:
|
||||
// Strands to be explored for liquidity
|
||||
std::vector<Strand const*> cur_;
|
||||
// Strands that may be explored for liquidity on the next iteration
|
||||
std::vector<Strand const*> next_;
|
||||
|
||||
public:
|
||||
ActiveStrands(std::vector<Strand> const& strands)
|
||||
{
|
||||
cur_.reserve(strands.size());
|
||||
next_.reserve(strands.size());
|
||||
for (auto& strand : strands)
|
||||
next_.push_back(&strand);
|
||||
}
|
||||
|
||||
// Start a new iteration in the search for liquidity
|
||||
// Set the current strands to the strands in `next_`
|
||||
void
|
||||
activateNext(ReadView const& v, std::optional<Quality> const& limitQuality)
|
||||
{
|
||||
// add the strands in `next_` to `cur_`, sorted by theoretical quality.
|
||||
// Best quality first.
|
||||
cur_.clear();
|
||||
if (!next_.empty())
|
||||
{
|
||||
std::vector<std::pair<Quality, Strand const*>> strandQualities;
|
||||
strandQualities.reserve(next_.size());
|
||||
if (next_.size() > 1) // no need to sort one strand
|
||||
{
|
||||
for (Strand const* strand : next_)
|
||||
{
|
||||
if (!strand)
|
||||
{
|
||||
// should not happen
|
||||
continue;
|
||||
}
|
||||
if (auto const qual = qualityUpperBound(v, *strand))
|
||||
{
|
||||
if (limitQuality && *qual < *limitQuality)
|
||||
{
|
||||
// If a strand's quality is ever over limitQuality
|
||||
// it is no longer part of the candidate set. Note
|
||||
// that when transfer fees are charged, and an
|
||||
// account goes from redeeming to issuing then
|
||||
// strand quality _can_ increase; However, this is
|
||||
// an unusual corner case.
|
||||
continue;
|
||||
}
|
||||
strandQualities.push_back({*qual, strand});
|
||||
}
|
||||
}
|
||||
// must stable sort for deterministic order across different c++
|
||||
// standard library implementations
|
||||
std::stable_sort(strandQualities.begin(), strandQualities.end(), [](auto const& lhs, auto const& rhs) {
|
||||
// higher qualities first
|
||||
return std::get<Quality>(lhs) > std::get<Quality>(rhs);
|
||||
});
|
||||
next_.clear();
|
||||
next_.reserve(strandQualities.size());
|
||||
for (auto const& sq : strandQualities)
|
||||
{
|
||||
next_.push_back(std::get<Strand const*>(sq));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::swap(cur_, next_);
|
||||
}
|
||||
|
||||
Strand const*
|
||||
get(size_t i) const
|
||||
{
|
||||
if (i >= cur_.size())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::ActiveStrands::get : input out of range");
|
||||
return nullptr;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return cur_[i];
|
||||
}
|
||||
|
||||
void
|
||||
push(Strand const* s)
|
||||
{
|
||||
next_.push_back(s);
|
||||
}
|
||||
|
||||
// Push the strands from index i to the end of cur_ to next_
|
||||
void
|
||||
pushRemainingCurToNext(size_t i)
|
||||
{
|
||||
if (i >= cur_.size())
|
||||
return;
|
||||
next_.insert(next_.end(), std::next(cur_.begin(), i), cur_.end());
|
||||
}
|
||||
|
||||
auto
|
||||
size() const
|
||||
{
|
||||
return cur_.size();
|
||||
}
|
||||
|
||||
void
|
||||
removeIndex(std::size_t i)
|
||||
{
|
||||
if (i >= next_.size())
|
||||
return;
|
||||
next_.erase(next_.begin() + i);
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
Request `out` amount from a collection of strands
|
||||
|
||||
Attempt to fulfill the payment by using liquidity from the strands in order
|
||||
from least expensive to most expensive
|
||||
|
||||
@param baseView Trust lines and balances
|
||||
@param strands Each strand contains the steps of accounts to ripple through
|
||||
and offer books to use
|
||||
@param outReq Amount of output requested from the strand
|
||||
@param partialPayment If true allow less than the full payment
|
||||
@param offerCrossing If true offer crossing, not handling a standard payment
|
||||
@param limitQuality If present, the minimum quality for any strand taken
|
||||
@param sendMaxST If present, the maximum STAmount to send
|
||||
@param j Journal to write journal messages to
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param flowDebugInfo If pointer is non-null, write flow debug info here
|
||||
@return Actual amount in and out from the strands, errors, and payment
|
||||
sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
FlowResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
std::vector<Strand> const& strands,
|
||||
TOutAmt const& outReq,
|
||||
bool partialPayment,
|
||||
OfferCrossing offerCrossing,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<STAmount> const& sendMaxST,
|
||||
beast::Journal j,
|
||||
AMMContext& ammContext,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo = nullptr)
|
||||
{
|
||||
// Used to track the strand that offers the best quality (output/input
|
||||
// ratio)
|
||||
struct BestStrand
|
||||
{
|
||||
TInAmt in;
|
||||
TOutAmt out;
|
||||
PaymentSandbox sb;
|
||||
Strand const& strand;
|
||||
Quality quality;
|
||||
|
||||
BestStrand(
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sb_,
|
||||
Strand const& strand_,
|
||||
Quality const& quality_)
|
||||
: in(in_), out(out_), sb(std::move(sb_)), strand(strand_), quality(quality_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t const maxTries = 1000;
|
||||
std::size_t curTry = 0;
|
||||
std::uint32_t maxOffersToConsider = 1500;
|
||||
std::uint32_t offersConsidered = 0;
|
||||
|
||||
// There is a bug in gcc that incorrectly warns about using uninitialized
|
||||
// values if `remainingIn` is initialized through a copy constructor. We can
|
||||
// get similar warnings for `sendMax` if it is initialized in the most
|
||||
// natural way. Using `make_optional`, allows us to work around this bug.
|
||||
TInAmt const sendMaxInit = sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
|
||||
std::optional<TInAmt> const sendMax =
|
||||
(sendMaxST && sendMaxInit >= beast::zero) ? std::make_optional(sendMaxInit) : std::nullopt;
|
||||
std::optional<TInAmt> remainingIn = !!sendMax ? std::make_optional(sendMaxInit) : std::nullopt;
|
||||
// std::optional<TInAmt> remainingIn{sendMax};
|
||||
|
||||
TOutAmt remainingOut(outReq);
|
||||
|
||||
PaymentSandbox sb(&baseView);
|
||||
|
||||
// non-dry strands
|
||||
ActiveStrands activeStrands(strands);
|
||||
|
||||
// Keeping a running sum of the amount in the order they are processed
|
||||
// will not give the best precision. Keep a collection so they may be summed
|
||||
// from smallest to largest
|
||||
boost::container::flat_multiset<TInAmt> savedIns;
|
||||
savedIns.reserve(maxTries);
|
||||
boost::container::flat_multiset<TOutAmt> savedOuts;
|
||||
savedOuts.reserve(maxTries);
|
||||
|
||||
auto sum = [](auto const& col) {
|
||||
using TResult = std::decay_t<decltype(*col.begin())>;
|
||||
if (col.empty())
|
||||
return TResult{beast::zero};
|
||||
return std::accumulate(col.begin() + 1, col.end(), *col.begin());
|
||||
};
|
||||
|
||||
// These offers only need to be removed if the payment is not
|
||||
// successful
|
||||
boost::container::flat_set<uint256> ofrsToRmOnFail;
|
||||
|
||||
while (remainingOut > beast::zero && (!remainingIn || *remainingIn > beast::zero))
|
||||
{
|
||||
++curTry;
|
||||
if (curTry >= maxTries)
|
||||
{
|
||||
return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
activeStrands.activateNext(sb, limitQuality);
|
||||
|
||||
ammContext.setMultiPath(activeStrands.size() > 1);
|
||||
|
||||
// Limit only if one strand and limitQuality
|
||||
auto const limitRemainingOut = [&]() {
|
||||
if (activeStrands.size() == 1 && limitQuality)
|
||||
if (auto const strand = activeStrands.get(0))
|
||||
return limitOut(sb, *strand, remainingOut, *limitQuality);
|
||||
return remainingOut;
|
||||
}();
|
||||
auto const adjustedRemOut = limitRemainingOut != remainingOut;
|
||||
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
std::optional<BestStrand> best;
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->newLiquidityPass();
|
||||
// Index of strand to mark as inactive (remove from the active list) if
|
||||
// the liquidity is used. This is used for strands that consume too many
|
||||
// offers Constructed as `false,0` to workaround a gcc warning about
|
||||
// uninitialized variables
|
||||
std::optional<std::size_t> markInactiveOnUse;
|
||||
for (size_t strandIndex = 0, sie = activeStrands.size(); strandIndex != sie; ++strandIndex)
|
||||
{
|
||||
Strand const* strand = activeStrands.get(strandIndex);
|
||||
if (!strand)
|
||||
{
|
||||
// should not happen
|
||||
continue;
|
||||
}
|
||||
// Clear AMM liquidity used flag. The flag might still be set if
|
||||
// the previous strand execution failed. It has to be reset
|
||||
// since this strand might not have AMM liquidity.
|
||||
ammContext.clear();
|
||||
if (offerCrossing && limitQuality)
|
||||
{
|
||||
auto const strandQ = qualityUpperBound(sb, *strand);
|
||||
if (!strandQ || *strandQ < *limitQuality)
|
||||
continue;
|
||||
}
|
||||
auto f = flow<TInAmt, TOutAmt>(sb, *strand, remainingIn, limitRemainingOut, j);
|
||||
|
||||
// rm bad offers even if the strand fails
|
||||
SetUnion(ofrsToRm, f.ofrsToRm);
|
||||
|
||||
offersConsidered += f.ofrsUsed;
|
||||
|
||||
if (!f.success || f.out == beast::zero)
|
||||
continue;
|
||||
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
|
||||
|
||||
XRPL_ASSERT(
|
||||
f.out <= remainingOut && f.sandbox && (!remainingIn || f.in <= *remainingIn),
|
||||
"xrpl::flow : remaining constraints");
|
||||
|
||||
Quality const q(f.out, f.in);
|
||||
|
||||
JLOG(j.trace()) << "New flow iter (iter, in, out): " << curTry - 1 << " " << to_string(f.in) << " "
|
||||
<< to_string(f.out);
|
||||
|
||||
// limitOut() finds output to generate exact requested
|
||||
// limitQuality. But the actual limit quality might be slightly
|
||||
// off due to the round off.
|
||||
if (limitQuality && q < *limitQuality &&
|
||||
(!adjustedRemOut || !withinRelativeDistance(q, *limitQuality, Number(1, -7))))
|
||||
{
|
||||
JLOG(j.trace()) << "Path rejected by limitQuality"
|
||||
<< " limit: " << *limitQuality << " path q: " << q;
|
||||
continue;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(!best, "xrpl::flow : best is unset");
|
||||
if (!f.inactive)
|
||||
activeStrands.push(strand);
|
||||
best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
|
||||
activeStrands.pushRemainingCurToNext(strandIndex + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
bool const shouldBreak = !best || offersConsidered >= maxOffersToConsider;
|
||||
|
||||
if (best)
|
||||
{
|
||||
if (markInactiveOnUse)
|
||||
{
|
||||
activeStrands.removeIndex(*markInactiveOnUse);
|
||||
markInactiveOnUse.reset();
|
||||
}
|
||||
savedIns.insert(best->in);
|
||||
savedOuts.insert(best->out);
|
||||
remainingOut = outReq - sum(savedOuts);
|
||||
if (sendMax)
|
||||
remainingIn = *sendMax - sum(savedIns);
|
||||
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->pushPass(EitherAmount(best->in), EitherAmount(best->out), activeStrands.size());
|
||||
|
||||
JLOG(j.trace()) << "Best path: in: " << to_string(best->in) << " out: " << to_string(best->out)
|
||||
<< " remainingOut: " << to_string(remainingOut);
|
||||
|
||||
best->sb.apply(sb);
|
||||
ammContext.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "All strands dry.";
|
||||
}
|
||||
|
||||
best.reset(); // view in best must be destroyed before modifying base
|
||||
// view
|
||||
if (!ofrsToRm.empty())
|
||||
{
|
||||
SetUnion(ofrsToRmOnFail, ofrsToRm);
|
||||
for (auto const& o : ofrsToRm)
|
||||
{
|
||||
if (auto ok = sb.peek(keylet::offer(o)))
|
||||
offerDelete(sb, ok, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBreak)
|
||||
break;
|
||||
}
|
||||
|
||||
auto const actualOut = sum(savedOuts);
|
||||
auto const actualIn = sum(savedIns);
|
||||
|
||||
JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn) << " out: " << to_string(actualOut);
|
||||
|
||||
/* flowCross doesn't handle offer crossing with tfFillOrKill flag correctly.
|
||||
* 1. If tfFillOrKill is set then the owner must receive the full
|
||||
* TakerPays. We reverse pays and gets because during crossing
|
||||
* we are taking, therefore the owner must deliver the full TakerPays and
|
||||
* the entire TakerGets doesn't have to be spent.
|
||||
* Pre-fixFillOrKill amendment code fails if the entire TakerGets
|
||||
* is not spent. fixFillOrKill addresses this issue.
|
||||
* 2. If tfSell is also set then the owner must spend the entire TakerGets
|
||||
* even if it means obtaining more than TakerPays. Since the pays and gets
|
||||
* are reversed, the owner must send the entire TakerGets.
|
||||
*/
|
||||
bool const fillOrKillEnabled = baseView.rules().enabled(fixFillOrKill);
|
||||
|
||||
if (actualOut != outReq)
|
||||
{
|
||||
if (actualOut > outReq)
|
||||
{
|
||||
// Rounding in the payment engine is causing this assert to
|
||||
// sometimes fire with "dust" amounts. This is causing issues when
|
||||
// running debug builds of rippled. While this issue still needs to
|
||||
// be resolved, the assert is causing more harm than good at this
|
||||
// point.
|
||||
// UNREACHABLE("xrpl::flow : rounding error");
|
||||
|
||||
return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
if (!partialPayment)
|
||||
{
|
||||
// If we're offerCrossing a !partialPayment, then we're
|
||||
// handling tfFillOrKill.
|
||||
// Pre-fixFillOrKill amendment:
|
||||
// That case is handled below; not here.
|
||||
// fixFillOrKill amendment:
|
||||
// That case is handled here if tfSell is also not set; i.e,
|
||||
// case 1.
|
||||
if (!offerCrossing || (fillOrKillEnabled && offerCrossing != OfferCrossing::sell))
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
else if (actualOut == beast::zero)
|
||||
{
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
}
|
||||
if (offerCrossing && (!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::sell)))
|
||||
{
|
||||
// If we're offer crossing and partialPayment is *not* true, then
|
||||
// we're handling a FillOrKill offer. In this case remainingIn must
|
||||
// be zero (all funds must be consumed) or else we kill the offer.
|
||||
// Pre-fixFillOrKill amendment:
|
||||
// Handles both cases 1. and 2.
|
||||
// fixFillOrKill amendment:
|
||||
// Handles 2. 1. is handled above and falls through for tfSell.
|
||||
XRPL_ASSERT(remainingIn, "xrpl::flow : nonzero remainingIn");
|
||||
if (remainingIn && *remainingIn != beast::zero)
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
67
include/xrpl/tx/transactors/AMM/AMMBid.h
Normal file
67
include/xrpl/tx/transactors/AMM/AMMBid.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMBid implements AMM bid Transactor.
|
||||
* This is a mechanism for an AMM instance to auction-off
|
||||
* the trading advantages to users (arbitrageurs) at a discounted
|
||||
* TradingFee for a 24 hour slot. Any account that owns corresponding
|
||||
* LPTokens can bid for the auction slot of that AMM instance.
|
||||
* Part of the proceeds from the auction, i.e. LPTokens are refunded
|
||||
* to the current slot-holder computed on a pro rata basis.
|
||||
* Remaining part of the proceeds - in the units of LPTokens- is burnt,
|
||||
* thus effectively increasing the LPs shares.
|
||||
* Total slot time of 24 hours is divided into 20 equal intervals.
|
||||
* The auction slot can be in any of the following states at any time:
|
||||
* - Empty - no account currently holds the slot.
|
||||
* - Occupied - an account owns the slot with at least 5% of the remaining
|
||||
* slot time (in one of 1-19 intervals).
|
||||
* - Tailing - an account owns the slot with less than 5% of the remaining time.
|
||||
* The slot-holder owns the slot privileges when in state Occupied or Tailing.
|
||||
* If x is the fraction of used slot time for the current slot holder
|
||||
* and X is the price at which the slot can be bought specified in LPTokens
|
||||
* then: The minimum bid price for the slot in first interval is
|
||||
* f(x) = X * 1.05 + min_slot_price
|
||||
* The bid price of slot any time is
|
||||
* f(x) = X * 1.05 * (1 - x^60) + min_slot_price, where min_slot_price
|
||||
* is a constant fraction of the total LPTokens.
|
||||
* The revenue from a successful bid is split between the current slot-holder
|
||||
* and the pool. The current slot holder is always refunded the remaining slot
|
||||
* value f(x) = (1 - x) * X.
|
||||
* The remaining LPTokens are burnt.
|
||||
* The auction information is maintained in AuctionSlot of ltAMM object.
|
||||
* AuctionSlot contains:
|
||||
* Account - account id, which owns the slot.
|
||||
* Expiration - slot expiration time
|
||||
* DiscountedFee - trading fee charged to the account, default is 0.
|
||||
* Price - price paid for the slot in LPTokens.
|
||||
* AuthAccounts - up to four accounts authorized to trade at
|
||||
* the discounted fee.
|
||||
* @see [XLS30d:Continuous Auction
|
||||
* Mechanism](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMBid : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMBid(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
56
include/xrpl/tx/transactors/AMM/AMMClawback.h
Normal file
56
include/xrpl/tx/transactors/AMM/AMMClawback.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
class Sandbox;
|
||||
class AMMClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
TER
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Withdraw both assets by providing maximum amount of asset1,
|
||||
* asset2's amount will be calculated according to the current proportion.
|
||||
* Since it is two-asset withdrawal, tfee is omitted.
|
||||
* @param view
|
||||
* @param ammAccount current AMM account
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @return
|
||||
*/
|
||||
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
equalWithdrawMatchingOneAmount(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& holder,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& holdLPtokens,
|
||||
STAmount const& amount);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
96
include/xrpl/tx/transactors/AMM/AMMContext.h
Normal file
96
include/xrpl/tx/transactors/AMM/AMMContext.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Maintains AMM info per overall payment engine execution and
|
||||
* individual iteration.
|
||||
* Only one instance of this class is created in Flow.cpp::flow().
|
||||
* The reference is percolated through calls to AMMLiquidity class,
|
||||
* which handles AMM offer generation.
|
||||
*/
|
||||
class AMMContext
|
||||
{
|
||||
public:
|
||||
// Restrict number of AMM offers. If this restriction is removed
|
||||
// then need to restrict in some other way because AMM offers are
|
||||
// not counted in the BookStep offer counter.
|
||||
constexpr static std::uint8_t MaxIterations = 30;
|
||||
|
||||
private:
|
||||
// Tx account owner is required to get the AMM trading fee in BookStep
|
||||
AccountID account_;
|
||||
// true if payment has multiple paths
|
||||
bool multiPath_{false};
|
||||
// Is true if AMM offer is consumed during a payment engine iteration.
|
||||
bool ammUsed_{false};
|
||||
// Counter of payment engine iterations with consumed AMM
|
||||
std::uint16_t ammIters_{0};
|
||||
|
||||
public:
|
||||
AMMContext(AccountID const& account, bool multiPath) : account_(account), multiPath_(multiPath)
|
||||
{
|
||||
}
|
||||
~AMMContext() = default;
|
||||
AMMContext(AMMContext const&) = delete;
|
||||
AMMContext&
|
||||
operator=(AMMContext const&) = delete;
|
||||
|
||||
bool
|
||||
multiPath() const
|
||||
{
|
||||
return multiPath_;
|
||||
}
|
||||
|
||||
void
|
||||
setMultiPath(bool fs)
|
||||
{
|
||||
multiPath_ = fs;
|
||||
}
|
||||
|
||||
void
|
||||
setAMMUsed()
|
||||
{
|
||||
ammUsed_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
update()
|
||||
{
|
||||
if (ammUsed_)
|
||||
++ammIters_;
|
||||
ammUsed_ = false;
|
||||
}
|
||||
|
||||
bool
|
||||
maxItersReached() const
|
||||
{
|
||||
return ammIters_ >= MaxIterations;
|
||||
}
|
||||
|
||||
std::uint16_t
|
||||
curIters() const
|
||||
{
|
||||
return ammIters_;
|
||||
}
|
||||
|
||||
AccountID
|
||||
account() const
|
||||
{
|
||||
return account_;
|
||||
}
|
||||
|
||||
/** Strand execution may fail. Reset the flag at the start
|
||||
* of each payment engine iteration.
|
||||
*/
|
||||
void
|
||||
clear()
|
||||
{
|
||||
ammUsed_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
63
include/xrpl/tx/transactors/AMM/AMMCreate.h
Normal file
63
include/xrpl/tx/transactors/AMM/AMMCreate.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMCreate implements Automatic Market Maker(AMM) creation Transactor.
|
||||
* It creates a new AMM instance with two tokens. Any trader, or Liquidity
|
||||
* Provider (LP), can create the AMM instance and receive in return shares
|
||||
* of the AMM pool in the form of LPTokens. The number of tokens that LP gets
|
||||
* are determined by LPTokens = sqrt(A * B), where A and B is the current
|
||||
* composition of the AMM pool. LP can add (AMMDeposit) or withdraw
|
||||
* (AMMWithdraw) tokens from AMM and
|
||||
* AMM can be used transparently in the payment or offer crossing transactions.
|
||||
* Trading fee is charged to the traders for the trades executed against
|
||||
* AMM instance. The fee is added to the AMM pool and distributed to the LPs
|
||||
* in proportion to the LPTokens upon liquidity removal. The fee can be voted
|
||||
* on by LP's (AMMVote). LP's can continuously bid (AMMBid) for the 24 hour
|
||||
* auction slot, which enables LP's to trade at zero trading fee.
|
||||
* AMM instance creates AccountRoot object with disabled master key
|
||||
* for book-keeping of XRP balance if one of the tokens
|
||||
* is XRP, a trustline for each IOU token, a trustline to keep track
|
||||
* of LPTokens, and ltAMM ledger object. AccountRoot ID is generated
|
||||
* internally from the parent's hash. ltAMM's object ID is
|
||||
* hash{token1.currency, token1.issuer, token2.currency, token2.issuer}, where
|
||||
* issue1 < issue2. ltAMM object provides mapping from the hash to AccountRoot
|
||||
* ID and contains: AMMAccount - AMM AccountRoot ID. TradingFee - AMM voted
|
||||
* TradingFee. VoteSlots - Array of VoteEntry, contains fee vote information.
|
||||
* AuctionSlot - Auction slot, contains discounted fee bid information.
|
||||
* LPTokenBalance - LPTokens outstanding balance.
|
||||
* AMMToken - currency/issuer information for AMM tokens.
|
||||
* AMMDeposit, AMMWithdraw, AMMVote, and AMMBid transactions use the hash
|
||||
* to access AMM instance.
|
||||
* @see [XLS30d:Creating AMM instance on
|
||||
* XRPL](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Attempt to create the AMM instance. */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/AMM/AMMDelete.h
Normal file
35
include/xrpl/tx/transactors/AMM/AMMDelete.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMDelete implements AMM delete transactor. This is a mechanism to
|
||||
* delete AMM in an empty state when the number of LP tokens is 0.
|
||||
* AMMDelete deletes the trustlines up to configured maximum. If all
|
||||
* trustlines are deleted then AMM ltAMM and root account are deleted.
|
||||
* Otherwise AMMDelete should be called again.
|
||||
*/
|
||||
class AMMDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
231
include/xrpl/tx/transactors/AMM/AMMDeposit.h
Normal file
231
include/xrpl/tx/transactors/AMM/AMMDeposit.h
Normal file
@@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Sandbox;
|
||||
|
||||
/** AMMDeposit implements AMM deposit Transactor.
|
||||
* The deposit transaction is used to add liquidity to the AMM instance pool,
|
||||
* thus obtaining some share of the instance's pools in the form of LPTokens.
|
||||
* If the trader deposits proportional values of both assets without changing
|
||||
* their relative price, then no trading fee is charged on the transaction.
|
||||
* The trader can specify different combination of the fields in the deposit.
|
||||
* LPTokens - transaction assumes proportional deposit of pools assets in
|
||||
* exchange for the specified amount of LPTokens of the AMM instance.
|
||||
* Asset1In - transaction assumes single asset deposit of the amount of asset
|
||||
* specified by Asset1In. This is essentially a swap and an equal asset
|
||||
* deposit.
|
||||
* Asset1In and Asset2In - transaction assumes proportional deposit of pool
|
||||
* assets with the constraints on the maximum amount of each asset that
|
||||
* the trader is willing to deposit.
|
||||
* Asset1In and LPTokens - transaction assumes that a single asset asset1
|
||||
* is deposited to obtain some share of the AMM instance's pools
|
||||
* represented by amount of LPTokens.
|
||||
* Asset1In and EPrice - transaction assumes single asset deposit with
|
||||
* the following two constraints:
|
||||
* a. amount of asset1 if specified (not 0) in Asset1In specifies the
|
||||
* maximum amount of asset1 that the trader is willing to deposit b. The
|
||||
* effective-price of the LPTokens traded out does not exceed the specified
|
||||
* EPrice. Following updates after a successful AMMDeposit transaction: The
|
||||
* deposited asset, if XRP, is transferred from the account that initiated the
|
||||
* transaction to the AMM instance account, thus changing the Balance field of
|
||||
* each account. The deposited asset, if tokens, are balanced between the AMM
|
||||
* account and the issuer account trustline. The LPTokens are issued by the AMM
|
||||
* instance account to the account that initiated the transaction and a new
|
||||
* trustline is created, if there does not exist one. The pool composition is
|
||||
* updated.
|
||||
* @see [XLS30d:AMMDeposit
|
||||
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMDeposit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMDeposit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Deposit requested assets and token amount into LP account.
|
||||
* Return new total LPToken balance.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amountDeposit
|
||||
* @param amount2Deposit
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param depositMin minimum accepted amount deposit
|
||||
* @param deposit2Min minimum accepted amount2 deposit
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
deposit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountDeposit,
|
||||
std::optional<STAmount> const& amount2Deposit,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::optional<STAmount> const& depositMin,
|
||||
std::optional<STAmount> const& deposit2Min,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal asset deposit (LPTokens) for the specified share of
|
||||
* the AMM instance pools. The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param depositMin minimum accepted amount deposit
|
||||
* @param deposit2Min minimum accepted amount2 deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositTokens(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::optional<STAmount> const& depositMin,
|
||||
std::optional<STAmount> const& deposit2Min,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal asset deposit (Asset1In, Asset2In) with the constraint on
|
||||
* the maximum amount of both assets that the trader is willing to deposit.
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount maximum asset1 deposit amount
|
||||
* @param amount2 maximum asset2 deposit amount
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositLimit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In) by the amount.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDeposit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In, LPTokens) by the tokens.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount max asset1 to deposit
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDepositTokens(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In, EPrice) with two constraints.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param ePrice maximum effective price
|
||||
* @param tfee
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDepositEPrice(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& ePrice,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal deposit in empty AMM state (LP tokens balance is 0)
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param amount2 requested asset2 deposit amount
|
||||
* @param tfee
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositInEmptyState(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
Issue const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
689
include/xrpl/tx/transactors/AMM/AMMHelpers.h
Normal file
689
include/xrpl/tx/transactors/AMM/AMMHelpers.h
Normal file
@@ -0,0 +1,689 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
Number
|
||||
reduceOffer(auto const& amount)
|
||||
{
|
||||
static Number const reducedOfferPct(9999, -4);
|
||||
|
||||
// Make sure the result is always less than amount or zero.
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
return amount * reducedOfferPct;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
enum class IsDeposit : bool { No = false, Yes = true };
|
||||
|
||||
/** Calculate LP Tokens given AMM pool reserves.
|
||||
* @param asset1 AMM one side of the pool reserve
|
||||
* @param asset2 AMM another side of the pool reserve
|
||||
* @return LP Tokens as IOU
|
||||
*/
|
||||
STAmount
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Issue const& lptIssue);
|
||||
|
||||
/** Calculate LP Tokens given asset's deposit amount.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param asset1Deposit requested asset1 deposit amount
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return tokens
|
||||
*/
|
||||
STAmount
|
||||
lpTokensOut(
|
||||
STAmount const& asset1Balance,
|
||||
STAmount const& asset1Deposit,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Calculate asset deposit given LP Tokens.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param lpTokens LP Tokens
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
STAmount
|
||||
ammAssetIn(STAmount const& asset1Balance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee);
|
||||
|
||||
/** Calculate LP Tokens given asset's withdraw amount. Return 0
|
||||
* if can't calculate.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param asset1Withdraw requested asset1 withdraw amount
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return tokens out amount
|
||||
*/
|
||||
STAmount
|
||||
lpTokensIn(
|
||||
STAmount const& asset1Balance,
|
||||
STAmount const& asset1Withdraw,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Calculate asset withdrawal by tokens
|
||||
* @param assetBalance balance of the asset being withdrawn
|
||||
* @param lptAMMBalance total AMM Tokens balance
|
||||
* @param lpTokens LP Tokens balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return calculated asset amount
|
||||
*/
|
||||
STAmount
|
||||
ammAssetOut(STAmount const& assetBalance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee);
|
||||
|
||||
/** Check if the relative distance between the qualities
|
||||
* is within the requested distance.
|
||||
* @param calcQuality calculated quality
|
||||
* @param reqQuality requested quality
|
||||
* @param dist requested relative distance
|
||||
* @return true if within dist, false otherwise
|
||||
*/
|
||||
inline bool
|
||||
withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Number const& dist)
|
||||
{
|
||||
if (calcQuality == reqQuality)
|
||||
return true;
|
||||
auto const [min, max] = std::minmax(calcQuality, reqQuality);
|
||||
// Relative distance is (max - min)/max. Can't use basic operations
|
||||
// on Quality. Have to use Quality::rate() instead, which
|
||||
// is inverse of quality: (1/max.rate - 1/min.rate)/(1/max.rate)
|
||||
return ((min.rate() - max.rate()) / min.rate()) < dist;
|
||||
}
|
||||
|
||||
/** Check if the relative distance between the amounts
|
||||
* is within the requested distance.
|
||||
* @param calc calculated amount
|
||||
* @param req requested amount
|
||||
* @param dist requested relative distance
|
||||
* @return true if within dist, false otherwise
|
||||
*/
|
||||
// clang-format off
|
||||
template <typename Amt>
|
||||
requires(
|
||||
std::is_same_v<Amt, STAmount> || std::is_same_v<Amt, IOUAmount> ||
|
||||
std::is_same_v<Amt, XRPAmount> || std::is_same_v<Amt, Number>)
|
||||
bool
|
||||
withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist)
|
||||
{
|
||||
if (calc == req)
|
||||
return true;
|
||||
auto const [min, max] = std::minmax(calc, req);
|
||||
return ((max - min) / max) < dist;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
/** Solve quadratic equation to find takerGets or takerPays. Round
|
||||
* to minimize the amount in order to maximize the quality.
|
||||
*/
|
||||
std::optional<Number>
|
||||
solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c);
|
||||
|
||||
/** Generate AMM offer starting with takerGets when AMM pool
|
||||
* from the payment perspective is IOU(in)/XRP(out)
|
||||
* Equations:
|
||||
* Spot Price Quality after the offer is consumed:
|
||||
* Qsp = (O - o) / (I + i) -- equation (1)
|
||||
* where O is poolPays, I is poolGets, o is takerGets, i is takerPays
|
||||
* Swap out:
|
||||
* i = (I * o) / (O - o) * f -- equation (2)
|
||||
* where f is (1 - tfee/100000), tfee is in basis points
|
||||
* Effective price targetQuality:
|
||||
* Qep = o / i -- equation (3)
|
||||
* There are two scenarios to consider
|
||||
* A) Qsp = Qep. Substitute i in (1) with (2) and solve for o
|
||||
* and Qsp = targetQuality(Qt):
|
||||
* o**2 + o * (I * Qt * (1 - 1 / f) - 2 * O) + O**2 - Qt * I * O = 0
|
||||
* B) Qep = Qsp. Substitute i in (3) with (2) and solve for o
|
||||
* and Qep = targetQuality(Qt):
|
||||
* o = O - I * Qt / f
|
||||
* Since the scenario is not known a priori, both A and B are solved and
|
||||
* the lowest value of o is takerGets. takerPays is calculated with
|
||||
* swap out eq (2). If o is less or equal to 0 then the offer can't
|
||||
* be generated.
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
getAMMOfferStartWithTakerGets(TAmounts<TIn, TOut> const& pool, Quality const& targetQuality, std::uint16_t const& tfee)
|
||||
{
|
||||
if (targetQuality.rate() == beast::zero)
|
||||
return std::nullopt;
|
||||
|
||||
NumberRoundModeGuard mg(Number::to_nearest);
|
||||
auto const f = feeMult(tfee);
|
||||
auto const a = 1;
|
||||
auto const b = pool.in * (1 - 1 / f) / targetQuality.rate() - 2 * pool.out;
|
||||
auto const c = pool.out * pool.out - (pool.in * pool.out) / targetQuality.rate();
|
||||
|
||||
auto nTakerGets = solveQuadraticEqSmallest(a, b, c);
|
||||
if (!nTakerGets || *nTakerGets <= 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto const nTakerGetsConstraint = pool.out - pool.in / (targetQuality.rate() * f);
|
||||
if (nTakerGetsConstraint <= 0)
|
||||
return std::nullopt;
|
||||
|
||||
// Select the smallest to maximize the quality
|
||||
if (nTakerGetsConstraint < *nTakerGets)
|
||||
nTakerGets = nTakerGetsConstraint;
|
||||
|
||||
auto getAmounts = [&pool, &tfee](Number const& nTakerGetsProposed) {
|
||||
// Round downward to minimize the offer and to maximize the quality.
|
||||
// This has the most impact when takerGets is XRP.
|
||||
auto const takerGets = toAmount<TOut>(getIssue(pool.out), nTakerGetsProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{swapAssetOut(pool, takerGets, tfee), takerGets};
|
||||
};
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.out));
|
||||
else
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** Generate AMM offer starting with takerPays when AMM pool
|
||||
* from the payment perspective is XRP(in)/IOU(out) or IOU(in)/IOU(out).
|
||||
* Equations:
|
||||
* Spot Price Quality after the offer is consumed:
|
||||
* Qsp = (O - o) / (I + i) -- equation (1)
|
||||
* where O is poolPays, I is poolGets, o is takerGets, i is takerPays
|
||||
* Swap in:
|
||||
* o = (O * i * f) / (I + i * f) -- equation (2)
|
||||
* where f is (1 - tfee/100000), tfee is in basis points
|
||||
* Effective price quality:
|
||||
* Qep = o / i -- equation (3)
|
||||
* There are two scenarios to consider
|
||||
* A) Qsp = Qep. Substitute o in (1) with (2) and solve for i
|
||||
* and Qsp = targetQuality(Qt):
|
||||
* i**2 * f + i * I * (1 + f) + I**2 - I * O / Qt = 0
|
||||
* B) Qep = Qsp. Substitute i in (3) with (2) and solve for i
|
||||
* and Qep = targetQuality(Qt):
|
||||
* i = O / Qt - I / f
|
||||
* Since the scenario is not known a priori, both A and B are solved and
|
||||
* the lowest value of i is takerPays. takerGets is calculated with
|
||||
* swap in eq (2). If i is less or equal to 0 then the offer can't
|
||||
* be generated.
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
getAMMOfferStartWithTakerPays(TAmounts<TIn, TOut> const& pool, Quality const& targetQuality, std::uint16_t tfee)
|
||||
{
|
||||
if (targetQuality.rate() == beast::zero)
|
||||
return std::nullopt;
|
||||
|
||||
NumberRoundModeGuard mg(Number::to_nearest);
|
||||
auto const f = feeMult(tfee);
|
||||
auto const& a = f;
|
||||
auto const b = pool.in * (1 + f);
|
||||
auto const c = pool.in * pool.in - pool.in * pool.out * targetQuality.rate();
|
||||
|
||||
auto nTakerPays = solveQuadraticEqSmallest(a, b, c);
|
||||
if (!nTakerPays || nTakerPays <= 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto const nTakerPaysConstraint = pool.out * targetQuality.rate() - pool.in / f;
|
||||
if (nTakerPaysConstraint <= 0)
|
||||
return std::nullopt;
|
||||
|
||||
// Select the smallest to maximize the quality
|
||||
if (nTakerPaysConstraint < *nTakerPays)
|
||||
nTakerPays = nTakerPaysConstraint;
|
||||
|
||||
auto getAmounts = [&pool, &tfee](Number const& nTakerPaysProposed) {
|
||||
// Round downward to minimize the offer and to maximize the quality.
|
||||
// This has the most impact when takerPays is XRP.
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPaysProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
};
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.in));
|
||||
else
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** Generate AMM offer so that either updated Spot Price Quality (SPQ)
|
||||
* is equal to LOB quality (in this case AMM offer quality is
|
||||
* better than LOB quality) or AMM offer is equal to LOB quality
|
||||
* (in this case SPQ is better than LOB quality).
|
||||
* Pre-amendment code calculates takerPays first. If takerGets is XRP,
|
||||
* it is rounded down, which results in worse offer quality than
|
||||
* LOB quality, and the offer might fail to generate.
|
||||
* Post-amendment code calculates the XRP offer side first. The result
|
||||
* is rounded down, which makes the offer quality better.
|
||||
* It might not be possible to match either SPQ or AMM offer to LOB
|
||||
* quality. This generally happens at higher fees.
|
||||
* @param pool AMM pool balances
|
||||
* @param quality requested quality
|
||||
* @param tfee trading fee in basis points
|
||||
* @return seated in/out amounts if the quality can be changed
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
changeSpotPriceQuality(
|
||||
TAmounts<TIn, TOut> const& pool,
|
||||
Quality const& quality,
|
||||
std::uint16_t tfee,
|
||||
Rules const& rules,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_1))
|
||||
{
|
||||
// Finds takerPays (i) and takerGets (o) such that given pool
|
||||
// composition poolGets(I) and poolPays(O): (O - o) / (I + i) = quality.
|
||||
// Where takerGets is calculated as the swapAssetIn (see below).
|
||||
// The above equation produces the quadratic equation:
|
||||
// i^2*(1-fee) + i*I*(2-fee) + I^2 - I*O/quality,
|
||||
// which is solved for i, and o is found with swapAssetIn().
|
||||
auto const f = feeMult(tfee); // 1 - fee
|
||||
auto const& a = f;
|
||||
auto const b = pool.in * (1 + f);
|
||||
Number const c = pool.in * pool.in - pool.in * pool.out * quality.rate();
|
||||
if (auto const res = b * b - 4 * a * c; res < 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
else if (auto const nTakerPaysPropose = (-b + root2(res)) / (2 * a); nTakerPaysPropose > 0)
|
||||
{
|
||||
auto const nTakerPays = [&]() {
|
||||
// The fee might make the AMM offer quality less than CLOB
|
||||
// quality. Therefore, AMM offer has to satisfy this constraint:
|
||||
// o / i >= q. Substituting o with swapAssetIn() gives: i <= O /
|
||||
// q - I / (1 - fee).
|
||||
auto const nTakerPaysConstraint = pool.out * quality.rate() - pool.in / f;
|
||||
if (nTakerPaysPropose > nTakerPaysConstraint)
|
||||
return nTakerPaysConstraint;
|
||||
return nTakerPaysPropose;
|
||||
}();
|
||||
if (nTakerPays <= 0)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
|
||||
// should not fail
|
||||
if (auto const amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
Quality{amounts} < quality && !withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
|
||||
{
|
||||
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out)
|
||||
<< " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
|
||||
<< to_string(amounts.out);
|
||||
Throw<std::runtime_error>("changeSpotPriceQuality failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
|
||||
<< to_string(amounts.out);
|
||||
return amounts;
|
||||
}
|
||||
}
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " " << to_string(pool.out)
|
||||
<< " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Generate the offer starting with XRP side. Return seated offer amounts
|
||||
// if the offer can be generated, otherwise nullopt.
|
||||
auto const amounts = [&]() {
|
||||
if (isXRP(getIssue(pool.out)))
|
||||
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
|
||||
return getAMMOfferStartWithTakerPays(pool, quality, tfee);
|
||||
}();
|
||||
if (!amounts)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< quality << " " << tfee << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (Quality{*amounts} < quality)
|
||||
{
|
||||
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out);
|
||||
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** AMM pool invariant - the product (A * B) after swap in/out has to remain
|
||||
* at least the same: (A + in) * (B - out) >= A * B
|
||||
* XRP round-off may result in a smaller product after swap in/out.
|
||||
* To address this:
|
||||
* - if on swapIn the out is XRP then the amount is round-off
|
||||
* downward, making the product slightly larger since out
|
||||
* value is reduced.
|
||||
* - if on swapOut the in is XRP then the amount is round-off
|
||||
* upward, making the product slightly larger since in
|
||||
* value is increased.
|
||||
*/
|
||||
|
||||
/** Swap assetIn into the pool and swap out a proportional amount
|
||||
* of the other asset. Implements AMM Swap in.
|
||||
* @see [XLS30d:AMM
|
||||
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
* @param pool current AMM pool balances
|
||||
* @param assetIn amount to swap in
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
TOut
|
||||
swapAssetIn(TAmounts<TIn, TOut> const& pool, TIn const& assetIn, std::uint16_t tfee)
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixAMMv1_1))
|
||||
{
|
||||
// set rounding to always favor the amm. Clip to zero.
|
||||
// calculate:
|
||||
// pool.out -
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// and explicitly set the rounding modes
|
||||
// Favoring the amm means we should:
|
||||
// minimize:
|
||||
// pool.out -
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// maximize:
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// (pool.in * pool.out)
|
||||
// minimize:
|
||||
// (pool.in + assetIn * feeMult(tfee)),
|
||||
// minimize:
|
||||
// assetIn * feeMult(tfee)
|
||||
// feeMult is: (1-fee), fee is tfee/100000
|
||||
// minimize:
|
||||
// 1-fee
|
||||
// maximize:
|
||||
// fee
|
||||
saveNumberRoundMode _{Number::getround()};
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const numerator = pool.in * pool.out;
|
||||
auto const fee = getFee(tfee);
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const denom = pool.in + assetIn * (1 - fee);
|
||||
|
||||
if (denom.signum() <= 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const ratio = numerator / denom;
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const swapOut = pool.out - ratio;
|
||||
|
||||
if (swapOut.signum() < 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
|
||||
return toAmount<TOut>(getIssue(pool.out), swapOut, Number::downward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TOut>(
|
||||
getIssue(pool.out),
|
||||
pool.out - (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
Number::downward);
|
||||
}
|
||||
}
|
||||
|
||||
/** Swap assetOut out of the pool and swap in a proportional amount
|
||||
* of the other asset. Implements AMM Swap out.
|
||||
* @see [XLS30d:AMM
|
||||
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
* @param pool current AMM pool balances
|
||||
* @param assetOut amount to swap out
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
TIn
|
||||
swapAssetOut(TAmounts<TIn, TOut> const& pool, TOut const& assetOut, std::uint16_t tfee)
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixAMMv1_1))
|
||||
{
|
||||
// set rounding to always favor the amm. Clip to zero.
|
||||
// calculate:
|
||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
||||
// (1-tfee/100000)
|
||||
// maximize:
|
||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in)
|
||||
// maximize:
|
||||
// (pool.in * pool.out) / (pool.out - assetOut)
|
||||
// maximize:
|
||||
// (pool.in * pool.out)
|
||||
// minimize
|
||||
// (pool.out - assetOut)
|
||||
// minimize:
|
||||
// (1-tfee/100000)
|
||||
// maximize:
|
||||
// tfee/100000
|
||||
|
||||
saveNumberRoundMode _{Number::getround()};
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const numerator = pool.in * pool.out;
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const denom = pool.out - assetOut;
|
||||
if (denom.signum() <= 0)
|
||||
{
|
||||
return toMaxAmount<TIn>(getIssue(pool.in));
|
||||
}
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const ratio = numerator / denom;
|
||||
auto const numerator2 = ratio - pool.in;
|
||||
auto const fee = getFee(tfee);
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const feeMult = 1 - fee;
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const swapIn = numerator2 / feeMult;
|
||||
if (swapIn.signum() < 0)
|
||||
return toAmount<TIn>(getIssue(pool.in), 0);
|
||||
|
||||
return toAmount<TIn>(getIssue(pool.in), swapIn, Number::upward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TIn>(
|
||||
getIssue(pool.in),
|
||||
((pool.in * pool.out) / (pool.out - assetOut) - pool.in) / feeMult(tfee),
|
||||
Number::upward);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return square of n.
|
||||
*/
|
||||
Number
|
||||
square(Number const& n);
|
||||
|
||||
/** Adjust LP tokens to deposit/withdraw.
|
||||
* Amount type keeps 16 digits. Maintaining the LP balance by adding
|
||||
* deposited tokens or subtracting withdrawn LP tokens from LP balance
|
||||
* results in losing precision in LP balance. I.e. the resulting LP balance
|
||||
* is less than the actual sum of LP tokens. To adjust for this, subtract
|
||||
* old tokens balance from the new one for deposit or vice versa for
|
||||
* withdraw to cancel out the precision loss.
|
||||
* @param lptAMMBalance LPT AMM Balance
|
||||
* @param lpTokens LP tokens to deposit or withdraw
|
||||
* @param isDeposit Yes if deposit, No if withdraw
|
||||
*/
|
||||
STAmount
|
||||
adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposit isDeposit);
|
||||
|
||||
/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if
|
||||
* the adjusted LP tokens are less than the provided LP tokens.
|
||||
* @param amountBalance asset1 pool balance
|
||||
* @param amount asset1 to deposit or withdraw
|
||||
* @param amount2 asset2 to deposit or withdraw
|
||||
* @param lptAMMBalance LPT AMM Balance
|
||||
* @param lpTokens LP tokens to deposit or withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param isDeposit Yes if deposit, No if withdraw
|
||||
* @return
|
||||
*/
|
||||
std::tuple<STAmount, std::optional<STAmount>, STAmount>
|
||||
adjustAmountsByLPTokens(
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
std::optional<STAmount> const& amount2,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
std::uint16_t tfee,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/** Positive solution for quadratic equation:
|
||||
* x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
|
||||
*/
|
||||
Number
|
||||
solveQuadraticEq(Number const& a, Number const& b, Number const& c);
|
||||
|
||||
STAmount
|
||||
multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm);
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline Number::rounding_mode
|
||||
getLPTokenRounding(IsDeposit isDeposit)
|
||||
{
|
||||
// Minimize on deposit, maximize on withdraw to ensure
|
||||
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
|
||||
return isDeposit == IsDeposit::Yes ? Number::downward : Number::upward;
|
||||
}
|
||||
|
||||
inline Number::rounding_mode
|
||||
getAssetRounding(IsDeposit isDeposit)
|
||||
{
|
||||
// Maximize on deposit, minimize on withdraw to ensure
|
||||
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
|
||||
return isDeposit == IsDeposit::Yes ? Number::upward : Number::downward;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/** Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas
|
||||
* calculate the amount as a fractional value of the pool balance. The rounding
|
||||
* takes place on the last step of multiplying the balance by the fraction if
|
||||
* AMMv1_3 is enabled.
|
||||
*/
|
||||
template <typename A>
|
||||
STAmount
|
||||
getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDeposit isDeposit)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
{
|
||||
if constexpr (std::is_same_v<A, STAmount>)
|
||||
return multiply(balance, frac, balance.issue());
|
||||
else
|
||||
return toSTAmount(balance.issue(), balance * frac);
|
||||
}
|
||||
auto const rm = detail::getAssetRounding(isDeposit);
|
||||
return multiply(balance, frac, rm);
|
||||
}
|
||||
|
||||
/** Round AMM single deposit/withdrawal amount.
|
||||
* The lambda's are used to delay evaluation until the function
|
||||
* is executed so that the calculation is not done twice. noRoundCb() is
|
||||
* called if AMMv1_3 is disabled. Otherwise, the rounding is set and
|
||||
* the amount is:
|
||||
* isDeposit is Yes - the balance multiplied by productCb()
|
||||
* isDeposit is No - the result of productCb(). The rounding is
|
||||
* the same for all calculations in productCb()
|
||||
*/
|
||||
STAmount
|
||||
getRoundedAsset(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
STAmount const& balance,
|
||||
std::function<Number()>&& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas
|
||||
* calculate the lptokens as a fractional value of the AMM total lptokens.
|
||||
* The rounding takes place on the last step of multiplying the balance by
|
||||
* the fraction if AMMv1_3 is enabled. The tokens are then
|
||||
* adjusted to factor in the loss in precision (we only keep 16 significant
|
||||
* digits) when adding the lptokens to the balance.
|
||||
*/
|
||||
STAmount
|
||||
getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& frac, IsDeposit isDeposit);
|
||||
|
||||
/** Round AMM single deposit/withdrawal LPToken amount.
|
||||
* The lambda's are used to delay evaluation until the function is executed
|
||||
* so that the calculations are not done twice.
|
||||
* noRoundCb() is called if AMMv1_3 is disabled. Otherwise, the rounding is set
|
||||
* and the lptokens are:
|
||||
* if isDeposit is Yes - the result of productCb(). The rounding is
|
||||
* the same for all calculations in productCb()
|
||||
* if isDeposit is No - the balance multiplied by productCb()
|
||||
* The lptokens are then adjusted to factor in the loss in precision
|
||||
* (we only keep 16 significant digits) when adding the lptokens to the balance.
|
||||
*/
|
||||
STAmount
|
||||
getRoundedLPTokens(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::function<Number()>&& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/* Next two functions adjust asset in/out amount to factor in the adjusted
|
||||
* lptokens. The lptokens are calculated from the asset in/out. The lptokens are
|
||||
* then adjusted to factor in the loss in precision. The adjusted lptokens might
|
||||
* be less than the initially calculated tokens. Therefore, the asset in/out
|
||||
* must be adjusted. The rounding might result in the adjusted amount being
|
||||
* greater than the original asset in/out amount. If this happens,
|
||||
* then the original amount is reduced by the difference in the adjusted amount
|
||||
* and the original amount. The actual tokens and the actual adjusted amount
|
||||
* are then recalculated. The minimum of the original and the actual
|
||||
* adjusted amount is returned.
|
||||
*/
|
||||
std::pair<STAmount, STAmount>
|
||||
adjustAssetInByTokens(
|
||||
Rules const& rules,
|
||||
STAmount const& balance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& tokens,
|
||||
std::uint16_t tfee);
|
||||
std::pair<STAmount, STAmount>
|
||||
adjustAssetOutByTokens(
|
||||
Rules const& rules,
|
||||
STAmount const& balance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& tokens,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Find a fraction of tokens after the tokens are adjusted. The fraction
|
||||
* is used to adjust equal deposit/withdraw amount.
|
||||
*/
|
||||
Number
|
||||
adjustFracByTokens(Rules const& rules, STAmount const& lptAMMBalance, STAmount const& tokens, Number const& frac);
|
||||
|
||||
} // namespace xrpl
|
||||
101
include/xrpl/tx/transactors/AMM/AMMUtils.h
Normal file
101
include/xrpl/tx/transactors/AMM/AMMUtils.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ReadView;
|
||||
class ApplyView;
|
||||
class Sandbox;
|
||||
class NetClock;
|
||||
|
||||
/** Get AMM pool balances.
|
||||
*/
|
||||
std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get AMM pool and LP token balances. If both optIssue are
|
||||
* provided then they are used as the AMM token pair issues.
|
||||
* Otherwise the missing issues are fetched from ammSle.
|
||||
*/
|
||||
Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
|
||||
ammHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
std::optional<Issue> const& optIssue1,
|
||||
std::optional<Issue> const& optIssue2,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get the balance of LP tokens.
|
||||
*/
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Currency const& cur1,
|
||||
Currency const& cur2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j);
|
||||
|
||||
STAmount
|
||||
ammLPHolds(ReadView const& view, SLE const& ammSle, AccountID const& lpAccount, beast::Journal const j);
|
||||
|
||||
/** Get AMM trading fee for the given account. The fee is discounted
|
||||
* if the account is the auction slot owner or one of the slot's authorized
|
||||
* accounts.
|
||||
*/
|
||||
std::uint16_t
|
||||
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account);
|
||||
|
||||
/** Returns total amount held by AMM for the given token.
|
||||
*/
|
||||
STAmount
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const& issue);
|
||||
|
||||
/** Delete trustlines to AMM. If all trustlines are deleted then
|
||||
* AMM object and account are deleted. Otherwise tecIMPCOMPLETE is returned.
|
||||
*/
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& view, Issue const& asset, Issue const& asset2, beast::Journal j);
|
||||
|
||||
/** Initialize Auction and Voting slots and set the trading/discounted fee.
|
||||
*/
|
||||
void
|
||||
initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Issue const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Return true if the Liquidity Provider is the only AMM provider, false
|
||||
* otherwise. Return tecINTERNAL if encountered an unexpected condition,
|
||||
* for instance Liquidity Provider has more than one LPToken trustline.
|
||||
*/
|
||||
Expected<bool, TER>
|
||||
isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount);
|
||||
|
||||
/** Due to rounding, the LPTokenBalance of the last LP might
|
||||
* not match the LP's trustline balance. If it's within the tolerance,
|
||||
* update LPTokenBalance to match the LP's trustline balance.
|
||||
*/
|
||||
Expected<bool, TER>
|
||||
verifyAndAdjustLPTokenBalance(
|
||||
Sandbox& sb,
|
||||
STAmount const& lpTokens,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account);
|
||||
|
||||
} // namespace xrpl
|
||||
52
include/xrpl/tx/transactors/AMM/AMMVote.h
Normal file
52
include/xrpl/tx/transactors/AMM/AMMVote.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMVote implements AMM vote Transactor.
|
||||
* This transactor allows for the TradingFee of the AMM instance be a votable
|
||||
* parameter. Any account (LP) that holds the corresponding LPTokens can cast
|
||||
* a vote using the new AMMVote transaction. VoteSlots array in ltAMM object
|
||||
* keeps track of upto eight active votes (VoteEntry) for the instance.
|
||||
* VoteEntry contains:
|
||||
* Account - account id that cast the vote.
|
||||
* FeeVal - proposed fee in basis points.
|
||||
* VoteWeight - LPTokens owned by the account in basis points.
|
||||
* TradingFee is calculated as sum(VoteWeight_i * fee_i)/sum(VoteWeight_i).
|
||||
* Every time AMMVote transaction is submitted, the transactor
|
||||
* - Fails the transaction if the account doesn't hold LPTokens
|
||||
* - Removes VoteEntry for accounts that don't hold LPTokens
|
||||
* - If there are fewer than eight VoteEntry objects then add new VoteEntry
|
||||
* object for the account.
|
||||
* - If all eight VoteEntry slots are full, then remove VoteEntry that
|
||||
* holds less LPTokens than the account. If all accounts hold more
|
||||
* LPTokens then fail transaction.
|
||||
* - If the account already holds a vote, then update VoteEntry.
|
||||
* - Calculate and update TradingFee.
|
||||
* @see [XLS30d:Governance: Trading Fee Voting
|
||||
* Mechanism](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMVote : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMVote(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
294
include/xrpl/tx/transactors/AMM/AMMWithdraw.h
Normal file
294
include/xrpl/tx/transactors/AMM/AMMWithdraw.h
Normal file
@@ -0,0 +1,294 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Sandbox;
|
||||
|
||||
/** AMMWithdraw implements AMM withdraw Transactor.
|
||||
* The withdraw transaction is used to remove liquidity from the AMM instance
|
||||
* pool, thus redeeming some share of the pools that one owns in the form
|
||||
* of LPTokens. If the trader withdraws proportional values of both assets
|
||||
* without changing their relative pricing, no trading fee is charged on
|
||||
* the transaction. The trader can specify different combination of
|
||||
* the fields in the withdrawal.
|
||||
* LPTokens - transaction assumes proportional withdrawal of pool assets
|
||||
* for the amount of LPTokens.
|
||||
* Asset1Out - transaction assumes withdrawal of single asset equivalent
|
||||
* to the amount specified in Asset1Out.
|
||||
* Asset1Out and Asset2Out - transaction assumes all assets withdrawal
|
||||
* with the constraints on the maximum amount of each asset that
|
||||
* the trader is willing to withdraw.
|
||||
* Asset1Out and LPTokens - transaction assumes withdrawal of single
|
||||
* asset specified in Asset1Out proportional to the share represented
|
||||
* by the amount of LPTokens.
|
||||
* Asset1Out and EPrice - transaction assumes withdrawal of single
|
||||
* asset with the following constraints:
|
||||
* a. Amount of asset1 if specified (not 0) in Asset1Out specifies
|
||||
* the minimum amount of asset1 that the trader is willing
|
||||
* to withdraw.
|
||||
* b. The effective price of asset traded out does not exceed
|
||||
* the amount specified in EPrice.
|
||||
* Following updates after a successful transaction:
|
||||
* The withdrawn asset, if XRP, is transferred from AMM instance account
|
||||
* to the account that initiated the transaction, thus changing
|
||||
* the Balance field of each account.
|
||||
* The withdrawn asset, if token, is balanced between the AMM instance
|
||||
* account and the issuer account.
|
||||
* The LPTokens ~ are balanced between the AMM instance account and
|
||||
* the account that initiated the transaction.
|
||||
* The pool composition is updated.
|
||||
* @see [XLS30d:AMMWithdraw
|
||||
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
|
||||
enum class WithdrawAll : bool { No = false, Yes };
|
||||
|
||||
class AMMWithdraw : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMWithdraw(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amount2Balance current LP asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokens current LPT balance
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param withdrawAll if withdrawing all lptokens
|
||||
* @param priorBalance balance before fees
|
||||
* @return
|
||||
*/
|
||||
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
equalWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const account,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHanding,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
|
||||
/** Withdraw requested assets and token from AMM into LP account.
|
||||
* Return new total LPToken balance and the withdrawn amounts for both
|
||||
* assets.
|
||||
* @param view
|
||||
* @param ammSle AMM ledger entry
|
||||
* @param ammAccount AMM account
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amountWithdraw asset1 withdraw amount
|
||||
* @param amount2Withdraw asset2 withdraw amount
|
||||
* @param lpTokensAMMBalance current AMM LPT balance
|
||||
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param withdrawAll if withdraw all lptokens
|
||||
* @param priorBalance balance before fees
|
||||
* @return
|
||||
*/
|
||||
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
withdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& account,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountWithdraw,
|
||||
std::optional<STAmount> const& amount2Withdraw,
|
||||
STAmount const& lpTokensAMMBalance,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
|
||||
static std::pair<TER, bool>
|
||||
deleteAMMAccountIfEmpty(
|
||||
Sandbox& sb,
|
||||
std::shared_ptr<SLE> const ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
beast::Journal const& journal);
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Withdraw requested assets and token from AMM into LP account.
|
||||
* Return new total LPToken balance.
|
||||
* @param view
|
||||
* @param ammSle AMM ledger entry
|
||||
* @param ammAccount AMM account
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amountWithdraw asset1 withdraw amount
|
||||
* @param amount2Withdraw asset2 withdraw amount
|
||||
* @param lpTokensAMMBalance current AMM LPT balance
|
||||
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
withdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountWithdraw,
|
||||
std::optional<STAmount> const& amount2Withdraw,
|
||||
STAmount const& lpTokensAMMBalance,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amount2Balance current LP asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokens current LPT balance
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Withdraw both assets (Asset1Out, Asset2Out) with the constraints
|
||||
* on the maximum amount of each asset that the trader is willing
|
||||
* to withdraw. The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param amount2 max asset2 withdraw amount
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalWithdrawLimit(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset withdrawal (Asset1Out) equivalent to the amount specified
|
||||
* in Asset1Out. The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset withdrawal (Asset1Out, LPTokens) proportional
|
||||
* to the share specified by tokens. The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Withdraw single asset (Asset1Out, EPrice) with two constraints.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param ePrice maximum asset1 effective price
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdrawEPrice(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& ePrice,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Check from the flags if it's withdraw all */
|
||||
static WithdrawAll
|
||||
isWithdrawAll(STTx const& tx);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/tx/transactors/Batch.h
Normal file
55
include/xrpl/tx/transactors/Batch.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Batch : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Batch(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkSign(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
static constexpr auto disabledTxTypes = std::to_array<TxType>({
|
||||
ttVAULT_CREATE,
|
||||
ttVAULT_SET,
|
||||
ttVAULT_DELETE,
|
||||
ttVAULT_DEPOSIT,
|
||||
ttVAULT_WITHDRAW,
|
||||
ttVAULT_CLAWBACK,
|
||||
ttLOAN_BROKER_SET,
|
||||
ttLOAN_BROKER_DELETE,
|
||||
ttLOAN_BROKER_COVER_DEPOSIT,
|
||||
ttLOAN_BROKER_COVER_WITHDRAW,
|
||||
ttLOAN_BROKER_COVER_CLAWBACK,
|
||||
ttLOAN_SET,
|
||||
ttLOAN_DELETE,
|
||||
ttLOAN_MANAGE,
|
||||
ttLOAN_PAY,
|
||||
});
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
45
include/xrpl/tx/transactors/Change.h
Normal file
45
include/xrpl/tx/transactors/Change.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Change : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Change(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
void
|
||||
preCompute() override;
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return XRPAmount{0};
|
||||
}
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
private:
|
||||
TER
|
||||
applyAmendment();
|
||||
|
||||
TER
|
||||
applyFee();
|
||||
|
||||
TER
|
||||
applyUNLModify();
|
||||
};
|
||||
|
||||
using EnableAmendment = Change;
|
||||
using SetFee = Change;
|
||||
using UNLModify = Change;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CancelCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CancelCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CancelCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CancelCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCancel = CancelCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CashCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CashCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CashCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CashCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCash = CashCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CreateCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CreateCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CreateCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCreate = CreateCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/Clawback.h
Normal file
29
include/xrpl/tx/transactors/Clawback.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Clawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Clawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
66
include/xrpl/tx/transactors/CreateTicket.h
Normal file
66
include/xrpl/tx/transactors/CreateTicket.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CreateTicket : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
constexpr static std::uint32_t minValidCount = 1;
|
||||
|
||||
// A note on how the maxValidCount was determined. The goal is for
|
||||
// a single TicketCreate transaction to not use more compute power than
|
||||
// a single compute-intensive Payment.
|
||||
//
|
||||
// Timing was performed using a MacBook Pro laptop and a release build
|
||||
// with asserts off. 20 measurements were taken of each of the Payment
|
||||
// and TicketCreate transactions and averaged to get timings.
|
||||
//
|
||||
// For the example compute-intensive Payment a Discrepancy unit test
|
||||
// unit test Payment with 3 paths was chosen. With all the latest
|
||||
// amendments enabled, that Payment::doApply() operation took, on
|
||||
// average, 1.25 ms.
|
||||
//
|
||||
// Using that same test set up creating 250 Tickets in a single
|
||||
// CreateTicket::doApply() in a unit test took, on average, 1.21 ms.
|
||||
//
|
||||
// So, for the moment, a single transaction creating 250 Tickets takes
|
||||
// about the same compute time as a single compute-intensive payment.
|
||||
//
|
||||
// October 2018.
|
||||
constexpr static std::uint32_t maxValidCount = 250;
|
||||
|
||||
// The maximum number of Tickets an account may hold. If a
|
||||
// TicketCreate would cause an account to own more than this many
|
||||
// tickets, then the TicketCreate will fail.
|
||||
//
|
||||
// The number was chosen arbitrarily and is an effort toward avoiding
|
||||
// ledger-stuffing with Tickets.
|
||||
constexpr static std::uint32_t maxTicketThreshold = 250;
|
||||
|
||||
explicit CreateTicket(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Precondition: fee collection is likely. Attempt to create ticket(s). */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using TicketCreate = CreateTicket;
|
||||
|
||||
} // namespace xrpl
|
||||
77
include/xrpl/tx/transactors/Credentials.h
Normal file
77
include/xrpl/tx/transactors/Credentials.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialAccept : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
47
include/xrpl/tx/transactors/DID.h
Normal file
47
include/xrpl/tx/transactors/DID.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DIDSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DIDDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner);
|
||||
|
||||
static TER
|
||||
deleteSLE(ApplyView& view, std::shared_ptr<SLE> sle, AccountID const owner, beast::Journal j);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
30
include/xrpl/tx/transactors/Delegate/DelegateSet.h
Normal file
30
include/xrpl/tx/transactors/Delegate/DelegateSet.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DelegateSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DelegateSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
deleteDelegate(ApplyView& view, std::shared_ptr<SLE> const& sle, AccountID const& account, beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/Delegate/DelegateUtils.h
Normal file
35
include/xrpl/tx/transactors/Delegate/DelegateUtils.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Check if the delegate account has permission to execute the transaction.
|
||||
* @param delegate The delegate account.
|
||||
* @param tx The transaction that the delegate account intends to execute.
|
||||
* @return tesSUCCESS if the transaction is allowed, terNO_DELEGATE_PERMISSION
|
||||
* if not.
|
||||
*/
|
||||
NotTEC
|
||||
checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx);
|
||||
|
||||
/**
|
||||
* Load the granular permissions granted to the delegate account for the
|
||||
* specified transaction type
|
||||
* @param delegate The delegate account.
|
||||
* @param type Used to determine which granted granular permissions to load,
|
||||
* based on the transaction type.
|
||||
* @param granularPermissions Granted granular permissions tied to the
|
||||
* transaction type.
|
||||
*/
|
||||
void
|
||||
loadGranularPermission(
|
||||
std::shared_ptr<SLE const> const& delegate,
|
||||
TxType const& type,
|
||||
std::unordered_set<GranularPermissionType>& granularPermissions);
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/DeleteAccount.h
Normal file
34
include/xrpl/tx/transactors/DeleteAccount.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DeleteAccount : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit DeleteAccount(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using AccountDelete = DeleteAccount;
|
||||
|
||||
} // namespace xrpl
|
||||
40
include/xrpl/tx/transactors/DeleteOracle.h
Normal file
40
include/xrpl/tx/transactors/DeleteOracle.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
Price Oracle is a system that acts as a bridge between
|
||||
a blockchain network and the external world, providing off-chain price data
|
||||
to decentralized applications (dApps) on the blockchain. This implementation
|
||||
conforms to the requirements specified in the XLS-47d.
|
||||
|
||||
The DeleteOracle transactor implements the deletion of Oracle objects.
|
||||
*/
|
||||
|
||||
class DeleteOracle : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
static TER
|
||||
deleteOracle(ApplyView& view, std::shared_ptr<SLE> const& sle, AccountID const& account, beast::Journal j);
|
||||
};
|
||||
|
||||
using OracleDelete = DeleteOracle;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/tx/transactors/DepositPreauth.h
Normal file
33
include/xrpl/tx/transactors/DepositPreauth.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DepositPreauth : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DepositPreauth(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
removeFromLedger(ApplyView& view, uint256 const& delIndex, beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
80
include/xrpl/tx/transactors/Escrow.h
Normal file
80
include/xrpl/tx/transactors/Escrow.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowFinish : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowCancel : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/tx/transactors/LedgerStateFix.h
Normal file
33
include/xrpl/tx/transactors/LedgerStateFix.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LedgerStateFix : public Transactor
|
||||
{
|
||||
public:
|
||||
enum FixType : std::uint16_t {
|
||||
nfTokenPageLink = 1,
|
||||
};
|
||||
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LedgerStateFix(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
467
include/xrpl/tx/transactors/Lending/LendingHelpers.h
Normal file
467
include/xrpl/tx/transactors/Lending/LendingHelpers.h
Normal file
@@ -0,0 +1,467 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct PreflightContext;
|
||||
|
||||
// Lending protocol has dependencies, so capture them here.
|
||||
bool
|
||||
checkLendingProtocolDependencies(PreflightContext const& ctx);
|
||||
|
||||
static constexpr std::uint32_t secondsInYear = 365 * 24 * 60 * 60;
|
||||
|
||||
Number
|
||||
loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
|
||||
|
||||
/// Ensure the periodic payment is always rounded consistently
|
||||
inline Number
|
||||
roundPeriodicPayment(Asset const& asset, Number const& periodicPayment, std::int32_t scale)
|
||||
{
|
||||
return roundToAsset(asset, periodicPayment, scale, Number::upward);
|
||||
}
|
||||
|
||||
/* Represents the breakdown of amounts to be paid and changes applied to the
|
||||
* Loan object while processing a loan payment.
|
||||
*
|
||||
* This structure is returned after processing a loan payment transaction and
|
||||
* captures the amounts that need to be paid. The actual ledger entry changes
|
||||
* are made in LoanPay based on this structure values.
|
||||
*
|
||||
* The sum of principalPaid, interestPaid, and feePaid represents the total
|
||||
* amount to be deducted from the borrower's account. The valueChange field
|
||||
* tracks whether the loan's total value increased or decreased beyond normal
|
||||
* amortization.
|
||||
*
|
||||
* This structure is explained in the XLS-66 spec, section 3.2.4.2 (Payment
|
||||
* Processing).
|
||||
*/
|
||||
struct LoanPaymentParts
|
||||
{
|
||||
// The amount of principal paid that reduces the loan balance.
|
||||
// This amount is subtracted from sfPrincipalOutstanding in the Loan object
|
||||
// and paid to the Vault
|
||||
Number principalPaid = numZero;
|
||||
|
||||
// The total amount of interest paid to the Vault.
|
||||
// This includes:
|
||||
// - Tracked interest from the amortization schedule
|
||||
// - Untracked interest (e.g., late payment penalty interest)
|
||||
// This value is always non-negative.
|
||||
Number interestPaid = numZero;
|
||||
|
||||
// The change in the loan's total value outstanding.
|
||||
// - If valueChange < 0: Loan value decreased
|
||||
// - If valueChange > 0: Loan value increased
|
||||
// - If valueChange = 0: No value adjustment
|
||||
//
|
||||
// For regular on-time payments, this is always 0. Non-zero values occur
|
||||
// when:
|
||||
// - Overpayments reduce the loan balance beyond the scheduled amount
|
||||
// - Late payments add penalty interest to the loan value
|
||||
// - Early full payment may increase or decrease the loan value based on
|
||||
// terms
|
||||
Number valueChange = numZero;
|
||||
|
||||
/* The total amount of fees paid to the Broker.
|
||||
* This includes:
|
||||
* - Tracked management fees from the amortization schedule
|
||||
* - Untracked fees (e.g., late payment fees, service fees, origination
|
||||
* fees) This value is always non-negative.
|
||||
*/
|
||||
Number feePaid = numZero;
|
||||
|
||||
LoanPaymentParts&
|
||||
operator+=(LoanPaymentParts const& other);
|
||||
|
||||
bool
|
||||
operator==(LoanPaymentParts const& other) const;
|
||||
};
|
||||
|
||||
/** This structure captures the parts of a loan state.
|
||||
*
|
||||
* Whether the values are theoretical (unrounded) or rounded will depend on how
|
||||
* it was computed.
|
||||
*
|
||||
* Many of the fields can be derived from each other, but they're all provided
|
||||
* here to reduce code duplication and possible mistakes.
|
||||
* e.g.
|
||||
* * interestOutstanding = valueOutstanding - principalOutstanding
|
||||
* * interestDue = interestOutstanding - managementFeeDue
|
||||
*/
|
||||
struct LoanState
|
||||
{
|
||||
// Total value still due to be paid by the borrower.
|
||||
Number valueOutstanding;
|
||||
// Principal still due to be paid by the borrower.
|
||||
Number principalOutstanding;
|
||||
// Interest still due to be paid to the Vault.
|
||||
// This is a portion of interestOutstanding
|
||||
Number interestDue;
|
||||
// Management fee still due to be paid to the broker.
|
||||
// This is a portion of interestOutstanding
|
||||
Number managementFeeDue;
|
||||
|
||||
// Interest still due to be paid by the borrower.
|
||||
Number
|
||||
interestOutstanding() const
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
interestDue + managementFeeDue == valueOutstanding - principalOutstanding,
|
||||
"xrpl::LoanState::interestOutstanding",
|
||||
"other values add up correctly");
|
||||
return interestDue + managementFeeDue;
|
||||
}
|
||||
};
|
||||
|
||||
/* Describes the initial computed properties of a loan.
|
||||
*
|
||||
* This structure contains the fundamental calculated values that define a
|
||||
* loan's payment structure and amortization schedule. These properties are
|
||||
* computed:
|
||||
* - At loan creation (LoanSet transaction)
|
||||
* - When loan terms change (e.g., after an overpayment that reduces the loan
|
||||
* balance)
|
||||
*/
|
||||
struct LoanProperties
|
||||
{
|
||||
// The unrounded amount to be paid at each regular payment period.
|
||||
// Calculated using the standard amortization formula based on principal,
|
||||
// interest rate, and number of payments.
|
||||
// The actual amount paid in the LoanPay transaction must be rounded up to
|
||||
// the precision of the asset and loan.
|
||||
Number periodicPayment;
|
||||
|
||||
// The loan's current state, with all values rounded to the loan's scale.
|
||||
LoanState loanState;
|
||||
|
||||
// The scale (decimal places) used for rounding all loan amounts.
|
||||
// This is the maximum of:
|
||||
// - The asset's native scale
|
||||
// - A minimum scale required to represent the periodic payment accurately
|
||||
// All loan state values (principal, interest, fees) are rounded to this
|
||||
// scale.
|
||||
std::int32_t loanScale;
|
||||
|
||||
// The principal portion of the first payment.
|
||||
Number firstPaymentPrincipal;
|
||||
};
|
||||
|
||||
// Some values get re-rounded to the vault scale any time they are adjusted. In
|
||||
// addition, they are prevented from ever going below zero. This helps avoid
|
||||
// accumulated rounding errors and leftover dust amounts.
|
||||
template <class NumberProxy>
|
||||
void
|
||||
adjustImpreciseNumber(NumberProxy value, Number const& adjustment, Asset const& asset, int vaultScale)
|
||||
{
|
||||
value = roundToAsset(asset, value + adjustment, vaultScale);
|
||||
|
||||
if (*value < beast::zero)
|
||||
value = 0;
|
||||
}
|
||||
|
||||
inline int
|
||||
getAssetsTotalScale(SLE::const_ref vaultSle)
|
||||
{
|
||||
if (!vaultSle)
|
||||
return Number::minExponent - 1; // LCOV_EXCL_LINE
|
||||
return STAmount{vaultSle->at(sfAsset), vaultSle->at(sfAssetsTotal)}.exponent();
|
||||
}
|
||||
|
||||
TER
|
||||
checkLoanGuards(
|
||||
Asset const& vaultAsset,
|
||||
Number const& principalRequested,
|
||||
bool expectInterest,
|
||||
std::uint32_t paymentTotal,
|
||||
LoanProperties const& properties,
|
||||
beast::Journal j);
|
||||
|
||||
LoanState
|
||||
computeTheoreticalLoanState(
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t const paymentRemaining,
|
||||
TenthBips32 const managementFeeRate);
|
||||
|
||||
// Constructs a valid LoanState object from arbitrary inputs
|
||||
LoanState
|
||||
constructLoanState(
|
||||
Number const& totalValueOutstanding,
|
||||
Number const& principalOutstanding,
|
||||
Number const& managementFeeOutstanding);
|
||||
|
||||
// Constructs a valid LoanState object from a Loan object, which always has
|
||||
// rounded values
|
||||
LoanState
|
||||
constructRoundedLoanState(SLE::const_ref loan);
|
||||
|
||||
Number
|
||||
computeManagementFee(Asset const& asset, Number const& interest, TenthBips32 managementFeeRate, std::int32_t scale);
|
||||
|
||||
Number
|
||||
computeFullPaymentInterest(
|
||||
Number const& theoreticalPrincipalOutstanding,
|
||||
Number const& periodicRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t paymentInterval,
|
||||
std::uint32_t prevPaymentDate,
|
||||
std::uint32_t startDate,
|
||||
TenthBips32 closeInterestRate);
|
||||
|
||||
namespace detail {
|
||||
// These classes and functions should only be accessed by LendingHelper
|
||||
// functions and unit tests
|
||||
|
||||
enum class PaymentSpecialCase { none, final, extra };
|
||||
|
||||
/* Represents a single loan payment component parts.
|
||||
|
||||
* This structure captures the "delta" (change) values that will be applied to
|
||||
* the tracked fields in the Loan ledger object when a payment is processed.
|
||||
*
|
||||
* These are called "deltas" because they represent the amount by which each
|
||||
* corresponding field in the Loan object will be reduced.
|
||||
* They are "tracked" as they change tracked loan values.
|
||||
*/
|
||||
struct PaymentComponents
|
||||
{
|
||||
// The change in total value outstanding for this payment.
|
||||
// This amount will be subtracted from sfTotalValueOutstanding in the Loan
|
||||
// object. Equal to the sum of trackedPrincipalDelta,
|
||||
// trackedInterestPart(), and trackedManagementFeeDelta.
|
||||
Number trackedValueDelta;
|
||||
|
||||
// The change in principal outstanding for this payment.
|
||||
// This amount will be subtracted from sfPrincipalOutstanding in the Loan
|
||||
// object, representing the portion of the payment that reduces the
|
||||
// original loan amount.
|
||||
Number trackedPrincipalDelta;
|
||||
|
||||
// The change in management fee outstanding for this payment.
|
||||
// This amount will be subtracted from sfManagementFeeOutstanding in the
|
||||
// Loan object. This represents only the tracked management fees from the
|
||||
// amortization schedule and does not include additional untracked fees
|
||||
// (such as late payment fees) that go directly to the broker.
|
||||
Number trackedManagementFeeDelta;
|
||||
|
||||
// Indicates if this payment has special handling requirements.
|
||||
// - none: Regular scheduled payment
|
||||
// - final: The last payment that closes out the loan
|
||||
// - extra: An additional payment beyond the regular schedule (overpayment)
|
||||
PaymentSpecialCase specialCase = PaymentSpecialCase::none;
|
||||
|
||||
// Calculates the tracked interest portion of this payment.
|
||||
// This is derived from the other components as:
|
||||
// trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta
|
||||
//
|
||||
// @return The amount of tracked interest included in this payment that
|
||||
// will be paid to the vault.
|
||||
Number
|
||||
trackedInterestPart() const;
|
||||
};
|
||||
|
||||
/* Extends PaymentComponents with untracked payment amounts.
|
||||
*
|
||||
* This structure adds untracked fees and interest to the base
|
||||
* PaymentComponents, representing amounts that don't affect the Loan object's
|
||||
* tracked state but are still part of the total payment due from the borrower.
|
||||
*
|
||||
* Untracked amounts include:
|
||||
* - Late payment fees that go directly to the Broker
|
||||
* - Late payment penalty interest that goes directly to the Vault
|
||||
* - Service fees
|
||||
*
|
||||
* The key distinction is that tracked amounts reduce the Loan object's state
|
||||
* (sfTotalValueOutstanding, sfPrincipalOutstanding,
|
||||
* sfManagementFeeOutstanding), while untracked amounts are paid directly to the
|
||||
* recipient without affecting the loan's amortization schedule.
|
||||
*/
|
||||
struct ExtendedPaymentComponents : public PaymentComponents
|
||||
{
|
||||
// Additional management fees that go directly to the Broker.
|
||||
// This includes fees not part of the standard amortization schedule
|
||||
// (e.g., late fees, service fees, origination fees).
|
||||
// This value may be negative, though the final value returned in
|
||||
// LoanPaymentParts.feePaid will never be negative.
|
||||
Number untrackedManagementFee;
|
||||
|
||||
// Additional interest that goes directly to the Vault.
|
||||
// This includes interest not part of the standard amortization schedule
|
||||
// (e.g., late payment penalty interest).
|
||||
// This value may be negative, though the final value returned in
|
||||
// LoanPaymentParts.interestPaid will never be negative.
|
||||
Number untrackedInterest;
|
||||
|
||||
// The complete amount due from the borrower for this payment.
|
||||
// Calculated as: trackedValueDelta + untrackedInterest +
|
||||
// untrackedManagementFee
|
||||
//
|
||||
// This value is used to validate that the payment amount provided by the
|
||||
// borrower is sufficient to cover all components of the payment.
|
||||
Number totalDue;
|
||||
|
||||
ExtendedPaymentComponents(PaymentComponents const& p, Number fee, Number interest = numZero)
|
||||
: PaymentComponents(p)
|
||||
, untrackedManagementFee(fee)
|
||||
, untrackedInterest(interest)
|
||||
, totalDue(trackedValueDelta + untrackedInterest + untrackedManagementFee)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Represents the differences between two loan states.
|
||||
*
|
||||
* This structure is used to capture the change in each component of a loan's
|
||||
* state, typically when computing the difference between two LoanState objects
|
||||
* (e.g., before and after a payment). It is a convenient way to capture changes
|
||||
* in each component. How that difference is used depends on the context.
|
||||
*/
|
||||
struct LoanStateDeltas
|
||||
{
|
||||
// The difference in principal outstanding between two loan states.
|
||||
Number principal;
|
||||
|
||||
// The difference in interest due between two loan states.
|
||||
Number interest;
|
||||
|
||||
// The difference in management fee outstanding between two loan states.
|
||||
Number managementFee;
|
||||
|
||||
/* Calculates the total change across all components.
|
||||
* @return The sum of principal, interest, and management fee deltas.
|
||||
*/
|
||||
Number
|
||||
total() const
|
||||
{
|
||||
return principal + interest + managementFee;
|
||||
}
|
||||
|
||||
// Ensures all delta values are non-negative.
|
||||
void
|
||||
nonNegative();
|
||||
};
|
||||
|
||||
Expected<std::pair<LoanPaymentParts, LoanProperties>, TER>
|
||||
tryOverpayment(
|
||||
Asset const& asset,
|
||||
std::int32_t loanScale,
|
||||
ExtendedPaymentComponents const& overpaymentComponents,
|
||||
LoanState const& roundedLoanState,
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentRemaining,
|
||||
TenthBips16 const managementFeeRate,
|
||||
beast::Journal j);
|
||||
|
||||
Number
|
||||
computeRaisedRate(Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
computePaymentFactor(Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
std::pair<Number, Number>
|
||||
computeInterestAndFeeParts(
|
||||
Asset const& asset,
|
||||
Number const& interest,
|
||||
TenthBips16 managementFeeRate,
|
||||
std::int32_t loanScale);
|
||||
|
||||
Number
|
||||
loanPeriodicPayment(Number const& principalOutstanding, Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
loanPrincipalFromPeriodicPayment(
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
loanLatePaymentInterest(
|
||||
Number const& principalOutstanding,
|
||||
TenthBips32 lateInterestRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t nextPaymentDueDate);
|
||||
|
||||
Number
|
||||
loanAccruedInterest(
|
||||
Number const& principalOutstanding,
|
||||
Number const& periodicRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t startDate,
|
||||
std::uint32_t prevPaymentDate,
|
||||
std::uint32_t paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents
|
||||
computeOverpaymentComponents(
|
||||
Asset const& asset,
|
||||
int32_t const loanScale,
|
||||
Number const& overpayment,
|
||||
TenthBips32 const overpaymentInterestRate,
|
||||
TenthBips32 const overpaymentFeeRate,
|
||||
TenthBips16 const managementFeeRate);
|
||||
|
||||
PaymentComponents
|
||||
computePaymentComponents(
|
||||
Asset const& asset,
|
||||
std::int32_t scale,
|
||||
Number const& totalValueOutstanding,
|
||||
Number const& principalOutstanding,
|
||||
Number const& managementFeeOutstanding,
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentRemaining,
|
||||
TenthBips16 managementFeeRate);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
detail::LoanStateDeltas
|
||||
operator-(LoanState const& lhs, LoanState const& rhs);
|
||||
|
||||
LoanState
|
||||
operator-(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
|
||||
|
||||
LoanState
|
||||
operator+(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
|
||||
|
||||
LoanProperties
|
||||
computeLoanProperties(
|
||||
Asset const& asset,
|
||||
Number const& principalOutstanding,
|
||||
TenthBips32 interestRate,
|
||||
std::uint32_t paymentInterval,
|
||||
std::uint32_t paymentsRemaining,
|
||||
TenthBips32 managementFeeRate,
|
||||
std::int32_t minimumScale);
|
||||
|
||||
LoanProperties
|
||||
computeLoanProperties(
|
||||
Asset const& asset,
|
||||
Number const& principalOutstanding,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentsRemaining,
|
||||
TenthBips32 managementFeeRate,
|
||||
std::int32_t minimumScale);
|
||||
|
||||
bool
|
||||
isRounded(Asset const& asset, Number const& value, std::int32_t scale);
|
||||
|
||||
// Indicates what type of payment is being made.
|
||||
// regular, late, and full are mutually exclusive.
|
||||
// overpayment is an "add on" to a regular payment, and follows that path with
|
||||
// potential extra work at the end.
|
||||
enum class LoanPaymentType { regular = 0, late, full, overpayment };
|
||||
|
||||
Expected<LoanPaymentParts, TER>
|
||||
loanMakePayment(
|
||||
Asset const& asset,
|
||||
ApplyView& view,
|
||||
SLE::ref loan,
|
||||
SLE::const_ref brokerSle,
|
||||
STAmount const& amount,
|
||||
LoanPaymentType const paymentType,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverDeposit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverDeposit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverWithdraw : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverWithdraw(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/Lending/LoanBrokerSet.h
Normal file
34
include/xrpl/tx/transactors/Lending/LoanBrokerSet.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static std::vector<OptionaledField<STNumber>> const&
|
||||
getValueFields();
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanDelete.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanDelete.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/tx/transactors/Lending/LoanManage.h
Normal file
55
include/xrpl/tx/transactors/Lending/LoanManage.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanManage : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanManage(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
static TER
|
||||
defaultLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref brokerSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
static TER
|
||||
impairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
[[nodiscard]] static TER
|
||||
unimpairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/tx/transactors/Lending/LoanPay.h
Normal file
37
include/xrpl/tx/transactors/Lending/LoanPay.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanPay : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanPay(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
56
include/xrpl/tx/transactors/Lending/LoanSet.h
Normal file
56
include/xrpl/tx/transactors/Lending/LoanSet.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/transactors/Lending/LendingHelpers.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkSign(PreclaimContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static std::vector<OptionaledField<STNumber>> const&
|
||||
getValueFields();
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
public:
|
||||
static std::uint32_t constexpr minPaymentTotal = 1;
|
||||
static std::uint32_t constexpr defaultPaymentTotal = 1;
|
||||
static_assert(defaultPaymentTotal >= minPaymentTotal);
|
||||
|
||||
static std::uint32_t constexpr minPaymentInterval = 60;
|
||||
static std::uint32_t constexpr defaultPaymentInterval = 60;
|
||||
static_assert(defaultPaymentInterval >= minPaymentInterval);
|
||||
|
||||
static std::uint32_t constexpr defaultGracePeriod = 60;
|
||||
static_assert(defaultGracePeriod >= minPaymentInterval);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
41
include/xrpl/tx/transactors/MPT/MPTokenAuthorize.h
Normal file
41
include/xrpl/tx/transactors/MPT/MPTokenAuthorize.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct MPTAuthorizeArgs
|
||||
{
|
||||
XRPAmount const& priorBalance;
|
||||
MPTID const& mptIssuanceID;
|
||||
AccountID const& account;
|
||||
std::uint32_t flags{};
|
||||
std::optional<AccountID> holderID{};
|
||||
};
|
||||
|
||||
class MPTokenAuthorize : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit MPTokenAuthorize(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
static TER
|
||||
createMPToken(ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, std::uint32_t const flags);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
48
include/xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h
Normal file
48
include/xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct MPTCreateArgs
|
||||
{
|
||||
std::optional<XRPAmount> priorBalance;
|
||||
AccountID const& account;
|
||||
std::uint32_t sequence;
|
||||
std::uint32_t flags = 0;
|
||||
std::optional<std::uint64_t> maxAmount{};
|
||||
std::optional<std::uint8_t> assetScale{};
|
||||
std::optional<std::uint16_t> transferFee{};
|
||||
std::optional<Slice> const& metadata{};
|
||||
std::optional<uint256> domainId{};
|
||||
std::optional<std::uint32_t> mutableFlags{};
|
||||
};
|
||||
|
||||
class MPTokenIssuanceCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit MPTokenIssuanceCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
static Expected<MPTID, TER>
|
||||
create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h
Normal file
29
include/xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class MPTokenIssuanceDestroy : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit MPTokenIssuanceDestroy(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h
Normal file
35
include/xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class MPTokenIssuanceSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit MPTokenIssuanceSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
42
include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h
Normal file
42
include/xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenAcceptOffer : public Transactor
|
||||
{
|
||||
private:
|
||||
TER
|
||||
pay(AccountID const& from, AccountID const& to, STAmount const& amount);
|
||||
|
||||
TER
|
||||
acceptOffer(std::shared_ptr<SLE> const& offer);
|
||||
|
||||
TER
|
||||
bridgeOffers(std::shared_ptr<SLE> const& buy, std::shared_ptr<SLE> const& sell);
|
||||
|
||||
TER
|
||||
transferNFToken(AccountID const& buyer, AccountID const& seller, uint256 const& nfTokenID);
|
||||
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenAcceptOffer(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/NFT/NFTokenBurn.h
Normal file
26
include/xrpl/tx/transactors/NFT/NFTokenBurn.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenBurn : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenBurn(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h
Normal file
29
include/xrpl/tx/transactors/NFT/NFTokenCancelOffer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenCancelOffer : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenCancelOffer(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/NFT/NFTokenCreateOffer.h
Normal file
29
include/xrpl/tx/transactors/NFT/NFTokenCreateOffer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenCreateOffer : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenCreateOffer(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
43
include/xrpl/tx/transactors/NFT/NFTokenMint.h
Normal file
43
include/xrpl/tx/transactors/NFT/NFTokenMint.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/nft.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/transactors/NFT/NFTokenUtils.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenMint : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenMint(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
// Public to support unit tests.
|
||||
static uint256
|
||||
createNFTokenID(
|
||||
std::uint16_t flags,
|
||||
std::uint16_t fee,
|
||||
AccountID const& issuer,
|
||||
nft::Taxon taxon,
|
||||
std::uint32_t tokenSeq);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/NFT/NFTokenModify.h
Normal file
26
include/xrpl/tx/transactors/NFT/NFTokenModify.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class NFTokenModify : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit NFTokenModify(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
128
include/xrpl/tx/transactors/NFT/NFTokenUtils.h
Normal file
128
include/xrpl/tx/transactors/NFT/NFTokenUtils.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/nft.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace nft {
|
||||
|
||||
/** Delete up to a specified number of offers from the specified token offer
|
||||
* directory. */
|
||||
std::size_t
|
||||
removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t maxDeletableOffers);
|
||||
|
||||
/** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */
|
||||
TER
|
||||
notTooManyOffers(ReadView const& view, uint256 const& nftokenID);
|
||||
|
||||
/** Finds the specified token in the owner's token directory. */
|
||||
std::optional<STObject>
|
||||
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID);
|
||||
|
||||
/** Finds the token in the owner's token directory. Returns token and page. */
|
||||
struct TokenAndPage
|
||||
{
|
||||
STObject token;
|
||||
std::shared_ptr<SLE> page;
|
||||
|
||||
TokenAndPage(STObject const& token_, std::shared_ptr<SLE> page_) : token(token_), page(std::move(page_))
|
||||
{
|
||||
}
|
||||
};
|
||||
std::optional<TokenAndPage>
|
||||
findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID);
|
||||
|
||||
/** Insert the token in the owner's token directory. */
|
||||
TER
|
||||
insertToken(ApplyView& view, AccountID owner, STObject&& nft);
|
||||
|
||||
/** Remove the token from the owner's token directory. */
|
||||
TER
|
||||
removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID);
|
||||
|
||||
TER
|
||||
removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, std::shared_ptr<SLE>&& page);
|
||||
|
||||
/** Deletes the given token offer.
|
||||
|
||||
An offer is tracked in two separate places:
|
||||
- The token's 'buy' directory, if it's a buy offer; or
|
||||
- The token's 'sell' directory, if it's a sell offer; and
|
||||
- The owner directory of the account that placed the offer.
|
||||
|
||||
The offer also consumes one incremental reserve.
|
||||
*/
|
||||
bool
|
||||
deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer);
|
||||
|
||||
/** Repairs the links in an NFTokenPage directory.
|
||||
|
||||
Returns true if a repair took place, otherwise false.
|
||||
*/
|
||||
bool
|
||||
repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner);
|
||||
|
||||
bool
|
||||
compareTokens(uint256 const& a, uint256 const& b);
|
||||
|
||||
TER
|
||||
changeTokenURI(
|
||||
ApplyView& view,
|
||||
AccountID const& owner,
|
||||
uint256 const& nftokenID,
|
||||
std::optional<xrpl::Slice> const& uri);
|
||||
|
||||
/** Preflight checks shared by NFTokenCreateOffer and NFTokenMint */
|
||||
NotTEC
|
||||
tokenOfferCreatePreflight(
|
||||
AccountID const& acctID,
|
||||
STAmount const& amount,
|
||||
std::optional<AccountID> const& dest,
|
||||
std::optional<std::uint32_t> const& expiration,
|
||||
std::uint16_t nftFlags,
|
||||
Rules const& rules,
|
||||
std::optional<AccountID> const& owner = std::nullopt,
|
||||
std::uint32_t txFlags = lsfSellNFToken);
|
||||
|
||||
/** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */
|
||||
TER
|
||||
tokenOfferCreatePreclaim(
|
||||
ReadView const& view,
|
||||
AccountID const& acctID,
|
||||
AccountID const& nftIssuer,
|
||||
STAmount const& amount,
|
||||
std::optional<AccountID> const& dest,
|
||||
std::uint16_t nftFlags,
|
||||
std::uint16_t xferFee,
|
||||
beast::Journal j,
|
||||
std::optional<AccountID> const& owner = std::nullopt,
|
||||
std::uint32_t txFlags = lsfSellNFToken);
|
||||
|
||||
/** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */
|
||||
TER
|
||||
tokenOfferCreateApply(
|
||||
ApplyView& view,
|
||||
AccountID const& acctID,
|
||||
STAmount const& amount,
|
||||
std::optional<AccountID> const& dest,
|
||||
std::optional<std::uint32_t> const& expiration,
|
||||
SeqProxy seqProxy,
|
||||
uint256 const& nftokenID,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal j,
|
||||
std::uint32_t txFlags = lsfSellNFToken);
|
||||
|
||||
TER
|
||||
checkTrustlineAuthorized(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue);
|
||||
|
||||
TER
|
||||
checkTrustlineDeepFrozen(ReadView const& view, AccountID const id, beast::Journal const j, Issue const& issue);
|
||||
|
||||
} // namespace nft
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/Offer/CancelOffer.h
Normal file
29
include/xrpl/tx/transactors/Offer/CancelOffer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CancelOffer : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CancelOffer(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using OfferCancel = CancelOffer;
|
||||
|
||||
} // namespace xrpl
|
||||
79
include/xrpl/tx/transactors/Offer/CreateOffer.h
Normal file
79
include/xrpl/tx/transactors/Offer/CreateOffer.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PaymentSandbox;
|
||||
class Sandbox;
|
||||
|
||||
/** Transactor specialized for creating offers in the ledger. */
|
||||
class CreateOffer : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
/** Construct a Transactor subclass that creates an offer in the ledger. */
|
||||
explicit CreateOffer(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Precondition: fee collection is likely. Attempt to create the offer. */
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
applyGuts(Sandbox& view, Sandbox& view_cancel);
|
||||
|
||||
// Determine if we are authorized to hold the asset we want to get.
|
||||
static TER
|
||||
checkAcceptAsset(
|
||||
ReadView const& view,
|
||||
ApplyFlags const flags,
|
||||
AccountID const id,
|
||||
beast::Journal const j,
|
||||
Issue const& issue);
|
||||
|
||||
// Use the payment flow code to perform offer crossing.
|
||||
std::pair<TER, Amounts>
|
||||
flowCross(
|
||||
PaymentSandbox& psb,
|
||||
PaymentSandbox& psbCancel,
|
||||
Amounts const& takerAmount,
|
||||
std::optional<uint256> const& domainID);
|
||||
|
||||
static std::string
|
||||
format_amount(STAmount const& amount);
|
||||
|
||||
TER
|
||||
applyHybrid(
|
||||
Sandbox& sb,
|
||||
std::shared_ptr<STLedgerEntry> sleOffer,
|
||||
Keylet const& offer_index,
|
||||
STAmount const& saTakerPays,
|
||||
STAmount const& saTakerGets,
|
||||
std::function<void(SLE::ref, std::optional<uint256>)> const& setDir);
|
||||
};
|
||||
|
||||
using OfferCreate = CreateOffer;
|
||||
|
||||
} // namespace xrpl
|
||||
83
include/xrpl/tx/transactors/PayChan.h
Normal file
83
include/xrpl/tx/transactors/PayChan.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PayChanCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelCreate = PayChanCreate;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class PayChanFund : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit PayChanFund(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelFund = PayChanFund;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class PayChanClaim : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit PayChanClaim(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using PaymentChannelClaim = PayChanClaim;
|
||||
|
||||
} // namespace xrpl
|
||||
44
include/xrpl/tx/transactors/Payment.h
Normal file
44
include/xrpl/tx/transactors/Payment.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Payment : public Transactor
|
||||
{
|
||||
/* The largest number of paths we allow */
|
||||
static std::size_t const MaxPathSize = 6;
|
||||
|
||||
/* The longest path we allow */
|
||||
static std::size_t const MaxPathLength = 8;
|
||||
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit Payment(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
17
include/xrpl/tx/transactors/PermissionedDEXHelpers.h
Normal file
17
include/xrpl/tx/transactors/PermissionedDEXHelpers.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <xrpl/ledger/View.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace permissioned_dex {
|
||||
|
||||
// Check if an account is in a permissioned domain
|
||||
[[nodiscard]] bool
|
||||
accountInDomain(ReadView const& view, AccountID const& account, Domain const& domainID);
|
||||
|
||||
// Check if an offer is in the permissioned domain
|
||||
[[nodiscard]] bool
|
||||
offerInDomain(ReadView const& view, uint256 const& offerID, Domain const& domainID, beast::Journal j);
|
||||
|
||||
} // namespace permissioned_dex
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PermissionedDomainDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit PermissionedDomainDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Attempt to delete the Permissioned Domain. */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class PermissionedDomainSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit PermissionedDomainSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Attempt to create the Permissioned Domain. */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/tx/transactors/SetAccount.h
Normal file
38
include/xrpl/tx/transactors/SetAccount.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SetAccount : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit SetAccount(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using AccountSet = SetAccount;
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/tx/transactors/SetOracle.h
Normal file
37
include/xrpl/tx/transactors/SetOracle.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
Price Oracle is a system that acts as a bridge between
|
||||
a blockchain network and the external world, providing off-chain price data
|
||||
to decentralized applications (dApps) on the blockchain. This implementation
|
||||
conforms to the requirements specified in the XLS-47d.
|
||||
|
||||
The SetOracle transactor implements creating or updating Oracle objects.
|
||||
*/
|
||||
|
||||
class SetOracle : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit SetOracle(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using OracleSet = SetOracle;
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/SetRegularKey.h
Normal file
26
include/xrpl/tx/transactors/SetRegularKey.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SetRegularKey : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit SetRegularKey(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
71
include/xrpl/tx/transactors/SetSignerList.h
Normal file
71
include/xrpl/tx/transactors/SetSignerList.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/tx/SignerEntries.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
See the README.md for an overview of the SetSignerList transaction that
|
||||
this class implements.
|
||||
*/
|
||||
class SetSignerList : public Transactor
|
||||
{
|
||||
private:
|
||||
// Values determined during preCompute for use later.
|
||||
enum Operation { unknown, set, destroy };
|
||||
Operation do_{unknown};
|
||||
std::uint32_t quorum_{0};
|
||||
std::vector<SignerEntries::SignerEntry> signers_;
|
||||
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit SetSignerList(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
void
|
||||
preCompute() override;
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
removeFromLedger(ServiceRegistry& registry, ApplyView& view, AccountID const& account, beast::Journal j);
|
||||
|
||||
private:
|
||||
static std::tuple<NotTEC, std::uint32_t, std::vector<SignerEntries::SignerEntry>, Operation>
|
||||
determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j);
|
||||
|
||||
static NotTEC
|
||||
validateQuorumAndSignerEntries(
|
||||
std::uint32_t quorum,
|
||||
std::vector<SignerEntries::SignerEntry> const& signers,
|
||||
AccountID const& account,
|
||||
beast::Journal j,
|
||||
Rules const&);
|
||||
|
||||
TER
|
||||
replaceSignerList();
|
||||
TER
|
||||
destroySignerList();
|
||||
|
||||
void
|
||||
writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t flags) const;
|
||||
};
|
||||
|
||||
using SignerListSet = SetSignerList;
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/SetTrust.h
Normal file
35
include/xrpl/tx/transactors/SetTrust.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SetTrust : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit SetTrust(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using TrustSet = SetTrust;
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/Vault/VaultClawback.h
Normal file
34
include/xrpl/tx/transactors/Vault/VaultClawback.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
Expected<std::pair<STAmount, STAmount>, TER>
|
||||
assetsToClawback(
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
std::shared_ptr<SLE const> const& sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
32
include/xrpl/tx/transactors/Vault/VaultCreate.h
Normal file
32
include/xrpl/tx/transactors/Vault/VaultCreate.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/Vault/VaultDelete.h
Normal file
26
include/xrpl/tx/transactors/Vault/VaultDelete.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/Vault/VaultDeposit.h
Normal file
26
include/xrpl/tx/transactors/Vault/VaultDeposit.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultDeposit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultDeposit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
32
include/xrpl/tx/transactors/Vault/VaultSet.h
Normal file
32
include/xrpl/tx/transactors/Vault/VaultSet.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
26
include/xrpl/tx/transactors/Vault/VaultWithdraw.h
Normal file
26
include/xrpl/tx/transactors/Vault/VaultWithdraw.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class VaultWithdraw : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit VaultWithdraw(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
239
include/xrpl/tx/transactors/XChainBridge.h
Normal file
239
include/xrpl/tx/transactors/XChainBridge.h
Normal file
@@ -0,0 +1,239 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/XChainAttestations.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
constexpr size_t xbridgeMaxAccountCreateClaims = 128;
|
||||
|
||||
// Attach a new bridge to a door account. Once this is done, the cross-chain
|
||||
// transfer transactions may be used to transfer funds from this account.
|
||||
class XChainCreateBridge : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateBridge(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
class BridgeModify : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit BridgeModify(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using XChainModifyBridge = BridgeModify;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Claim funds from a `XChainCommit` transaction. This is normally not needed,
|
||||
// but may be used to handle transaction failures or if the destination account
|
||||
// was not specified in the `XChainCommit` transaction. It may only be used
|
||||
// after a quorum of signatures have been sent from the witness servers.
|
||||
//
|
||||
// If the transaction succeeds in moving funds, the referenced `XChainClaimID`
|
||||
// ledger object will be destroyed. This prevents transaction replay. If the
|
||||
// transaction fails, the `XChainClaimID` will not be destroyed and the
|
||||
// transaction may be re-run with different parameters.
|
||||
class XChainClaim : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainClaim(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Put assets into trust on the locking-chain so they may be wrapped on the
|
||||
// issuing-chain, or return wrapped assets on the issuing-chain so they can be
|
||||
// unlocked on the locking-chain. The second step in a cross-chain transfer.
|
||||
class XChainCommit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
explicit XChainCommit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Create a new claim id owned by the account. This is the first step in a
|
||||
// cross-chain transfer. The claim id must be created on the destination chain
|
||||
// before the `XChainCommit` transaction (which must reference this number) can
|
||||
// be sent on the source chain. The account that will send the `XChainCommit` on
|
||||
// the source chain must be specified in this transaction (see note on the
|
||||
// `SourceAccount` field in the `XChainClaimID` ledger object for
|
||||
// justification). The actual sequence number must be retrieved from a validated
|
||||
// ledger.
|
||||
class XChainCreateClaimID : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateClaimID(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Provide attestations from a witness server attesting to events on
|
||||
// the other chain. The signatures must be from one of the keys on the door's
|
||||
// signer's list at the time the signature was provided. However, if the
|
||||
// signature list changes between the time the signature was submitted and the
|
||||
// quorum is reached, the new signature set is used and some of the currently
|
||||
// collected signatures may be removed. Also note the reward is only sent to
|
||||
// accounts that have keys on the current list.
|
||||
class XChainAddClaimAttestation : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainAddClaimAttestation(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
class XChainAddAccountCreateAttestation : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainAddAccountCreateAttestation(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This is a special transaction used for creating accounts through a
|
||||
// cross-chain transfer. A normal cross-chain transfer requires a "chain claim
|
||||
// id" (which requires an existing account on the destination chain). One
|
||||
// purpose of the "chain claim id" is to prevent transaction replay. For this
|
||||
// transaction, we use a different mechanism: the accounts must be claimed on
|
||||
// the destination chain in the same order that the `XChainCreateAccountCommit`
|
||||
// transactions occurred on the source chain.
|
||||
//
|
||||
// This transaction can only be used for XRP to XRP bridges.
|
||||
//
|
||||
// IMPORTANT: This transaction should only be enabled if the witness
|
||||
// attestations will be reliably delivered to the destination chain. If the
|
||||
// signatures are not delivered (for example, the chain relies on user wallets
|
||||
// to collect signatures) then account creation would be blocked for all
|
||||
// transactions that happened after the one waiting on attestations. This could
|
||||
// be used maliciously. To disable this transaction on XRP to XRP bridges, the
|
||||
// bridge's `MinAccountCreateAmount` should not be present.
|
||||
//
|
||||
// Note: If this account already exists, the XRP is transferred to the existing
|
||||
// account. However, note that unlike the `XChainCommit` transaction, there is
|
||||
// no error handling mechanism. If the claim transaction fails, there is no
|
||||
// mechanism for refunds. The funds are permanently lost. This transaction
|
||||
// should still only be used for account creation.
|
||||
class XChainCreateAccountCommit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateAccountCommit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using XChainAccountCreateCommit = XChainCreateAccountCommit;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
Reference in New Issue
Block a user