mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-11 22:42:24 +00:00
Compare commits
111 Commits
bthomee/pi
...
ripple/con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5229ff5a45 | ||
|
|
6a8016f288 | ||
|
|
5d38603f90 | ||
|
|
6f4fddc1ae | ||
|
|
4ae17f193f | ||
|
|
56c9d1d497 | ||
|
|
d52dd29d20 | ||
|
|
36c805692a | ||
|
|
7793b5f10b | ||
|
|
5bd7ce1dfd | ||
|
|
dfcad69155 | ||
|
|
0d7bf6948e | ||
|
|
6d1a5be8d2 | ||
|
|
58e38e05f9 | ||
|
|
697786afab | ||
|
|
834cb72515 | ||
|
|
261e7d7769 | ||
|
|
042b09563b | ||
|
|
cc2d6cbff6 | ||
|
|
6f1232140b | ||
|
|
13b3a243a3 | ||
|
|
2472ef7df7 | ||
|
|
b34ecc476a | ||
|
|
4c0e6012e3 | ||
|
|
3f749ecf76 | ||
|
|
a43cf94ff7 | ||
|
|
9f4cf28aea | ||
|
|
c45177b69e | ||
|
|
d615098849 | ||
|
|
eeb0d15ea9 | ||
|
|
84cc8599af | ||
|
|
36c1c5f3cd | ||
|
|
2c94e213c2 | ||
|
|
cbcc75ff2d | ||
|
|
da7698974c | ||
|
|
a3a82faa3d | ||
|
|
fff83c1d4a | ||
|
|
803ab67fc5 | ||
|
|
734b11c0e3 | ||
|
|
fae34d0f36 | ||
|
|
c52d317810 | ||
|
|
c2f8b91397 | ||
|
|
1ea9312946 | ||
|
|
6ad60d7141 | ||
|
|
94e911ed69 | ||
|
|
b2c434dd73 | ||
|
|
b6d1a8d62b | ||
|
|
9d0c854139 | ||
|
|
3a6ca681ff | ||
|
|
a216824c15 | ||
|
|
90cf86a920 | ||
|
|
e69d3c9bd7 | ||
|
|
fd390a4f1c | ||
|
|
3941283438 | ||
|
|
86af28d91d | ||
|
|
41f7102fb8 | ||
|
|
66ed0fa452 | ||
|
|
cad8fb328a | ||
|
|
31346425f0 | ||
|
|
40bfaa25d2 | ||
|
|
c4916f1251 | ||
|
|
fc8b7898c5 | ||
|
|
446f9fbe6d | ||
|
|
1297385b7e | ||
|
|
114adc0c57 | ||
|
|
1d349c32c5 | ||
|
|
a5f20c129d | ||
|
|
75d143a2a0 | ||
|
|
e3da98e310 | ||
|
|
ec6d7cb91d | ||
|
|
fa055c2bd5 | ||
|
|
6c38086f17 | ||
|
|
3e9dc276ed | ||
|
|
abf7a62b1f | ||
|
|
bd3a6e1631 | ||
|
|
7c0bd419a4 | ||
|
|
d3126959e7 | ||
|
|
67e8e89e0f | ||
|
|
4e4326a174 | ||
|
|
5397bd6d6e | ||
|
|
6dece25cc3 | ||
|
|
d9da8733be | ||
|
|
f6f51451e7 | ||
|
|
b94c95b3e9 | ||
|
|
8365148b5c | ||
|
|
c03866bf0f | ||
|
|
389afc5f06 | ||
|
|
7b04eaae81 | ||
|
|
1343019509 | ||
|
|
cd75e630a2 | ||
|
|
ec57fbdc5f | ||
|
|
4fe67f5715 | ||
|
|
44d885e39b | ||
|
|
3af758145c | ||
|
|
f3d4d4341b | ||
|
|
ddb518ad09 | ||
|
|
3899e3f36c | ||
|
|
e4a8ba51f9 | ||
|
|
35e4fad557 | ||
|
|
8e9cb3c1da | ||
|
|
18d92058e3 | ||
|
|
f24d584f29 | ||
|
|
da3fbcd25b | ||
|
|
daa1303b5a | ||
|
|
a636fe5871 | ||
|
|
bbc3071fd1 | ||
|
|
8fdc639206 | ||
|
|
5a89641d98 | ||
|
|
beefa248a6 | ||
|
|
e919a25ecb | ||
|
|
c3fdbc0430 |
@@ -88,6 +88,7 @@ find_package(ed25519 REQUIRED)
|
||||
find_package(gRPC REQUIRED)
|
||||
find_package(LibArchive REQUIRED)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(mpt-crypto REQUIRED)
|
||||
find_package(nudb REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
@@ -100,6 +101,7 @@ target_link_libraries(
|
||||
INTERFACE
|
||||
ed25519::ed25519
|
||||
lz4::lz4
|
||||
mpt-crypto::mpt-crypto
|
||||
OpenSSL::Crypto
|
||||
OpenSSL::SSL
|
||||
secp256k1::secp256k1
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
|
||||
"openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139",
|
||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384",
|
||||
"mpt-crypto/0.2.0-rc1#ed3f241f69d8b9ebf80069d1923d93a8%1773853481.755",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
||||
@@ -34,6 +35,7 @@
|
||||
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
|
||||
"m4/1.4.19#5d7a4994e5875d76faf7acf3ed056036%1774365463.87",
|
||||
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
|
||||
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1765850153.479",
|
||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
|
||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||
@@ -58,6 +60,12 @@
|
||||
],
|
||||
"lz4/[>=1.9.4 <2]": [
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
|
||||
],
|
||||
"openssl/3.5.5": [
|
||||
"openssl/3.6.1"
|
||||
],
|
||||
"openssl/[>=3 <4]": [
|
||||
"openssl/3.6.1"
|
||||
]
|
||||
},
|
||||
"config_requires": []
|
||||
|
||||
@@ -31,6 +31,7 @@ class Xrpl(ConanFile):
|
||||
"ed25519/2015.03",
|
||||
"grpc/1.78.1",
|
||||
"libarchive/3.8.1",
|
||||
"mpt-crypto/0.2.0-rc1",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.1",
|
||||
"secp256k1/0.7.1",
|
||||
@@ -214,6 +215,7 @@ class Xrpl(ConanFile):
|
||||
"grpc::grpc++",
|
||||
"libarchive::libarchive",
|
||||
"lz4::lz4",
|
||||
"mpt-crypto::mpt-crypto",
|
||||
"nudb::nudb",
|
||||
"openssl::crypto",
|
||||
"protobuf::libprotobuf",
|
||||
|
||||
@@ -44,6 +44,10 @@ suggestWords:
|
||||
words:
|
||||
- abempty
|
||||
- AMMID
|
||||
- AMMMPT
|
||||
- AMMMPToken
|
||||
- AMMMPTokens
|
||||
- AMMXRP
|
||||
- amt
|
||||
- amts
|
||||
- asnode
|
||||
@@ -55,6 +59,7 @@ words:
|
||||
- autobridging
|
||||
- bimap
|
||||
- bindir
|
||||
- blindings
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- Britto
|
||||
@@ -87,6 +92,7 @@ words:
|
||||
- daria
|
||||
- dcmake
|
||||
- dearmor
|
||||
- decryptor
|
||||
- deleteme
|
||||
- demultiplexer
|
||||
- deserializaton
|
||||
@@ -96,6 +102,8 @@ words:
|
||||
- distro
|
||||
- doxyfile
|
||||
- dxrpl
|
||||
- elgamal
|
||||
- enabled
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
@@ -105,6 +113,7 @@ words:
|
||||
- fmtdur
|
||||
- fsanitize
|
||||
- funclets
|
||||
- Gamal
|
||||
- gcov
|
||||
- gcovr
|
||||
- ghead
|
||||
@@ -148,6 +157,8 @@ words:
|
||||
- ltype
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
- Merkle
|
||||
- Metafuncton
|
||||
- misprediction
|
||||
@@ -157,6 +168,7 @@ words:
|
||||
- mptid
|
||||
- mptissuance
|
||||
- mptissuanceid
|
||||
- mptissue
|
||||
- mptoken
|
||||
- mptokenid
|
||||
- mptokenissuance
|
||||
@@ -191,6 +203,7 @@ words:
|
||||
- partitioner
|
||||
- paychan
|
||||
- paychans
|
||||
- Pedersen
|
||||
- permdex
|
||||
- perminute
|
||||
- permissioned
|
||||
@@ -227,6 +240,7 @@ words:
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- scons
|
||||
- Schnorr
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
@@ -254,6 +268,7 @@ words:
|
||||
- stvar
|
||||
- stvector
|
||||
- stxchainattestations
|
||||
- summands
|
||||
- superpeer
|
||||
- superpeers
|
||||
- takergets
|
||||
|
||||
@@ -213,11 +213,60 @@ public:
|
||||
// Called when a credit is made to an account
|
||||
// This is required to support PaymentSandbox
|
||||
virtual void
|
||||
creditHook(
|
||||
creditHookIOU(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditBalance)
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "creditHookIOU: amount is for Issue");
|
||||
}
|
||||
|
||||
virtual void
|
||||
creditHookMPT(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer)
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<MPTIssue>(), "creditHookMPT: amount is for MPTIssue");
|
||||
}
|
||||
|
||||
/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer.
|
||||
* Unlike IOU, MPT doesn't have bi-directional relationship with an issuer,
|
||||
* where a trustline limits an amount that can be issued to a holder.
|
||||
* Consequently, the credit step (last MPTEndpointStep or
|
||||
* BookStep buying MPT) might temporarily overflow OutstandingAmount.
|
||||
* Limiting of a step's output amount in this case is delegated to
|
||||
* the next step (in rev order). The next step always redeems when a holder
|
||||
* account sells MPT (first MPTEndpointStep or BookStep selling MPT).
|
||||
* In this case the holder account is only limited by the step's output
|
||||
* and it's available funds since it's transferring the funds from one
|
||||
* account to another account and doesn't change OutstandingAmount.
|
||||
* This doesn't apply to an offer owned by an issuer.
|
||||
* In this case the issuer sells or self debits and is increasing
|
||||
* OutstandingAmount. Ability to issue is limited by the issuer
|
||||
* originally available funds less already self sold MPT amounts (MPT sell
|
||||
* offer).
|
||||
* Consider an example:
|
||||
* - GW creates MPT(USD) with 1,000USD MaximumAmount.
|
||||
* - GW pays 950USD to A1.
|
||||
* - A1 creates an offer 100XRP(buy)/100USD(sell).
|
||||
* - GW creates an offer 100XRP(buy)/100USD(sell).
|
||||
* - A2 pays 200USD to A3 with sendMax of 200XRP.
|
||||
* Since the payment engine executes payments in reverse,
|
||||
* OutstandingAmount overflows in MPTEndpointStep: 950 + 200 = 1,150USD.
|
||||
* BookStep first consumes A1 offer. This reduces OutstandingAmount
|
||||
* by 100USD: 1,150 - 100 = 1,050USD. GW offer can only be partially
|
||||
* consumed because the initial available amount is 50USD = 1,000 - 950.
|
||||
* BookStep limits it's output to 150USD. This in turn limits A3's send
|
||||
* amount to 150XRP: A1 buys 100XRP and sells 100USD to A3. This doesn't
|
||||
* change OutstandingAmount. GW buys 50XRP and sells 50USD to A3. This
|
||||
* changes OutstandingAmount to 1,000USD.
|
||||
*/
|
||||
virtual void
|
||||
issuerSelfDebitHookMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <xrpl/ledger/AcceptedLedgerTx.h>
|
||||
#include <xrpl/ledger/BookListeners.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
@@ -53,30 +53,30 @@ public:
|
||||
issue. This is useful for pathfinding to find all possible next hops
|
||||
from a given currency.
|
||||
|
||||
@param issue The issue to search for
|
||||
@param asset The asset to search for
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return Vector of books that want this issue
|
||||
*/
|
||||
virtual std::vector<Book>
|
||||
getBooksByTakerPays(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
getBooksByTakerPays(Asset const& asset, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
|
||||
/** Get the count of order books that want a specific issue.
|
||||
|
||||
@param issue The issue to search for
|
||||
@param asset The asset to search for
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return Number of books that want this issue
|
||||
*/
|
||||
virtual int
|
||||
getBookSize(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
getBookSize(Asset const& asset, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
|
||||
/** Check if an order book to XRP exists for the given issue.
|
||||
|
||||
@param issue The issue to check
|
||||
@param asset The asset to check
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return true if a book from this issue to XRP exists
|
||||
*/
|
||||
virtual bool
|
||||
isBookToXRP(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
isBookToXRP(Asset const& asset, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
|
||||
/**
|
||||
* Process a transaction for order book tracking.
|
||||
|
||||
@@ -15,10 +15,55 @@ namespace detail {
|
||||
// into the PaymentSandbox class itself
|
||||
class DeferredCredits
|
||||
{
|
||||
public:
|
||||
struct Adjustment
|
||||
private:
|
||||
using KeyIOU = std::tuple<AccountID, AccountID, Currency>;
|
||||
struct ValueIOU
|
||||
{
|
||||
Adjustment(STAmount const& d, STAmount const& c, STAmount const& b)
|
||||
explicit ValueIOU() = default;
|
||||
|
||||
STAmount lowAcctCredits;
|
||||
STAmount highAcctCredits;
|
||||
STAmount lowAcctOrigBalance;
|
||||
};
|
||||
|
||||
struct HolderValueMPT
|
||||
{
|
||||
HolderValueMPT() = default;
|
||||
// Debit to issuer
|
||||
std::uint64_t debit = 0;
|
||||
std::uint64_t origBalance = 0;
|
||||
};
|
||||
|
||||
struct IssuerValueMPT
|
||||
{
|
||||
IssuerValueMPT() = default;
|
||||
std::map<AccountID, HolderValueMPT> holders;
|
||||
// Credit to holder
|
||||
std::uint64_t credit = 0;
|
||||
// OutstandingAmount might overflow when MPTs are credited to a holder.
|
||||
// Consider A1 paying 100MPT to A2 and A1 already having maximum MPTs.
|
||||
// Since the payment engine executes a payment in revers, A2 is
|
||||
// credited first and OutstandingAmount is going to be equal
|
||||
// to MaximumAmount + 100MPT. In the next step A1 redeems 100MPT
|
||||
// to the issuer and OutstandingAmount balances out.
|
||||
std::int64_t origBalance = 0;
|
||||
// Self debit on offer selling MPT. Since the payment engine executes
|
||||
// a payment in reverse, a crediting/buying step may overflow
|
||||
// OutstandingAmount. A sell MPT offer owned by a holder can redeem any
|
||||
// amount up to the offer's amount and holder's available funds,
|
||||
// balancing out OutstandingAmount. But if the offer's owner is issuer
|
||||
// then it issues more MPT. In this case the available amount to issue
|
||||
// is the initial issuer's available amount less all offer sell amounts
|
||||
// by the issuer. This is self-debit, where the offer's owner,
|
||||
// issuer in this case, debits to self.
|
||||
std::uint64_t selfDebit = 0;
|
||||
};
|
||||
using AdjustmentMPT = IssuerValueMPT;
|
||||
|
||||
public:
|
||||
struct AdjustmentIOU
|
||||
{
|
||||
AdjustmentIOU(STAmount const& d, STAmount const& c, STAmount const& b)
|
||||
: debits(d), credits(c), origBalance(b)
|
||||
{
|
||||
}
|
||||
@@ -29,16 +74,30 @@ public:
|
||||
|
||||
// Get the adjustments for the balance between main and other.
|
||||
// Returns the debits, credits and the original balance
|
||||
std::optional<Adjustment>
|
||||
adjustments(AccountID const& main, AccountID const& other, Currency const& currency) const;
|
||||
std::optional<AdjustmentIOU>
|
||||
adjustmentsIOU(AccountID const& main, AccountID const& other, Currency const& currency) const;
|
||||
|
||||
std::optional<AdjustmentMPT>
|
||||
adjustmentsMPT(MPTID const& mptID) const;
|
||||
|
||||
void
|
||||
credit(
|
||||
creditIOU(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditSenderBalance);
|
||||
|
||||
void
|
||||
creditMPT(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer);
|
||||
|
||||
void
|
||||
issuerSelfDebitMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance);
|
||||
|
||||
void
|
||||
ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next);
|
||||
|
||||
@@ -52,21 +111,11 @@ public:
|
||||
apply(DeferredCredits& to);
|
||||
|
||||
private:
|
||||
// lowAccount, highAccount
|
||||
using Key = std::tuple<AccountID, AccountID, Currency>;
|
||||
struct Value
|
||||
{
|
||||
explicit Value() = default;
|
||||
static KeyIOU
|
||||
makeKeyIOU(AccountID const& a1, AccountID const& a2, Currency const& currency);
|
||||
|
||||
STAmount lowAcctCredits;
|
||||
STAmount highAcctCredits;
|
||||
STAmount lowAcctOrigBalance;
|
||||
};
|
||||
|
||||
static Key
|
||||
makeKey(AccountID const& a1, AccountID const& a2, Currency const& c);
|
||||
|
||||
std::map<Key, Value> credits_;
|
||||
std::map<KeyIOU, ValueIOU> creditsIOU_;
|
||||
std::map<MPTID, IssuerValueMPT> creditsMPT_;
|
||||
std::map<AccountID, std::uint32_t> ownerCounts_;
|
||||
};
|
||||
|
||||
@@ -131,16 +180,35 @@ public:
|
||||
/** @} */
|
||||
|
||||
STAmount
|
||||
balanceHook(AccountID const& account, AccountID const& issuer, STAmount const& amount)
|
||||
balanceHookIOU(AccountID const& account, AccountID const& issuer, STAmount const& amount)
|
||||
const override;
|
||||
|
||||
STAmount
|
||||
balanceHookMPT(AccountID const& account, MPTIssue const& issue, std::int64_t amount)
|
||||
const override;
|
||||
|
||||
STAmount
|
||||
balanceHookSelfIssueMPT(MPTIssue const& issue, std::int64_t amount) const override;
|
||||
|
||||
void
|
||||
creditHook(
|
||||
creditHookIOU(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditBalance) override;
|
||||
|
||||
void
|
||||
creditHookMPT(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer) override;
|
||||
|
||||
void
|
||||
issuerSelfDebitHookMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance)
|
||||
override;
|
||||
|
||||
void
|
||||
adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) override;
|
||||
|
||||
|
||||
@@ -148,15 +148,35 @@ public:
|
||||
|
||||
// Accounts in a payment are not allowed to use assets acquired during that
|
||||
// payment. The PaymentSandbox tracks the debits, credits, and owner count
|
||||
// changes that accounts make during a payment. `balanceHook` adjusts
|
||||
// changes that accounts make during a payment. `balanceHookIOU` adjusts
|
||||
// balances so newly acquired assets are not counted toward the balance.
|
||||
// This is required to support PaymentSandbox.
|
||||
virtual STAmount
|
||||
balanceHook(AccountID const& account, AccountID const& issuer, STAmount const& amount) const
|
||||
balanceHookIOU(AccountID const& account, AccountID const& issuer, STAmount const& amount) const
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "balanceHookIOU: amount is for Issue");
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
// balanceHookMPT adjusts balances so newly acquired assets are not counted
|
||||
// toward the balance.
|
||||
virtual STAmount
|
||||
balanceHookMPT(AccountID const& account, MPTIssue const& issue, std::int64_t amount) const
|
||||
{
|
||||
return STAmount{issue, amount};
|
||||
}
|
||||
|
||||
// An offer owned by an issuer and selling MPT is limited by the issuer's
|
||||
// funds available to issue, which are originally available funds less
|
||||
// already self sold MPT amounts (MPT sell offer). This hook is used
|
||||
// by issuerFundsToSelfIssue() function.
|
||||
virtual STAmount
|
||||
balanceHookSelfIssueMPT(MPTIssue const& issue, std::int64_t amount) const
|
||||
{
|
||||
return STAmount{issue, amount};
|
||||
}
|
||||
|
||||
// Accounts in a payment are not allowed to use assets acquired during that
|
||||
// payment. The PaymentSandbox tracks the debits, credits, and owner count
|
||||
// changes that accounts make during a payment. `ownerCountHook` adjusts the
|
||||
|
||||
@@ -63,8 +63,8 @@ isVaultPseudoAccountFrozen(
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& asset,
|
||||
Issue const& asset2);
|
||||
Asset const& asset,
|
||||
Asset const& asset2);
|
||||
|
||||
// Return the list of enabled amendments
|
||||
[[nodiscard]] std::set<uint256>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -11,6 +16,7 @@
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -36,7 +42,7 @@ enum class IsDeposit : bool { No = false, Yes = true };
|
||||
* @return LP Tokens as IOU
|
||||
*/
|
||||
STAmount
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Issue const& lptIssue);
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Asset const& lptIssue);
|
||||
|
||||
/** Calculate LP Tokens given asset's deposit amount.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
@@ -124,7 +130,8 @@ withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Nu
|
||||
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>)
|
||||
std::is_same_v<Amt, XRPAmount> || std::is_same_v<Amt, MPTAmount> ||
|
||||
std::is_same_v<Amt, Number>)
|
||||
bool
|
||||
withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist)
|
||||
{
|
||||
@@ -195,7 +202,7 @@ getAMMOfferStartWithTakerGets(
|
||||
// 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);
|
||||
toAmount<TOut>(getAsset(pool.out), nTakerGetsProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{swapAssetOut(pool, takerGets, tfee), takerGets};
|
||||
};
|
||||
|
||||
@@ -262,7 +269,7 @@ getAMMOfferStartWithTakerPays(
|
||||
// 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);
|
||||
toAmount<TIn>(getAsset(pool.in), nTakerPaysProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
};
|
||||
|
||||
@@ -331,7 +338,7 @@ changeSpotPriceQuality(
|
||||
<< " " << to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
|
||||
auto const takerPays = toAmount<TIn>(getAsset(pool.in), nTakerPays, Number::upward);
|
||||
// should not fail
|
||||
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
Quality{amounts} < quality &&
|
||||
@@ -360,7 +367,7 @@ changeSpotPriceQuality(
|
||||
// Generate the offer starting with XRP side. Return seated offer amounts
|
||||
// if the offer can be generated, otherwise nullopt.
|
||||
auto amounts = [&]() {
|
||||
if (isXRP(getIssue(pool.out)))
|
||||
if (isXRP(getAsset(pool.out)))
|
||||
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
|
||||
return getAMMOfferStartWithTakerPays(pool, quality, tfee);
|
||||
}();
|
||||
@@ -445,7 +452,7 @@ swapAssetIn(TAmounts<TIn, TOut> const& pool, TIn const& assetIn, std::uint16_t t
|
||||
auto const denom = pool.in + assetIn * (1 - fee);
|
||||
|
||||
if (denom.signum() <= 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
return toAmount<TOut>(getAsset(pool.out), 0);
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const ratio = numerator / denom;
|
||||
@@ -454,14 +461,14 @@ swapAssetIn(TAmounts<TIn, TOut> const& pool, TIn const& assetIn, std::uint16_t t
|
||||
auto const swapOut = pool.out - ratio;
|
||||
|
||||
if (swapOut.signum() < 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
return toAmount<TOut>(getAsset(pool.out), 0);
|
||||
|
||||
return toAmount<TOut>(getIssue(pool.out), swapOut, Number::downward);
|
||||
return toAmount<TOut>(getAsset(pool.out), swapOut, Number::downward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TOut>(
|
||||
getIssue(pool.out),
|
||||
getAsset(pool.out),
|
||||
pool.out - (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
Number::downward);
|
||||
}
|
||||
@@ -508,7 +515,7 @@ swapAssetOut(TAmounts<TIn, TOut> const& pool, TOut const& assetOut, std::uint16_
|
||||
auto const denom = pool.out - assetOut;
|
||||
if (denom.signum() <= 0)
|
||||
{
|
||||
return toMaxAmount<TIn>(getIssue(pool.in));
|
||||
return toMaxAmount<TIn>(getAsset(pool.in));
|
||||
}
|
||||
|
||||
Number::setround(Number::upward);
|
||||
@@ -522,14 +529,14 @@ swapAssetOut(TAmounts<TIn, TOut> const& pool, TOut const& assetOut, std::uint16_
|
||||
Number::setround(Number::upward);
|
||||
auto const swapIn = numerator2 / feeMult;
|
||||
if (swapIn.signum() < 0)
|
||||
return toAmount<TIn>(getIssue(pool.in), 0);
|
||||
return toAmount<TIn>(getAsset(pool.in), 0);
|
||||
|
||||
return toAmount<TIn>(getIssue(pool.in), swapIn, Number::upward);
|
||||
return toAmount<TIn>(getAsset(pool.in), swapIn, Number::upward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TIn>(
|
||||
getIssue(pool.in),
|
||||
getAsset(pool.in),
|
||||
((pool.in * pool.out) / (pool.out - assetOut) - pool.in) / feeMult(tfee),
|
||||
Number::upward);
|
||||
}
|
||||
@@ -616,9 +623,9 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
{
|
||||
if constexpr (std::is_same_v<A, STAmount>)
|
||||
return multiply(balance, frac, balance.issue());
|
||||
return multiply(balance, frac, balance.asset());
|
||||
else
|
||||
return toSTAmount(balance.issue(), balance * frac);
|
||||
return toSTAmount(balance.asset(), balance * frac);
|
||||
}
|
||||
auto const rm = detail::getAssetRounding(isDeposit);
|
||||
return multiply(balance, frac, rm);
|
||||
@@ -712,4 +719,94 @@ adjustFracByTokens(
|
||||
STAmount const& tokens,
|
||||
Number const& frac);
|
||||
|
||||
/** Get AMM pool balances.
|
||||
*/
|
||||
std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
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<Asset> const& optAsset1,
|
||||
std::optional<Asset> const& optAsset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get the balance of LP tokens.
|
||||
*/
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
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, Asset const& asset);
|
||||
|
||||
/** Delete trustlines to AMM. If all trustlines are deleted then
|
||||
* AMM object and account are deleted. Otherwise tecINCOMPLETE is returned.
|
||||
*/
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& view, Asset const& asset, Asset 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,
|
||||
Asset const& lptAsset,
|
||||
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
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.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
|
||||
@@ -41,7 +41,8 @@ escrowUnlockApplyHelper<Issue>(
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
Keylet const trustLineKey = keylet::line(receiver, amount.issue());
|
||||
Issue const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::line(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
@@ -64,9 +65,9 @@ escrowUnlockApplyHelper<Issue>(
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
}
|
||||
|
||||
Currency const currency = amount.getCurrency();
|
||||
STAmount initialBalance(amount.issue());
|
||||
initialBalance.setIssuer(noAccount());
|
||||
Currency const currency = issue.currency;
|
||||
STAmount initialBalance(issue);
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
if (TER const ter = trustCreate(
|
||||
view, // payment sandbox
|
||||
@@ -113,7 +114,8 @@ escrowUnlockApplyHelper<Issue>(
|
||||
if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
|
||||
{
|
||||
// compute transfer fee, if any
|
||||
auto const xferFee = amount.value() - divideRound(amount, lockedRate, amount.issue(), true);
|
||||
auto const xferFee =
|
||||
amount.value() - divideRound(amount, lockedRate, amount.get<Issue>(), true);
|
||||
// compute balance to transfer
|
||||
finalAmt = amount.value() - xferFee;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ authorizeMPToken(
|
||||
* requireAuth check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
* WeakAuth intentionally allows missing MPTokens under MPToken V2.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
@@ -114,6 +115,12 @@ canTransfer(
|
||||
AccountID const& from,
|
||||
AccountID const& to);
|
||||
|
||||
/** Check if Asset can be traded on DEX. return tecNO_PERMISSION
|
||||
* if it doesn't and tesSUCCESS otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTrade(ReadView const& view, Asset const& asset);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (MPT-specific)
|
||||
@@ -164,4 +171,73 @@ createMPToken(
|
||||
AccountID const& account,
|
||||
std::uint32_t const flags);
|
||||
|
||||
TER
|
||||
checkCreateMPT(
|
||||
xrpl::ApplyView& view,
|
||||
xrpl::MPTIssue const& mptIssue,
|
||||
xrpl::AccountID const& holder,
|
||||
beast::Journal j);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// MPT Overflow related
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// MaximumAmount doesn't exceed 2**63-1
|
||||
std::int64_t
|
||||
maxMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
// OutstandingAmount may overflow and available amount might be negative.
|
||||
// But available amount is always <= |MaximumAmount - OutstandingAmount|.
|
||||
std::int64_t
|
||||
availableMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID);
|
||||
|
||||
/** Checks for two types of OutstandingAmount overflow during a send operation.
|
||||
* 1. **Direct directSendNoFee (Overflow: No):** A true overflow check when
|
||||
* `OutstandingAmount > MaximumAmount`. This threshold is used for direct
|
||||
* directSendNoFee transactions that bypass the payment engine.
|
||||
* 2. **accountSend & Payment Engine (Overflow: Yes):** A temporary overflow
|
||||
* check when `OutstandingAmount > UINT64_MAX`. This higher threshold is used
|
||||
* for `accountSend` and payments processed via the payment engine.
|
||||
*/
|
||||
bool
|
||||
isMPTOverflow(
|
||||
std::int64_t sendAmount,
|
||||
std::uint64_t outstandingAmount,
|
||||
std::int64_t maximumAmount,
|
||||
AllowMPTOverflow allowOverflow);
|
||||
|
||||
/**
|
||||
* Determine funds available for an issuer to sell in an issuer owned offer.
|
||||
* Issuing step, which could be either MPTEndPointStep last step or BookStep's
|
||||
* TakerPays may overflow OutstandingAmount. Redeeming step, in BookStep's
|
||||
* TakerGets redeems the offer's owner funds, essentially balancing out
|
||||
* the overflow, unless the offer's owner is the issuer.
|
||||
*/
|
||||
[[nodiscard]] STAmount
|
||||
issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue);
|
||||
|
||||
/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer.
|
||||
* See ApplyView::issuerSelfDebitHookMPT().
|
||||
*/
|
||||
void
|
||||
issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// MPT DEX
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* Return true if a transaction is allowed for the specified MPT/account. The
|
||||
* function checks MPTokenIssuance and MPToken objects flags to determine if the
|
||||
* transaction is allowed.
|
||||
*/
|
||||
TER
|
||||
checkMPTTxAllowed(ReadView const& v, TxType tx, Asset const& asset, AccountID const& accountID);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -252,4 +252,14 @@ deleteAMMTrustLine(
|
||||
std::optional<AccountID> const& ammAccountID,
|
||||
beast::Journal j);
|
||||
|
||||
/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior
|
||||
* call to view.peek().
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
deleteAMMMPToken(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> sleMPT,
|
||||
AccountID const& ammAccountID,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,6 +31,9 @@ enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE };
|
||||
|
||||
enum class WaiveTransferFee : bool { No = false, Yes };
|
||||
|
||||
/** Controls whether accountSend is allowed to overflow OutstandingAmount **/
|
||||
enum class AllowMPTOverflow : bool { No = false, Yes };
|
||||
|
||||
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
|
||||
* - StrongAuth - before checking if authorization is required
|
||||
* - WeakAuth
|
||||
@@ -176,6 +179,16 @@ accountFunds(
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j);
|
||||
|
||||
// Overload with AuthHandling to support IOU and MPT.
|
||||
[[nodiscard]] STAmount
|
||||
accountFunds(
|
||||
ReadView const& view,
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal j);
|
||||
|
||||
/** Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
* @param amount The amount to transfer
|
||||
@@ -257,7 +270,8 @@ accountSend(
|
||||
AccountID const& to,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee = WaiveTransferFee::No);
|
||||
WaiveTransferFee waiveFee = WaiveTransferFee::No,
|
||||
AllowMPTOverflow allowOverflow = AllowMPTOverflow::No);
|
||||
|
||||
using MultiplePaymentDestinations = std::vector<std::pair<AccountID, Number>>;
|
||||
/** Like accountSend, except one account is sending multiple payments (with the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
@@ -31,12 +31,12 @@ class Rules;
|
||||
/** Calculate Liquidity Provider Token (LPT) Currency.
|
||||
*/
|
||||
Currency
|
||||
ammLPTCurrency(Currency const& cur1, Currency const& cur2);
|
||||
ammLPTCurrency(Asset const& asset1, Asset const& asset2);
|
||||
|
||||
/** Calculate LPT Issue from AMM asset pair.
|
||||
*/
|
||||
Issue
|
||||
ammLPTIssue(Currency const& cur1, Currency const& cur2, AccountID const& ammAccountID);
|
||||
ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID);
|
||||
|
||||
/** Validate the amount.
|
||||
* If validZero is false and amount is beast::zero then invalid amount.
|
||||
@@ -46,19 +46,19 @@ ammLPTIssue(Currency const& cur1, Currency const& cur2, AccountID const& ammAcco
|
||||
NotTEC
|
||||
invalidAMMAmount(
|
||||
STAmount const& amount,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt,
|
||||
bool validZero = false);
|
||||
|
||||
NotTEC
|
||||
invalidAMMAsset(
|
||||
Issue const& issue,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt);
|
||||
Asset const& asset,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt);
|
||||
|
||||
NotTEC
|
||||
invalidAMMAssetPair(
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt);
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt);
|
||||
|
||||
/** Get time slot of the auction slot.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
@@ -9,11 +10,12 @@
|
||||
namespace xrpl {
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(IOUAmount const& iou, Issue const& iss)
|
||||
toSTAmount(IOUAmount const& iou, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(asset.holds<Issue>(), "xrpl::toSTAmount : is Issue");
|
||||
bool const isNeg = iou.signum() < 0;
|
||||
std::uint64_t const umant = isNeg ? -iou.mantissa() : iou.mantissa();
|
||||
return STAmount(iss, umant, iou.exponent(), isNeg, STAmount::unchecked());
|
||||
return STAmount(asset, umant, iou.exponent(), isNeg, STAmount::unchecked());
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
@@ -31,12 +33,25 @@ toSTAmount(XRPAmount const& xrp)
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(XRPAmount const& xrp, Issue const& iss)
|
||||
toSTAmount(XRPAmount const& xrp, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(isXRP(iss.account) && isXRP(iss.currency), "xrpl::toSTAmount : is XRP");
|
||||
XRPL_ASSERT(isXRP(asset), "xrpl::toSTAmount : is XRP");
|
||||
return toSTAmount(xrp);
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(MPTAmount const& mpt)
|
||||
{
|
||||
return STAmount(mpt, noMPT());
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(MPTAmount const& mpt, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(asset.holds<MPTIssue>(), "xrpl::toSTAmount : is MPT");
|
||||
return STAmount(mpt, asset.get<MPTIssue>());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(STAmount const& amt) = delete;
|
||||
@@ -76,6 +91,21 @@ toAmount<XRPAmount>(STAmount const& amt)
|
||||
return XRPAmount(sMant);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline MPTAmount
|
||||
toAmount<MPTAmount>(STAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amt.holds<MPTIssue>() && amt.mantissa() <= maxMPTokenAmount && amt.exponent() == 0,
|
||||
"xrpl::toAmount<MPTAmount> : maximum mantissa");
|
||||
if (amt.mantissa() > maxMPTokenAmount || amt.exponent() != 0)
|
||||
Throw<std::runtime_error>("toAmount<MPTAmount>: invalid mantissa or exponent");
|
||||
bool const isNeg = amt.negative();
|
||||
std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
|
||||
|
||||
return MPTAmount(sMant);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(IOUAmount const& amt) = delete;
|
||||
@@ -98,23 +128,36 @@ toAmount<XRPAmount>(XRPAmount const& amt)
|
||||
return amt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(MPTAmount const& amt) = delete;
|
||||
|
||||
template <>
|
||||
inline MPTAmount
|
||||
toAmount<MPTAmount>(MPTAmount const& amt)
|
||||
{
|
||||
return amt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
toAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
toAmount(Asset const& asset, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
{
|
||||
saveNumberRoundMode const rm(Number::getround());
|
||||
if (isXRP(issue))
|
||||
if (isXRP(asset))
|
||||
Number::setround(mode);
|
||||
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return IOUAmount(n);
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return XRPAmount(static_cast<std::int64_t>(n));
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return MPTAmount(static_cast<std::int64_t>(n));
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(n));
|
||||
return STAmount(issue, n);
|
||||
if (isXRP(asset))
|
||||
return STAmount(asset, static_cast<std::int64_t>(n));
|
||||
return STAmount(asset, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -125,17 +168,23 @@ toAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Numbe
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
toMaxAmount(Issue const& issue)
|
||||
toMaxAmount(Asset const& asset)
|
||||
{
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return IOUAmount(STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return XRPAmount(static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return MPTAmount(maxMPTokenAmount);
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
return STAmount(issue, STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
return asset.visit(
|
||||
[](Issue const& issue) {
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
return STAmount(issue, STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
},
|
||||
[](MPTIssue const& issue) { return STAmount(issue, maxMPTokenAmount); });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,21 +194,23 @@ toMaxAmount(Issue const& issue)
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
toSTAmount(Asset const& asset, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
{
|
||||
return toAmount<STAmount>(issue, n, mode);
|
||||
return toAmount<STAmount>(asset, n, mode);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Issue
|
||||
getIssue(T const& amt)
|
||||
Asset
|
||||
getAsset(T const& amt)
|
||||
{
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return noIssue();
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return xrpIssue();
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return noMPT();
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
return amt.issue();
|
||||
return amt.asset();
|
||||
else
|
||||
{
|
||||
constexpr bool alwaysFalse = !std::is_same_v<T, T>;
|
||||
@@ -175,6 +226,8 @@ get(STAmount const& a)
|
||||
return a.iou();
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return a.xrp();
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return a.mpt();
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
return a;
|
||||
else
|
||||
|
||||
@@ -2,20 +2,37 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Asset;
|
||||
class STAmount;
|
||||
|
||||
template <typename TIss>
|
||||
concept ValidIssueType = std::is_same_v<TIss, Issue> || std::is_same_v<TIss, MPTIssue>;
|
||||
template <typename T>
|
||||
requires(
|
||||
std::is_same_v<T, XRPAmount> || std::is_same_v<T, IOUAmount> ||
|
||||
std::is_same_v<T, MPTAmount>)
|
||||
struct AmountType
|
||||
{
|
||||
using amount_type = T;
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
concept AssetType = std::is_convertible_v<A, Asset> || std::is_convertible_v<A, Issue> ||
|
||||
std::is_convertible_v<A, MPTIssue> || std::is_convertible_v<A, MPTID>;
|
||||
/* Used to check for an asset with either badCurrency()
|
||||
* or MPT with 0 account.
|
||||
*/
|
||||
struct BadAsset
|
||||
{
|
||||
};
|
||||
|
||||
inline BadAsset const&
|
||||
badAsset()
|
||||
{
|
||||
static BadAsset const a;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Asset is an abstraction of three different issue types: XRP, IOU, MPT.
|
||||
* For historical reasons, two issue types XRP and IOU are wrapped in Issue
|
||||
@@ -26,6 +43,9 @@ class Asset
|
||||
{
|
||||
public:
|
||||
using value_type = std::variant<Issue, MPTIssue>;
|
||||
using token_type = std::variant<Currency, MPTID>;
|
||||
using AmtType =
|
||||
std::variant<AmountType<XRPAmount>, AmountType<IOUAmount>, AmountType<MPTAmount>>;
|
||||
|
||||
private:
|
||||
value_type issue_;
|
||||
@@ -69,36 +89,42 @@ public:
|
||||
constexpr value_type const&
|
||||
value() const;
|
||||
|
||||
constexpr token_type
|
||||
token() const;
|
||||
|
||||
void
|
||||
setJson(Json::Value& jv) const;
|
||||
|
||||
STAmount
|
||||
operator()(Number const&) const;
|
||||
|
||||
bool
|
||||
constexpr AmtType
|
||||
getAmountType() const;
|
||||
|
||||
// Custom, generic visit implementation
|
||||
template <typename... Visitors>
|
||||
constexpr auto
|
||||
visit(Visitors&&... visitors) const -> decltype(auto)
|
||||
{
|
||||
// Simple delegation to the reusable utility, passing the internal
|
||||
// variant data.
|
||||
return detail::visit(issue_, std::forward<Visitors>(visitors)...);
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
native() const
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return issue.native();
|
||||
if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
return false;
|
||||
},
|
||||
issue_);
|
||||
return visit(
|
||||
[&](Issue const& issue) { return issue.native(); },
|
||||
[&](MPTIssue const&) { return false; });
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return issue.native();
|
||||
if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
return true;
|
||||
},
|
||||
issue_);
|
||||
return visit(
|
||||
[&](Issue const& issue) { return issue.native(); },
|
||||
[&](MPTIssue const&) { return true; });
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -110,6 +136,10 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Currency const& lhs, Asset const& rhs);
|
||||
|
||||
// rhs is either badCurrency() or MPT issuer is 0
|
||||
friend constexpr bool
|
||||
operator==(BadAsset const& lhs, Asset const& rhs);
|
||||
|
||||
/** Return true if both assets refer to the same currency (regardless of
|
||||
* issuer) or MPT issuance. Otherwise return false.
|
||||
*/
|
||||
@@ -117,6 +147,12 @@ public:
|
||||
equalTokens(Asset const& lhs, Asset const& rhs);
|
||||
};
|
||||
|
||||
template <ValidIssueType TIss>
|
||||
constexpr bool is_issue_v = std::is_same_v<TIss, Issue>;
|
||||
|
||||
template <ValidIssueType TIss>
|
||||
constexpr bool is_mptissue_v = std::is_same_v<TIss, MPTIssue>;
|
||||
|
||||
inline Json::Value
|
||||
to_json(Asset const& asset)
|
||||
{
|
||||
@@ -156,6 +192,29 @@ Asset::value() const
|
||||
return issue_;
|
||||
}
|
||||
|
||||
constexpr Asset::token_type
|
||||
Asset::token() const
|
||||
{
|
||||
return visit(
|
||||
[&](Issue const& issue) -> Asset::token_type { return issue.currency; },
|
||||
[&](MPTIssue const& issue) -> Asset::token_type { return issue.getMptID(); });
|
||||
}
|
||||
|
||||
constexpr Asset::AmtType
|
||||
Asset::getAmountType() const
|
||||
{
|
||||
return visit(
|
||||
[&](Issue const& issue) -> Asset::AmtType {
|
||||
constexpr AmountType<XRPAmount> xrp;
|
||||
constexpr AmountType<IOUAmount> iou;
|
||||
return native() ? AmtType(xrp) : AmtType(iou);
|
||||
},
|
||||
[&](MPTIssue const& issue) -> Asset::AmtType {
|
||||
constexpr AmountType<MPTAmount> mpt;
|
||||
return AmtType(mpt);
|
||||
});
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(Asset const& lhs, Asset const& rhs)
|
||||
{
|
||||
@@ -177,7 +236,7 @@ operator<=>(Asset const& lhs, Asset const& rhs)
|
||||
[]<ValidIssueType TLhs, ValidIssueType TRhs>(TLhs const& lhs_, TRhs const& rhs_) {
|
||||
if constexpr (std::is_same_v<TLhs, TRhs>)
|
||||
return std::weak_ordering(lhs_ <=> rhs_);
|
||||
else if constexpr (std::is_same_v<TLhs, Issue> && std::is_same_v<TRhs, MPTIssue>)
|
||||
else if constexpr (is_issue_v<TLhs> && is_mptissue_v<TRhs>)
|
||||
return std::weak_ordering::greater;
|
||||
else
|
||||
return std::weak_ordering::less;
|
||||
@@ -189,7 +248,17 @@ operator<=>(Asset const& lhs, Asset const& rhs)
|
||||
constexpr bool
|
||||
operator==(Currency const& lhs, Asset const& rhs)
|
||||
{
|
||||
return rhs.holds<Issue>() && rhs.get<Issue>().currency == lhs;
|
||||
return rhs.visit(
|
||||
[&](Issue const& issue) { return issue.currency == lhs; },
|
||||
[](MPTIssue const& issue) { return false; });
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(BadAsset const&, Asset const& rhs)
|
||||
{
|
||||
return rhs.visit(
|
||||
[](Issue const& issue) -> bool { return badCurrency() == issue.currency; },
|
||||
[](MPTIssue const& issue) -> bool { return issue.getIssuer() == xrpAccount(); });
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
@@ -223,4 +292,36 @@ validJSONAsset(Json::Value const& jv);
|
||||
Asset
|
||||
assetFromJson(Json::Value const& jv);
|
||||
|
||||
Json::Value
|
||||
to_json(Asset const& asset);
|
||||
|
||||
inline bool
|
||||
isConsistent(Asset const& asset)
|
||||
{
|
||||
return asset.visit(
|
||||
[](Issue const& issue) { return isConsistent(issue); },
|
||||
[](MPTIssue const&) { return true; });
|
||||
}
|
||||
|
||||
inline bool
|
||||
validAsset(Asset const& asset)
|
||||
{
|
||||
return asset.visit(
|
||||
[](Issue const& issue) { return isConsistent(issue) && issue.currency != badCurrency(); },
|
||||
[](MPTIssue const& issue) { return issue.getIssuer() != xrpAccount(); });
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, Asset const& r)
|
||||
{
|
||||
using beast::hash_append;
|
||||
r.visit(
|
||||
[&](Issue const& issue) { hash_append(h, issue); },
|
||||
[&](MPTIssue const& issue) { hash_append(h, issue); });
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Asset const& x);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
@@ -15,15 +15,15 @@ namespace xrpl {
|
||||
class Book final : public CountedObject<Book>
|
||||
{
|
||||
public:
|
||||
Issue in;
|
||||
Issue out;
|
||||
Asset in;
|
||||
Asset out;
|
||||
std::optional<uint256> domain;
|
||||
|
||||
Book()
|
||||
{
|
||||
}
|
||||
|
||||
Book(Issue const& in_, Issue const& out_, std::optional<uint256> const& domain_)
|
||||
Book(Asset const& in_, Asset const& out_, std::optional<uint256> const& domain_)
|
||||
: in(in_), out(out_), domain(domain_)
|
||||
{
|
||||
}
|
||||
@@ -112,16 +112,67 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::MPTIssue> : private boost::base_from_member<std::hash<xrpl::MPTID>, 0>
|
||||
{
|
||||
private:
|
||||
using id_hash_type = boost::base_from_member<std::hash<xrpl::MPTID>, 0>;
|
||||
|
||||
public:
|
||||
explicit hash() = default;
|
||||
|
||||
using value_type = std::size_t;
|
||||
using argument_type = xrpl::MPTIssue;
|
||||
|
||||
value_type
|
||||
operator()(argument_type const& value) const
|
||||
{
|
||||
value_type const result(id_hash_type::member(value.getMptID()));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Asset>
|
||||
{
|
||||
private:
|
||||
using value_type = std::size_t;
|
||||
using argument_type = xrpl::Asset;
|
||||
|
||||
using issue_hasher = std::hash<xrpl::Issue>;
|
||||
using mptissue_hasher = std::hash<xrpl::MPTIssue>;
|
||||
|
||||
issue_hasher m_issue_hasher;
|
||||
mptissue_hasher m_mptissue_hasher;
|
||||
|
||||
public:
|
||||
explicit hash() = default;
|
||||
|
||||
value_type
|
||||
operator()(argument_type const& asset) const
|
||||
{
|
||||
return asset.visit(
|
||||
[&](xrpl::Issue const& issue) {
|
||||
value_type const result(m_issue_hasher(issue));
|
||||
return result;
|
||||
},
|
||||
[&](xrpl::MPTIssue const& issue) {
|
||||
value_type const result(m_mptissue_hasher(issue));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Book>
|
||||
{
|
||||
private:
|
||||
using issue_hasher = std::hash<xrpl::Issue>;
|
||||
using asset_hasher = std::hash<xrpl::Asset>;
|
||||
using uint256_hasher = xrpl::uint256::hasher;
|
||||
|
||||
issue_hasher m_issue_hasher;
|
||||
asset_hasher m_asset_hasher;
|
||||
uint256_hasher m_uint256_hasher;
|
||||
|
||||
public:
|
||||
@@ -133,8 +184,8 @@ public:
|
||||
value_type
|
||||
operator()(argument_type const& value) const
|
||||
{
|
||||
value_type result(m_issue_hasher(value.in));
|
||||
boost::hash_combine(result, m_issue_hasher(value.out));
|
||||
value_type result(m_asset_hasher(value.in));
|
||||
boost::hash_combine(result, m_asset_hasher(value.out));
|
||||
|
||||
if (value.domain)
|
||||
boost::hash_combine(result, m_uint256_hasher(*value.domain));
|
||||
@@ -159,6 +210,22 @@ struct hash<xrpl::Issue> : std::hash<xrpl::Issue>
|
||||
// using Base::Base; // inherit ctors
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::MPTIssue> : std::hash<xrpl::MPTIssue>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::MPTIssue>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Asset> : std::hash<xrpl::Asset>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::Asset>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Book> : std::hash<xrpl::Book>
|
||||
{
|
||||
|
||||
86
include/xrpl/protocol/Concepts.h
Normal file
86
include/xrpl/protocol/Concepts.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class STAmount;
|
||||
class Asset;
|
||||
class Issue;
|
||||
class MPTIssue;
|
||||
class IOUAmount;
|
||||
class XRPAmount;
|
||||
class MPTAmount;
|
||||
|
||||
template <typename A>
|
||||
concept StepAmount =
|
||||
std::is_same_v<A, XRPAmount> || std::is_same_v<A, IOUAmount> || std::is_same_v<A, MPTAmount>;
|
||||
|
||||
template <typename TIss>
|
||||
concept ValidIssueType = std::is_same_v<TIss, Issue> || std::is_same_v<TIss, MPTIssue>;
|
||||
|
||||
template <typename A>
|
||||
concept AssetType = std::is_convertible_v<A, Asset> || std::is_convertible_v<A, Issue> ||
|
||||
std::is_convertible_v<A, MPTIssue> || std::is_convertible_v<A, MPTID>;
|
||||
|
||||
template <typename T>
|
||||
concept ValidPathAsset = (std::is_same_v<T, Currency> || std::is_same_v<T, MPTID>);
|
||||
|
||||
template <class TTakerPays, class TTakerGets>
|
||||
concept ValidTaker =
|
||||
((std::is_same_v<TTakerPays, IOUAmount> || std::is_same_v<TTakerPays, XRPAmount> ||
|
||||
std::is_same_v<TTakerPays, MPTAmount>) &&
|
||||
(std::is_same_v<TTakerGets, IOUAmount> || std::is_same_v<TTakerGets, XRPAmount> ||
|
||||
std::is_same_v<TTakerGets, MPTAmount>) &&
|
||||
(!std::is_same_v<TTakerPays, XRPAmount> || !std::is_same_v<TTakerGets, XRPAmount>));
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This template combines multiple callable objects (lambdas) into a single
|
||||
// object that std::visit can use for overload resolution.
|
||||
template <typename... Ts>
|
||||
struct CombineVisitors : Ts...
|
||||
{
|
||||
// Bring all operator() overloads from base classes into this scope.
|
||||
// It's the mechanism that makes the CombineVisitors struct function
|
||||
// as a single callable object with multiple overloads.
|
||||
using Ts::operator()...;
|
||||
|
||||
// Perfect forwarding constructor to correctly initialize the base class
|
||||
// lambdas
|
||||
constexpr CombineVisitors(Ts&&... ts) : Ts(std::forward<Ts>(ts))...
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// This function forces function template argument deduction, which is more
|
||||
// robust than class template argument deduction (CTAD) via the deduction guide.
|
||||
template <typename... Ts>
|
||||
constexpr CombineVisitors<std::decay_t<Ts>...>
|
||||
make_combine_visitors(Ts&&... ts)
|
||||
{
|
||||
// std::decay_t<Ts> is used to remove references/constness from the lambda
|
||||
// types before they are passed as template arguments to the CombineVisitors
|
||||
// struct.
|
||||
return CombineVisitors<std::decay_t<Ts>...>{std::forward<Ts>(ts)...};
|
||||
}
|
||||
|
||||
// This function takes ANY variant and ANY number of visitors, and performs the
|
||||
// visit. It is the reusable core logic.
|
||||
template <typename Variant, typename... Visitors>
|
||||
constexpr auto
|
||||
visit(Variant&& v, Visitors&&... visitors) -> decltype(auto)
|
||||
{
|
||||
// Use the function template helper instead of raw CTAD.
|
||||
auto visitor_set = make_combine_visitors(std::forward<Visitors>(visitors)...);
|
||||
|
||||
// Delegate to std::visit, perfectly forwarding the variant and the visitor
|
||||
// set.
|
||||
return std::visit(visitor_set, std::forward<Variant>(v));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace xrpl
|
||||
460
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
460
include/xrpl/protocol/ConfidentialTransfer.h
Normal file
@@ -0,0 +1,460 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/detail/secp256k1.h>
|
||||
|
||||
#include <secp256k1_mpt.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Bundles an ElGamal public key with its associated encrypted amount.
|
||||
*
|
||||
* Used to represent a recipient in confidential transfers, containing both
|
||||
* the recipient's ElGamal public key and the ciphertext encrypting the
|
||||
* transfer amount under that key.
|
||||
*/
|
||||
struct ConfidentialRecipient
|
||||
{
|
||||
Slice publicKey; ///< The recipient's ElGamal public key (size=xrpl::ecPubKeyLength).
|
||||
Slice encryptedAmount; ///< The encrypted amount ciphertext
|
||||
///< (size=xrpl::ecGamalEncryptedTotalLength).
|
||||
};
|
||||
|
||||
/// Holds two secp256k1 public key components representing an ElGamal ciphertext (C1, C2).
|
||||
struct EcPair
|
||||
{
|
||||
secp256k1_pubkey c1;
|
||||
secp256k1_pubkey c2;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Increments the confidential balance version counter on an MPToken.
|
||||
*
|
||||
* The version counter is used to prevent replay attacks by binding proofs
|
||||
* to a specific state of the account's confidential balance. Wraps to 0
|
||||
* on overflow (defined behavior for unsigned integers).
|
||||
*
|
||||
* @param mptoken The MPToken ledger entry to update.
|
||||
*/
|
||||
inline void
|
||||
incrementConfidentialVersion(STObject& mptoken)
|
||||
{
|
||||
// Retrieve current version and increment.
|
||||
// Unsigned integer overflow is defined behavior in C++ (wraps to 0),
|
||||
// which is acceptable here.
|
||||
mptoken[sfConfidentialBalanceVersion] =
|
||||
mptoken[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for ConfidentialMPTSend transactions.
|
||||
*
|
||||
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
|
||||
* this specific send transaction, preventing proof reuse across transactions.
|
||||
*
|
||||
* @param account The sender's account ID.
|
||||
* @param issuanceID The MPToken Issuance ID.
|
||||
* @param sequence The transaction sequence number or ticket number.
|
||||
* @param destination The destination account ID.
|
||||
* @param version The sender's confidential balance version.
|
||||
* @return A 256-bit context hash unique to this transaction.
|
||||
*/
|
||||
uint256
|
||||
getSendContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
AccountID const& destination,
|
||||
std::uint32_t version);
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for ConfidentialMPTClawback transactions.
|
||||
*
|
||||
* Creates a unique 256-bit hash that binds the equality proof to this
|
||||
* specific clawback transaction.
|
||||
*
|
||||
* @param account The issuer's account ID.
|
||||
* @param issuanceID The MPToken Issuance ID.
|
||||
* @param sequence The transaction sequence number or ticket number.
|
||||
* @param holder The holder's account ID being clawed back from.
|
||||
* @return A 256-bit context hash unique to this transaction.
|
||||
*/
|
||||
uint256
|
||||
getClawbackContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
AccountID const& holder);
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for ConfidentialMPTConvert transactions.
|
||||
*
|
||||
* Creates a unique 256-bit hash that binds the Schnorr proof (for key
|
||||
* registration) to this specific convert transaction.
|
||||
*
|
||||
* @param account The holder's account ID.
|
||||
* @param issuanceID The MPToken Issuance ID.
|
||||
* @param sequence The transaction sequence number or a ticket number.
|
||||
* @return A 256-bit context hash unique to this transaction.
|
||||
*/
|
||||
uint256
|
||||
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence);
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for ConfidentialMPTConvertBack transactions.
|
||||
*
|
||||
* Creates a unique 256-bit hash that binds the zero-knowledge proofs to
|
||||
* this specific convert-back transaction.
|
||||
*
|
||||
* @param account The holder's account ID.
|
||||
* @param issuanceID The MPToken Issuance ID.
|
||||
* @param sequence The transaction sequence number or a ticket number.
|
||||
* @param version The holder's confidential balance version.
|
||||
* @return A 256-bit context hash unique to this transaction.
|
||||
*/
|
||||
uint256
|
||||
getConvertBackContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
std::uint32_t version);
|
||||
|
||||
/**
|
||||
* @brief Parses an ElGamal ciphertext into two secp256k1 public key components.
|
||||
*
|
||||
* Breaks a 66-byte encrypted amount (two 33-byte compressed EC points) into
|
||||
* a pair containing (C1, C2) for use in cryptographic operations.
|
||||
*
|
||||
* @param buffer The 66-byte buffer containing the compressed ciphertext.
|
||||
* @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid.
|
||||
*/
|
||||
std::optional<EcPair>
|
||||
makeEcPair(Slice const& buffer);
|
||||
|
||||
/**
|
||||
* @brief Serializes an EcPair into compressed form.
|
||||
*
|
||||
* Converts an EcPair (C1, C2) back into a 66-byte buffer containing
|
||||
* two 33-byte compressed EC points.
|
||||
*
|
||||
* @param pair The EcPair to serialize.
|
||||
* @return The 66-byte buffer, or std::nullopt if serialization fails.
|
||||
*/
|
||||
std::optional<Buffer>
|
||||
serializeEcPair(EcPair const& pair);
|
||||
|
||||
/**
|
||||
* @brief Verifies that a buffer contains two valid, parsable EC public keys.
|
||||
*
|
||||
* @param buffer The input buffer containing two concatenated components.
|
||||
* @return true if both components can be parsed successfully, false otherwise.
|
||||
*/
|
||||
bool
|
||||
isValidCiphertext(Slice const& buffer);
|
||||
|
||||
/**
|
||||
* @brief Verifies that a buffer contains a valid, parsable compressed EC point.
|
||||
*
|
||||
* Can be used to validate both compressed public keys and Pedersen commitments.
|
||||
* Fails early if the prefix byte is not 0x02 or 0x03.
|
||||
*
|
||||
* @param buffer The input buffer containing a compressed EC point (33 bytes).
|
||||
* @return true if the point can be parsed successfully, false otherwise.
|
||||
*/
|
||||
bool
|
||||
isValidCompressedECPoint(Slice const& buffer);
|
||||
|
||||
/**
|
||||
* @brief Homomorphically adds two ElGamal ciphertexts.
|
||||
*
|
||||
* Uses the additive homomorphic property of ElGamal encryption to compute
|
||||
* Enc(a + b) from Enc(a) and Enc(b) without decryption.
|
||||
*
|
||||
* @param a The first ciphertext (66 bytes).
|
||||
* @param b The second ciphertext (66 bytes).
|
||||
* @return The resulting ciphertext Enc(a + b), or std::nullopt on failure.
|
||||
*/
|
||||
std::optional<Buffer>
|
||||
homomorphicAdd(Slice const& a, Slice const& b);
|
||||
|
||||
/**
|
||||
* @brief Homomorphically subtracts two ElGamal ciphertexts.
|
||||
*
|
||||
* Uses the additive homomorphic property of ElGamal encryption to compute
|
||||
* Enc(a - b) from Enc(a) and Enc(b) without decryption.
|
||||
*
|
||||
* @param a The minuend ciphertext (66 bytes).
|
||||
* @param b The subtrahend ciphertext (66 bytes).
|
||||
* @return The resulting ciphertext Enc(a - b), or std::nullopt on failure.
|
||||
*/
|
||||
std::optional<Buffer>
|
||||
homomorphicSubtract(Slice const& a, Slice const& b);
|
||||
|
||||
/**
|
||||
* @brief Encrypts an amount using ElGamal encryption.
|
||||
*
|
||||
* Produces a ciphertext C = (C1, C2) where C1 = r*G and C2 = m*G + r*Pk,
|
||||
* using the provided blinding factor r.
|
||||
*
|
||||
* @param amt The plaintext amount to encrypt.
|
||||
* @param pubKeySlice The recipient's ElGamal public key (size=xrpl::ecPubKeyLength).
|
||||
* @param blindingFactor The randomness used as blinding factor r
|
||||
* (size=xrpl::ecBlindingFactorLength).
|
||||
* @return The ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt on failure.
|
||||
*/
|
||||
std::optional<Buffer>
|
||||
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor);
|
||||
|
||||
/**
|
||||
* @brief Generates the canonical zero encryption for a specific MPToken.
|
||||
*
|
||||
* Creates a deterministic encryption of zero that is unique to the account
|
||||
* and MPT issuance. Used to initialize confidential balance fields.
|
||||
*
|
||||
* @param pubKeySlice The holder's ElGamal public key (size=xrpl::ecPubKeyLength).
|
||||
* @param account The account ID of the token holder.
|
||||
* @param mptId The MPToken Issuance ID.
|
||||
* @return The canonical zero ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt
|
||||
* on failure.
|
||||
*/
|
||||
std::optional<Buffer>
|
||||
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId);
|
||||
|
||||
/**
|
||||
* @brief Verifies a Schnorr proof of knowledge of an ElGamal private key.
|
||||
*
|
||||
* Proves that the submitter knows the secret key corresponding to the
|
||||
* provided public key, without revealing the secret key itself.
|
||||
*
|
||||
* @param pubKeySlice The ElGamal public key (size=xrpl::ecPubKeyLength).
|
||||
* @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength).
|
||||
* @param contextHash The 256-bit context hash binding the proof.
|
||||
* @return tesSUCCESS if valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Validates the format of encrypted amount fields in a transaction.
|
||||
*
|
||||
* Checks that all ciphertext fields in the transaction object have the
|
||||
* correct length and contain valid EC points. This function is only used
|
||||
* by ConfidentialMPTConvert and ConfidentialMPTConvertBack transactions.
|
||||
*
|
||||
* @param object The transaction object containing encrypted amount fields.
|
||||
* @return tesSUCCESS if all formats are valid, temMALFORMED if required fields
|
||||
* are missing, or temBAD_CIPHERTEXT if format validation fails.
|
||||
*/
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object);
|
||||
|
||||
/**
|
||||
* @brief Verifies revealed amount encryptions for all recipients.
|
||||
*
|
||||
* Validates that the same amount was correctly encrypted for the holder,
|
||||
* issuer, and optionally the auditor using their respective public keys.
|
||||
*
|
||||
* @param amount The revealed plaintext amount.
|
||||
* @param blindingFactor The blinding factor used in all encryptions
|
||||
* (size=xrpl::ecBlindingFactorLength).
|
||||
* @param holder The holder's public key and encrypted amount.
|
||||
* @param issuer The issuer's public key and encrypted amount.
|
||||
* @param auditor Optional auditor's public key and encrypted amount.
|
||||
* @return tesSUCCESS if all encryptions are valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyRevealedAmount(
|
||||
uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
ConfidentialRecipient const& holder,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of recipients in a confidential transfer.
|
||||
*
|
||||
* Returns 4 if an auditor is present (sender, destination, issuer, auditor),
|
||||
* or 3 if no auditor (sender, destination, issuer).
|
||||
*
|
||||
* @param hasAuditor Whether the issuance has an auditor configured.
|
||||
* @return The number of recipients (3 or 4).
|
||||
*/
|
||||
constexpr std::size_t
|
||||
getConfidentialRecipientCount(bool hasAuditor)
|
||||
{
|
||||
return hasAuditor ? 4 : 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size of a multi-ciphertext equality proof.
|
||||
*
|
||||
* Computes the byte size required for a zero-knowledge proof that demonstrates
|
||||
* multiple ciphertexts encrypt the same plaintext value. The size depends on
|
||||
* the number of recipients.
|
||||
*
|
||||
* @param nRecipients The number of recipients (typically 3 or 4).
|
||||
* @return The proof size in bytes.
|
||||
*/
|
||||
inline std::size_t
|
||||
getEqualityProofSize(std::size_t nRecipients)
|
||||
{
|
||||
return secp256k1_mpt_proof_equality_shared_r_size(nRecipients);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verifies a clawback equality proof.
|
||||
*
|
||||
* Proves that the issuer knows the exact amount encrypted in the holder's
|
||||
* balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer
|
||||
* can decrypt the balance using their private key.
|
||||
*
|
||||
* @param amount The revealed plaintext amount.
|
||||
* @param proof The zero-knowledge proof bytes.
|
||||
* @param pubKeySlice The issuer's ElGamal public key (64 bytes).
|
||||
* @param ciphertext The issuer's encrypted balance on the holder's account (66 bytes).
|
||||
* @param contextHash The 256-bit context hash binding the proof.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyClawbackEqualityProof(
|
||||
uint64_t const amount,
|
||||
Slice const& proof,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& ciphertext,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Generates a cryptographically secure 32-byte blinding factor.
|
||||
*
|
||||
* Produces random bytes suitable for use as an ElGamal blinding factor
|
||||
* or Pedersen commitment randomness.
|
||||
*
|
||||
* @return A 32-byte buffer containing the random blinding factor.
|
||||
*/
|
||||
Buffer
|
||||
generateBlindingFactor();
|
||||
|
||||
/**
|
||||
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction.
|
||||
*
|
||||
* This function calls mpt_verify_send_proof API in the mpt-crypto utility lib, which verifies the
|
||||
* equality proof, amount linkage, balance linkage, and range proof.
|
||||
* Equality proof: Proves the same value is encrypted for the sender, receiver, issuer, and auditor.
|
||||
* Amount linkage: Proves the send amount matches the amount Pedersen commitment.
|
||||
* Balance linkage: Proves the sender's balance matches the balance Pedersen
|
||||
* commitment.
|
||||
* Range proof: Proves the amount and the remaining balance are within range [0, 2^64-1].
|
||||
*
|
||||
* @param proof The full proof blob.
|
||||
* @param sender The sender's public key and encrypted amount.
|
||||
* @param destination The destination's public key and encrypted amount.
|
||||
* @param issuer The issuer's public key and encrypted amount.
|
||||
* @param auditor The auditor's public key and encrypted amount if present.
|
||||
* @param spendingBalance The sender's current spending balance ciphertext.
|
||||
* @param amountCommitment The Pedersen commitment to the send amount.
|
||||
* @param balanceCommitment The Pedersen commitment to the sender's balance.
|
||||
* @param contextHash The context hash binding the proof.
|
||||
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifySendProof(
|
||||
Slice const& proof,
|
||||
ConfidentialRecipient const& sender,
|
||||
ConfidentialRecipient const& destination,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor,
|
||||
Slice const& spendingBalance,
|
||||
Slice const& amountCommitment,
|
||||
Slice const& balanceCommitment,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction.
|
||||
*
|
||||
* This function calls mpt_verify_convert_back_proof API in the mpt-crypto utility lib, which
|
||||
* verifies the balance linkage proof and range proof. Balance linkage proof: proves the balance
|
||||
* commitment matches the spending ciphertext. Range proof: proves the remaining balance after
|
||||
* convert back is within range [0, 2^64-1].
|
||||
*
|
||||
* @param proof The full proof blob.
|
||||
* @param pubKeySlice The holder's public key.
|
||||
* @param spendingBalance The holder's spending balance ciphertext.
|
||||
* @param balanceCommitment The Pedersen commitment to the balance.
|
||||
* @param amount The amount being converted back to public.
|
||||
* @param contextHash The context hash binding the proof.
|
||||
* @return tesSUCCESS if all proofs are valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyConvertBackProof(
|
||||
Slice const& proof,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& spendingBalance,
|
||||
Slice const& balanceCommitment,
|
||||
uint64_t amount,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Sequential reader for extracting proof components from a ZKProof blob.
|
||||
*
|
||||
* Encapsulates the offset-based arithmetic for slicing a concatenated proof
|
||||
* blob into its individual components (equality proofs, Pedersen linkage
|
||||
* proofs, bulletproofs, etc.). Performs bounds checking on every read and
|
||||
* tracks whether the entire blob has been consumed.
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* ProofReader reader(tx[sfZKProof]);
|
||||
* auto equalityProof = reader.read(sizeEquality);
|
||||
* auto pedersenProof = reader.read(ecPedersenProofLength);
|
||||
* if (!equalityProof || !pedersenProof || !reader.done())
|
||||
* return tecINTERNAL;
|
||||
* @endcode
|
||||
*/
|
||||
class ProofReader
|
||||
{
|
||||
Slice data_;
|
||||
std::size_t offset_ = 0;
|
||||
|
||||
public:
|
||||
explicit ProofReader(Slice data) : data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the next @p length bytes from the proof blob.
|
||||
*
|
||||
* @param length Number of bytes to read.
|
||||
* @return A Slice of the requested bytes, or std::nullopt if there are
|
||||
* not enough remaining bytes.
|
||||
*/
|
||||
[[nodiscard]] std::optional<Slice>
|
||||
read(std::size_t length)
|
||||
{
|
||||
if (offset_ + length > data_.size())
|
||||
return std::nullopt;
|
||||
auto result = data_.substr(offset_, length);
|
||||
offset_ += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true when every byte has been consumed.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
done() const
|
||||
{
|
||||
return offset_ == data_.size();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -173,7 +173,8 @@ enum LedgerEntryType : std::uint16_t {
|
||||
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
|
||||
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
|
||||
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
|
||||
LSF_FLAG(lsfMPTCanConfidentialAmount, 0x00000080)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPTokenIssuanceMutable, \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \
|
||||
@@ -183,11 +184,13 @@ enum LedgerEntryType : std::uint16_t {
|
||||
LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \
|
||||
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
|
||||
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
|
||||
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000) \
|
||||
LSF_FLAG(lsmfMPTCannotMutateCanConfidentialAmount, 0x00040000)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPToken, \
|
||||
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
|
||||
LSF_FLAG(lsfMPTAuthorized, 0x00000002)) \
|
||||
LSF_FLAG(lsfMPTAuthorized, 0x00000002) \
|
||||
LSF_FLAG(lsfMPTAMM, 0x00000004)) \
|
||||
\
|
||||
LEDGER_OBJECT(Credential, \
|
||||
LSF_FLAG(lsfAccepted, 0x00010000)) \
|
||||
|
||||
@@ -22,11 +22,12 @@ public:
|
||||
using value_type = std::int64_t;
|
||||
|
||||
protected:
|
||||
value_type value_;
|
||||
value_type value_{};
|
||||
|
||||
public:
|
||||
MPTAmount() = default;
|
||||
constexpr MPTAmount(MPTAmount const& other) = default;
|
||||
constexpr MPTAmount(beast::Zero);
|
||||
constexpr MPTAmount&
|
||||
operator=(MPTAmount const& other) = default;
|
||||
|
||||
@@ -85,6 +86,11 @@ constexpr MPTAmount::MPTAmount(value_type value) : value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr MPTAmount::MPTAmount(beast::Zero)
|
||||
{
|
||||
*this = beast::zero;
|
||||
}
|
||||
|
||||
constexpr MPTAmount&
|
||||
MPTAmount::operator=(beast::Zero)
|
||||
{
|
||||
@@ -116,6 +122,14 @@ MPTAmount::value() const
|
||||
return value_;
|
||||
}
|
||||
|
||||
// Output MPTAmount as just the value.
|
||||
template <class Char, class Traits>
|
||||
std::basic_ostream<Char, Traits>&
|
||||
operator<<(std::basic_ostream<Char, Traits>& os, MPTAmount const& q)
|
||||
{
|
||||
return os << q.value();
|
||||
}
|
||||
|
||||
inline std::string
|
||||
to_string(MPTAmount const& amount)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,14 @@ private:
|
||||
public:
|
||||
MPTIssue() = default;
|
||||
|
||||
explicit MPTIssue(MPTID const& issuanceID);
|
||||
MPTIssue(MPTID const& issuanceID);
|
||||
|
||||
MPTIssue(std::uint32_t sequence, AccountID const& account);
|
||||
|
||||
operator MPTID const&() const
|
||||
{
|
||||
return mptID_;
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
getIssuer() const;
|
||||
@@ -73,6 +80,47 @@ isXRP(MPTID const&)
|
||||
return false;
|
||||
}
|
||||
|
||||
inline AccountID
|
||||
getMPTIssuer(MPTID const& mptid)
|
||||
{
|
||||
static_assert(sizeof(MPTID) == (sizeof(std::uint32_t) + sizeof(AccountID)));
|
||||
// Extract the 20 bytes for the AccountID
|
||||
std::array<std::uint8_t, sizeof(AccountID)> bytes{};
|
||||
std::copy_n(mptid.data() + sizeof(std::uint32_t), sizeof(AccountID), bytes.begin());
|
||||
|
||||
// bit_cast is a "magic" compiler intrinsic that is
|
||||
// usually optimized away to nothing in the final assembly.
|
||||
return std::bit_cast<AccountID>(bytes);
|
||||
}
|
||||
|
||||
// Disallow temporary
|
||||
inline AccountID const&
|
||||
getMPTIssuer(MPTID const&&) = delete;
|
||||
inline AccountID const&
|
||||
getMPTIssuer(MPTID&&) = delete;
|
||||
|
||||
inline MPTID
|
||||
noMPT()
|
||||
{
|
||||
static MPTIssue const mpt{0, noAccount()};
|
||||
return mpt.getMptID();
|
||||
}
|
||||
|
||||
inline MPTID
|
||||
badMPT()
|
||||
{
|
||||
static MPTIssue const mpt{0, xrpAccount()};
|
||||
return mpt.getMptID();
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, MPTIssue const& r)
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, r.getMptID());
|
||||
}
|
||||
|
||||
Json::Value
|
||||
to_json(MPTIssue const& mptIssue);
|
||||
|
||||
@@ -82,4 +130,17 @@ to_string(MPTIssue const& mptIssue);
|
||||
MPTIssue
|
||||
mptIssueFromJson(Json::Value const& jv);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, MPTIssue const& x);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::MPTID> : xrpl::MPTID::hasher
|
||||
{
|
||||
explicit hash() = default;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
130
include/xrpl/protocol/PathAsset.h
Normal file
130
include/xrpl/protocol/PathAsset.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/* Represent STPathElement's asset, which can be Currency or MPTID.
|
||||
*/
|
||||
class PathAsset
|
||||
{
|
||||
private:
|
||||
std::variant<Currency, MPTID> easset_;
|
||||
|
||||
public:
|
||||
PathAsset() = default;
|
||||
// Enables comparing Asset and PathAsset
|
||||
PathAsset(Asset const& asset);
|
||||
PathAsset(Currency const& currency) : easset_(currency)
|
||||
{
|
||||
}
|
||||
PathAsset(MPTID const& mpt) : easset_(mpt)
|
||||
{
|
||||
}
|
||||
|
||||
template <ValidPathAsset T>
|
||||
constexpr bool
|
||||
holds() const;
|
||||
|
||||
constexpr bool
|
||||
isXRP() const;
|
||||
|
||||
template <ValidPathAsset T>
|
||||
T const&
|
||||
get() const;
|
||||
|
||||
constexpr std::variant<Currency, MPTID> const&
|
||||
value() const;
|
||||
|
||||
// Custom, generic visit implementation
|
||||
template <typename... Visitors>
|
||||
constexpr auto
|
||||
visit(Visitors&&... visitors) const -> decltype(auto)
|
||||
{
|
||||
// Simple delegation to the reusable utility, passing the internal
|
||||
// variant data.
|
||||
return detail::visit(easset_, std::forward<Visitors>(visitors)...);
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator==(PathAsset const& lhs, PathAsset const& rhs);
|
||||
};
|
||||
|
||||
template <ValidPathAsset PA>
|
||||
constexpr bool is_currency_v = std::is_same_v<PA, Currency>;
|
||||
|
||||
template <ValidPathAsset PA>
|
||||
constexpr bool is_mptid_v = std::is_same_v<PA, MPTID>;
|
||||
|
||||
inline PathAsset::PathAsset(Asset const& asset)
|
||||
{
|
||||
asset.visit(
|
||||
[&](Issue const& issue) { easset_ = issue.currency; },
|
||||
[&](MPTIssue const& issue) { easset_ = issue.getMptID(); });
|
||||
}
|
||||
|
||||
template <ValidPathAsset T>
|
||||
constexpr bool
|
||||
PathAsset::holds() const
|
||||
{
|
||||
return std::holds_alternative<T>(easset_);
|
||||
}
|
||||
|
||||
template <ValidPathAsset T>
|
||||
T const&
|
||||
PathAsset::get() const
|
||||
{
|
||||
if (!holds<T>())
|
||||
Throw<std::runtime_error>("PathAsset doesn't hold requested asset.");
|
||||
return std::get<T>(easset_);
|
||||
}
|
||||
|
||||
constexpr std::variant<Currency, MPTID> const&
|
||||
PathAsset::value() const
|
||||
{
|
||||
return easset_;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
PathAsset::isXRP() const
|
||||
{
|
||||
return visit(
|
||||
[&](Currency const& currency) { return xrpl::isXRP(currency); },
|
||||
[](MPTID const&) { return false; });
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(PathAsset const& lhs, PathAsset const& rhs)
|
||||
{
|
||||
return std::visit(
|
||||
[]<ValidPathAsset TLhs, ValidPathAsset TRhs>(TLhs const& lhs_, TRhs const& rhs_) {
|
||||
if constexpr (std::is_same_v<TLhs, TRhs>)
|
||||
return lhs_ == rhs_;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
lhs.value(),
|
||||
rhs.value());
|
||||
}
|
||||
|
||||
template <typename Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, PathAsset const& pathAsset)
|
||||
{
|
||||
std::visit([&]<ValidPathAsset T>(T const& e) { hash_append(h, e); }, pathAsset.value());
|
||||
}
|
||||
|
||||
inline bool
|
||||
isXRP(PathAsset const& asset)
|
||||
{
|
||||
return asset.isXRP();
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(PathAsset const& asset);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, PathAsset const& x);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -307,4 +307,46 @@ std::size_t constexpr permissionMaxSize = 10;
|
||||
/** The maximum number of transactions that can be in a batch. */
|
||||
std::size_t constexpr maxBatchTxCount = 8;
|
||||
|
||||
/** Length of one component of EC ElGamal ciphertext */
|
||||
std::size_t constexpr ecGamalEncryptedLength = 33;
|
||||
|
||||
/** EC ElGamal ciphertext length: two 33-byte components concatenated */
|
||||
std::size_t constexpr ecGamalEncryptedTotalLength = ecGamalEncryptedLength * 2;
|
||||
|
||||
/** Length of equality ZKProof in bytes */
|
||||
std::size_t constexpr ecEqualityProofLength = 98;
|
||||
|
||||
/** Length of EC point (compressed) */
|
||||
std::size_t constexpr compressedECPointLength = 33;
|
||||
|
||||
/** Length of EC public key (compressed) */
|
||||
std::size_t constexpr ecPubKeyLength = compressedECPointLength;
|
||||
|
||||
/** Length of EC private key in bytes */
|
||||
std::size_t constexpr ecPrivKeyLength = 32;
|
||||
|
||||
/** Length of the EC blinding factor in bytes */
|
||||
std::size_t constexpr ecBlindingFactorLength = 32;
|
||||
|
||||
/** Length of Schnorr ZKProof for public key registration in bytes */
|
||||
std::size_t constexpr ecSchnorrProofLength = 65;
|
||||
|
||||
/** Length of ElGamal Pedersen linkage proof in bytes */
|
||||
std::size_t constexpr ecPedersenProofLength = 195;
|
||||
|
||||
/** Length of Pedersen Commitment (compressed) */
|
||||
std::size_t constexpr ecPedersenCommitmentLength = compressedECPointLength;
|
||||
|
||||
/** Length of single bulletproof (range proof for 1 commitment) in bytes */
|
||||
std::size_t constexpr ecSingleBulletproofLength = 688;
|
||||
|
||||
/** Length of double bulletproof (range proof for 2 commitments) in bytes */
|
||||
std::size_t constexpr ecDoubleBulletproofLength = 754;
|
||||
|
||||
/** Compressed EC point prefix for even y-coordinate */
|
||||
std::uint8_t constexpr ecCompressedPrefixEvenY = 0x02;
|
||||
|
||||
/** Compressed EC point prefix for odd y-coordinate */
|
||||
std::uint8_t constexpr ecCompressedPrefixOddY = 0x03;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -164,12 +164,9 @@ public:
|
||||
constexpr TIss const&
|
||||
get() const;
|
||||
|
||||
Issue const&
|
||||
issue() const;
|
||||
|
||||
// These three are deprecated
|
||||
Currency const&
|
||||
getCurrency() const;
|
||||
template <ValidIssueType TIss>
|
||||
TIss&
|
||||
get();
|
||||
|
||||
AccountID const&
|
||||
getIssuer() const;
|
||||
@@ -225,9 +222,6 @@ public:
|
||||
void
|
||||
clear(Asset const& asset);
|
||||
|
||||
void
|
||||
setIssuer(AccountID const& uIssuer);
|
||||
|
||||
/** Set the Issue for this amount. */
|
||||
void
|
||||
setIssue(Asset const& asset);
|
||||
@@ -466,16 +460,11 @@ STAmount::get() const
|
||||
return mAsset.get<TIss>();
|
||||
}
|
||||
|
||||
inline Issue const&
|
||||
STAmount::issue() const
|
||||
template <ValidIssueType TIss>
|
||||
TIss&
|
||||
STAmount::get()
|
||||
{
|
||||
return get<Issue>();
|
||||
}
|
||||
|
||||
inline Currency const&
|
||||
STAmount::getCurrency() const
|
||||
{
|
||||
return mAsset.get<Issue>().currency;
|
||||
return mAsset.get<TIss>();
|
||||
}
|
||||
|
||||
inline AccountID const&
|
||||
@@ -505,11 +494,13 @@ operator bool() const noexcept
|
||||
inline STAmount::
|
||||
operator Number() const
|
||||
{
|
||||
if (native())
|
||||
return xrp();
|
||||
if (mAsset.holds<MPTIssue>())
|
||||
return mpt();
|
||||
return iou();
|
||||
return asset().visit(
|
||||
[&](Issue const& issue) -> Number {
|
||||
if (issue.native())
|
||||
return xrp();
|
||||
return iou();
|
||||
},
|
||||
[&](MPTIssue const&) -> Number { return mpt(); });
|
||||
}
|
||||
|
||||
inline STAmount&
|
||||
@@ -568,12 +559,6 @@ STAmount::clear(Asset const& asset)
|
||||
clear();
|
||||
}
|
||||
|
||||
inline void
|
||||
STAmount::setIssuer(AccountID const& uIssuer)
|
||||
{
|
||||
mAsset.get<Issue>().account = uIssuer;
|
||||
}
|
||||
|
||||
inline STAmount const&
|
||||
STAmount::value() const noexcept
|
||||
{
|
||||
|
||||
@@ -349,6 +349,8 @@ public:
|
||||
void
|
||||
setFieldH128(SField const& field, uint128 const&);
|
||||
void
|
||||
setFieldH192(SField const& field, uint192 const&);
|
||||
void
|
||||
setFieldH256(SField const& field, uint256 const&);
|
||||
void
|
||||
setFieldI32(SField const& field, std::int32_t);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/PathAsset.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STBase.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
@@ -16,7 +18,7 @@ class STPathElement final : public CountedObject<STPathElement>
|
||||
{
|
||||
unsigned int mType;
|
||||
AccountID mAccountID;
|
||||
Currency mCurrencyID;
|
||||
PathAsset mAssetID;
|
||||
AccountID mIssuerID;
|
||||
|
||||
bool is_offer_;
|
||||
@@ -28,8 +30,10 @@ public:
|
||||
typeAccount = 0x01, // Rippling through an account (vs taking an offer).
|
||||
typeCurrency = 0x10, // Currency follows.
|
||||
typeIssuer = 0x20, // Issuer follows.
|
||||
typeMPT = 0x40, // MPT follows.
|
||||
typeBoundary = 0xFF, // Boundary between alternate paths.
|
||||
typeAll = typeAccount | typeCurrency | typeIssuer,
|
||||
typeAsset = typeCurrency | typeMPT,
|
||||
typeAll = typeAccount | typeCurrency | typeIssuer | typeMPT,
|
||||
// Combination of all types.
|
||||
};
|
||||
|
||||
@@ -40,19 +44,19 @@ public:
|
||||
|
||||
STPathElement(
|
||||
std::optional<AccountID> const& account,
|
||||
std::optional<Currency> const& currency,
|
||||
std::optional<PathAsset> const& asset,
|
||||
std::optional<AccountID> const& issuer);
|
||||
|
||||
STPathElement(
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer,
|
||||
bool forceCurrency = false);
|
||||
bool forceAsset = false);
|
||||
|
||||
STPathElement(
|
||||
unsigned int uType,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer);
|
||||
|
||||
auto
|
||||
@@ -70,6 +74,12 @@ public:
|
||||
bool
|
||||
hasCurrency() const;
|
||||
|
||||
bool
|
||||
hasMPT() const;
|
||||
|
||||
bool
|
||||
hasAsset() const;
|
||||
|
||||
bool
|
||||
isNone() const;
|
||||
|
||||
@@ -78,12 +88,21 @@ public:
|
||||
AccountID const&
|
||||
getAccountID() const;
|
||||
|
||||
PathAsset const&
|
||||
getPathAsset() const;
|
||||
|
||||
Currency const&
|
||||
getCurrency() const;
|
||||
|
||||
MPTID const&
|
||||
getMPTID() const;
|
||||
|
||||
AccountID const&
|
||||
getIssuerID() const;
|
||||
|
||||
bool
|
||||
isType(Type const& pe) const;
|
||||
|
||||
bool
|
||||
operator==(STPathElement const& t) const;
|
||||
|
||||
@@ -118,7 +137,7 @@ public:
|
||||
emplace_back(Args&&... args);
|
||||
|
||||
bool
|
||||
hasSeen(AccountID const& account, Currency const& currency, AccountID const& issuer) const;
|
||||
hasSeen(AccountID const& account, PathAsset const& asset, AccountID const& issuer) const;
|
||||
|
||||
Json::Value getJson(JsonOptions) const;
|
||||
|
||||
@@ -221,7 +240,7 @@ inline STPathElement::STPathElement() : mType(typeNone), is_offer_(true)
|
||||
|
||||
inline STPathElement::STPathElement(
|
||||
std::optional<AccountID> const& account,
|
||||
std::optional<Currency> const& currency,
|
||||
std::optional<PathAsset> const& asset,
|
||||
std::optional<AccountID> const& issuer)
|
||||
: mType(typeNone)
|
||||
{
|
||||
@@ -238,10 +257,10 @@ inline STPathElement::STPathElement(
|
||||
mAccountID != noAccount(), "xrpl::STPathElement::STPathElement : account is set");
|
||||
}
|
||||
|
||||
if (currency)
|
||||
if (asset)
|
||||
{
|
||||
mCurrencyID = *currency;
|
||||
mType |= typeCurrency;
|
||||
mAssetID = *asset;
|
||||
mType |= mAssetID.holds<Currency>() ? typeCurrency : typeMPT;
|
||||
}
|
||||
|
||||
if (issuer)
|
||||
@@ -256,20 +275,20 @@ inline STPathElement::STPathElement(
|
||||
|
||||
inline STPathElement::STPathElement(
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer,
|
||||
bool forceCurrency)
|
||||
bool forceAsset)
|
||||
: mType(typeNone)
|
||||
, mAccountID(account)
|
||||
, mCurrencyID(currency)
|
||||
, mAssetID(asset)
|
||||
, mIssuerID(issuer)
|
||||
, is_offer_(isXRP(mAccountID))
|
||||
{
|
||||
if (!is_offer_)
|
||||
mType |= typeAccount;
|
||||
|
||||
if (forceCurrency || !isXRP(currency))
|
||||
mType |= typeCurrency;
|
||||
if (forceAsset || !isXRP(mAssetID))
|
||||
mType |= asset.holds<Currency>() ? typeCurrency : typeMPT;
|
||||
|
||||
if (!isXRP(issuer))
|
||||
mType |= typeIssuer;
|
||||
@@ -280,14 +299,19 @@ inline STPathElement::STPathElement(
|
||||
inline STPathElement::STPathElement(
|
||||
unsigned int uType,
|
||||
AccountID const& account,
|
||||
Currency const& currency,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer)
|
||||
: mType(uType)
|
||||
, mAccountID(account)
|
||||
, mCurrencyID(currency)
|
||||
, mAssetID(asset)
|
||||
, mIssuerID(issuer)
|
||||
, is_offer_(isXRP(mAccountID))
|
||||
{
|
||||
// uType could be assetType; i.e. either Currency or MPTID.
|
||||
// Get the actual type.
|
||||
mAssetID.visit(
|
||||
[&](Currency const&) { mType = mType & (~Type::typeMPT); },
|
||||
[&](MPTID const&) { mType = mType & (~Type::typeCurrency); });
|
||||
hash_value_ = get_hash(*this);
|
||||
}
|
||||
|
||||
@@ -309,16 +333,34 @@ STPathElement::isAccount() const
|
||||
return !isOffer();
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::isType(Type const& pe) const
|
||||
{
|
||||
return (mType & pe) != 0u;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasIssuer() const
|
||||
{
|
||||
return getNodeType() & STPathElement::typeIssuer;
|
||||
return isType(STPathElement::typeIssuer);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasCurrency() const
|
||||
{
|
||||
return getNodeType() & STPathElement::typeCurrency;
|
||||
return isType(STPathElement::typeCurrency);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasMPT() const
|
||||
{
|
||||
return isType(STPathElement::typeMPT);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasAsset() const
|
||||
{
|
||||
return isType(STPathElement::typeAsset);
|
||||
}
|
||||
|
||||
inline bool
|
||||
@@ -335,10 +377,22 @@ STPathElement::getAccountID() const
|
||||
return mAccountID;
|
||||
}
|
||||
|
||||
inline PathAsset const&
|
||||
STPathElement::getPathAsset() const
|
||||
{
|
||||
return mAssetID;
|
||||
}
|
||||
|
||||
inline Currency const&
|
||||
STPathElement::getCurrency() const
|
||||
{
|
||||
return mCurrencyID;
|
||||
return mAssetID.get<Currency>();
|
||||
}
|
||||
|
||||
inline MPTID const&
|
||||
STPathElement::getMPTID() const
|
||||
{
|
||||
return mAssetID.get<MPTID>();
|
||||
}
|
||||
|
||||
inline AccountID const&
|
||||
@@ -351,7 +405,7 @@ inline bool
|
||||
STPathElement::operator==(STPathElement const& t) const
|
||||
{
|
||||
return (mType & typeAccount) == (t.mType & typeAccount) && hash_value_ == t.hash_value_ &&
|
||||
mAccountID == t.mAccountID && mCurrencyID == t.mCurrencyID && mIssuerID == t.mIssuerID;
|
||||
mAccountID == t.mAccountID && mAssetID == t.mAssetID && mIssuerID == t.mIssuerID;
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
||||
@@ -121,6 +121,8 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
temBAD_MPT,
|
||||
temBAD_CIPHERTEXT,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -208,6 +210,7 @@ enum TERcodes : TERUnderlyingType {
|
||||
terADDRESS_COLLISION, // Failed to allocate AccountID when trying to
|
||||
// create a pseudo-account
|
||||
terNO_DELEGATE_PERMISSION, // Delegate does not have permission
|
||||
terLOCKED, // MPT is locked
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -346,6 +349,7 @@ enum TECcodes : TERUnderlyingType {
|
||||
// backward compatibility with historical data on non-prod networks, can be
|
||||
// reclaimed after those networks reset.
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
tecBAD_PROOF = 199,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -138,7 +138,8 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
|
||||
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
|
||||
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
|
||||
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
|
||||
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
|
||||
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \
|
||||
TF_FLAG(tfMPTCanConfidentialAmount, lsfMPTCanConfidentialAmount), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(MPTokenAuthorize, \
|
||||
@@ -347,10 +348,12 @@ inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTrans
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
|
||||
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
|
||||
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
|
||||
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
|
||||
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
|
||||
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
|
||||
inline constexpr FlagValue tmfMPTCannotMutateCanConfidentialAmount =
|
||||
lsmfMPTCannotMutateCanConfidentialAmount;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = ~(
|
||||
tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
|
||||
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
|
||||
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee | tmfMPTCannotMutateCanConfidentialAmount);
|
||||
|
||||
// MPTokenIssuanceSet MutableFlags:
|
||||
// Set or Clear flags.
|
||||
@@ -367,10 +370,13 @@ inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100;
|
||||
inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200;
|
||||
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400;
|
||||
inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~(
|
||||
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
|
||||
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
|
||||
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback);
|
||||
inline constexpr FlagValue tmfMPTSetCanConfidentialAmount = 0x00001000;
|
||||
inline constexpr FlagValue tmfMPTClearCanConfidentialAmount = 0x00002000;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask =
|
||||
~(tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
|
||||
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
|
||||
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback |
|
||||
tmfMPTClearCanClawback | tmfMPTSetCanConfidentialAmount | tmfMPTClearCanConfidentialAmount);
|
||||
|
||||
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a
|
||||
// TrustLine to be added to the issuer of that token without explicit permission from that issuer.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(ConfidentialTransfer, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(MPTokensV2, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -161,8 +161,10 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
|
||||
{sfOwner, soeOPTIONAL}, // for owner directories
|
||||
{sfTakerPaysCurrency, soeOPTIONAL}, // order book directories
|
||||
{sfTakerPaysIssuer, soeOPTIONAL}, // order book directories
|
||||
{sfTakerPaysMPT, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsCurrency, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsIssuer, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsMPT, soeOPTIONAL}, // order book directories
|
||||
{sfExchangeRate, soeOPTIONAL}, // order book directories
|
||||
{sfIndexes, soeREQUIRED},
|
||||
{sfRootIndex, soeREQUIRED},
|
||||
@@ -385,32 +387,41 @@ LEDGER_ENTRY(ltAMM, 0x0079, AMM, amm, ({
|
||||
\sa keylet::mptIssuance
|
||||
*/
|
||||
LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
|
||||
{sfIssuer, soeREQUIRED},
|
||||
{sfSequence, soeREQUIRED},
|
||||
{sfTransferFee, soeDEFAULT},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfAssetScale, soeDEFAULT},
|
||||
{sfMaximumAmount, soeOPTIONAL},
|
||||
{sfOutstandingAmount, soeREQUIRED},
|
||||
{sfLockedAmount, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMutableFlags, soeDEFAULT},
|
||||
{sfIssuer, soeREQUIRED},
|
||||
{sfSequence, soeREQUIRED},
|
||||
{sfTransferFee, soeDEFAULT},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfAssetScale, soeDEFAULT},
|
||||
{sfMaximumAmount, soeOPTIONAL},
|
||||
{sfOutstandingAmount, soeREQUIRED},
|
||||
{sfLockedAmount, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
{sfMutableFlags, soeDEFAULT},
|
||||
{sfIssuerEncryptionKey, soeOPTIONAL},
|
||||
{sfAuditorEncryptionKey, soeOPTIONAL},
|
||||
{sfConfidentialOutstandingAmount, soeDEFAULT},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks MPToken
|
||||
\sa keylet::mptoken
|
||||
*/
|
||||
LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfMPTAmount, soeDEFAULT},
|
||||
{sfLockedAmount, soeOPTIONAL},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfMPTAmount, soeDEFAULT},
|
||||
{sfLockedAmount, soeOPTIONAL},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
{sfConfidentialBalanceInbox, soeOPTIONAL},
|
||||
{sfConfidentialBalanceSpending, soeOPTIONAL},
|
||||
{sfConfidentialBalanceVersion, soeDEFAULT},
|
||||
{sfIssuerEncryptedBalance, soeOPTIONAL},
|
||||
{sfAuditorEncryptedBalance, soeOPTIONAL},
|
||||
{sfHolderEncryptionKey, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** A ledger object which tracks Oracle
|
||||
|
||||
@@ -11,7 +11,10 @@ secp256k1Context()
|
||||
struct holder
|
||||
{
|
||||
secp256k1_context* impl;
|
||||
holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
|
||||
// SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY were
|
||||
// deprecated. All contexts support both signing and verification, so
|
||||
// SECP256K1_CONTEXT_NONE is the correct flag to use.
|
||||
holder() : impl(secp256k1_context_create(SECP256K1_CONTEXT_NONE))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ TYPED_SFIELD(sfInterestRate, UINT32, 65) // 1/10 basis points (bi
|
||||
TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfConfidentialBalanceVersion, UINT32, 69)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -146,6 +147,7 @@ TYPED_SFIELD(sfSubjectNode, UINT64, 28)
|
||||
TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::sMD_BaseTen|SField::sMD_Default)
|
||||
TYPED_SFIELD(sfVaultNode, UINT64, 30)
|
||||
TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31)
|
||||
TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::sMD_BaseTen|SField::sMD_Default)
|
||||
|
||||
// 128-bit
|
||||
TYPED_SFIELD(sfEmailHash, UINT128, 1)
|
||||
@@ -159,6 +161,8 @@ TYPED_SFIELD(sfTakerGetsIssuer, UINT160, 4)
|
||||
// 192-bit (common)
|
||||
TYPED_SFIELD(sfMPTokenIssuanceID, UINT192, 1)
|
||||
TYPED_SFIELD(sfShareMPTID, UINT192, 2)
|
||||
TYPED_SFIELD(sfTakerPaysMPT, UINT192, 3)
|
||||
TYPED_SFIELD(sfTakerGetsMPT, UINT192, 4)
|
||||
|
||||
// 256-bit (common)
|
||||
TYPED_SFIELD(sfLedgerHash, UINT256, 1)
|
||||
@@ -203,6 +207,7 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36)
|
||||
TYPED_SFIELD(sfLoanBrokerID, UINT256, 37,
|
||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfLoanID, UINT256, 38)
|
||||
TYPED_SFIELD(sfBlindingFactor, UINT256, 39)
|
||||
|
||||
// number (common)
|
||||
TYPED_SFIELD(sfNumber, NUMBER, 1)
|
||||
@@ -296,6 +301,21 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
|
||||
TYPED_SFIELD(sfProvider, VL, 29)
|
||||
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
|
||||
TYPED_SFIELD(sfCredentialType, VL, 31)
|
||||
TYPED_SFIELD(sfConfidentialBalanceInbox, VL, 32)
|
||||
TYPED_SFIELD(sfConfidentialBalanceSpending, VL, 33)
|
||||
TYPED_SFIELD(sfIssuerEncryptedBalance, VL, 34)
|
||||
TYPED_SFIELD(sfIssuerEncryptionKey, VL, 35)
|
||||
TYPED_SFIELD(sfHolderEncryptionKey, VL, 36)
|
||||
TYPED_SFIELD(sfZKProof, VL, 37)
|
||||
TYPED_SFIELD(sfHolderEncryptedAmount, VL, 38)
|
||||
TYPED_SFIELD(sfIssuerEncryptedAmount, VL, 39)
|
||||
TYPED_SFIELD(sfSenderEncryptedAmount, VL, 40)
|
||||
TYPED_SFIELD(sfDestinationEncryptedAmount, VL, 41)
|
||||
TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42)
|
||||
TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
|
||||
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
|
||||
TYPED_SFIELD(sfAmountCommitment, VL, 45)
|
||||
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
|
||||
|
||||
// account (common)
|
||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
createAcct,
|
||||
createAcct | mayCreateMPT,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
@@ -129,10 +129,10 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
mayCreateMPT,
|
||||
({
|
||||
{sfTakerPays, soeREQUIRED},
|
||||
{sfTakerGets, soeREQUIRED},
|
||||
{sfTakerPays, soeREQUIRED, soeMPTSupported},
|
||||
{sfTakerGets, soeREQUIRED, soeMPTSupported},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfOfferSequence, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL},
|
||||
@@ -239,7 +239,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSendMax, soeREQUIRED},
|
||||
{sfSendMax, soeREQUIRED, soeMPTSupported},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfInvoiceID, soeOPTIONAL},
|
||||
@@ -252,11 +252,11 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
noPriv,
|
||||
mayCreateMPT,
|
||||
({
|
||||
{sfCheckID, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfDeliverMin, soeOPTIONAL},
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
{sfDeliverMin, soeOPTIONAL, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction type cancels an existing check. */
|
||||
@@ -409,12 +409,12 @@ TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
Delegation::delegable,
|
||||
featureAMMClawback,
|
||||
mayDeleteAcct | overrideFreeze,
|
||||
mayDeleteAcct | overrideFreeze | mayAuthorizeMPT,
|
||||
({
|
||||
{sfHolder, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction type creates an AMM instance */
|
||||
@@ -424,10 +424,10 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
Delegation::delegable,
|
||||
featureAMM,
|
||||
createPseudoAcct,
|
||||
createPseudoAcct | mayCreateMPT,
|
||||
({
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAmount2, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfAmount2, soeREQUIRED, soeMPTSupported},
|
||||
{sfTradingFee, soeREQUIRED},
|
||||
}))
|
||||
|
||||
@@ -440,10 +440,10 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfAmount2, soeOPTIONAL},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
{sfAmount2, soeOPTIONAL, soeMPTSupported},
|
||||
{sfEPrice, soeOPTIONAL},
|
||||
{sfLPTokenOut, soeOPTIONAL},
|
||||
{sfTradingFee, soeOPTIONAL},
|
||||
@@ -456,12 +456,12 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
Delegation::delegable,
|
||||
featureAMM,
|
||||
mayDeleteAcct,
|
||||
mayDeleteAcct | mayAuthorizeMPT,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfAmount2, soeOPTIONAL},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfAmount, soeOPTIONAL, soeMPTSupported},
|
||||
{sfAmount2, soeOPTIONAL, soeMPTSupported},
|
||||
{sfEPrice, soeOPTIONAL},
|
||||
{sfLPTokenIn, soeOPTIONAL},
|
||||
}))
|
||||
@@ -475,8 +475,8 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfTradingFee, soeREQUIRED},
|
||||
}))
|
||||
|
||||
@@ -489,8 +489,8 @@ TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfBidMin, soeOPTIONAL},
|
||||
{sfBidMax, soeOPTIONAL},
|
||||
{sfAuthAccounts, soeOPTIONAL},
|
||||
@@ -503,10 +503,10 @@ TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
Delegation::delegable,
|
||||
featureAMM,
|
||||
mustDeleteAcct,
|
||||
mustDeleteAcct | mayDeleteMPT,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transactions creates a crosschain sequence number */
|
||||
@@ -734,6 +734,8 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfTransferFee, soeOPTIONAL},
|
||||
{sfMutableFlags, soeOPTIONAL},
|
||||
{sfIssuerEncryptionKey, soeOPTIONAL},
|
||||
{sfAuditorEncryptionKey, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
@@ -1076,6 +1078,90 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
|
||||
/** This transaction type converts into confidential MPT balance. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfMPTAmount, soeREQUIRED},
|
||||
{sfHolderEncryptionKey, soeOPTIONAL},
|
||||
{sfHolderEncryptedAmount, soeREQUIRED},
|
||||
{sfIssuerEncryptedAmount, soeREQUIRED},
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfBlindingFactor, soeREQUIRED},
|
||||
{sfZKProof, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type merges MPT inbox. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTMergeInbox.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This transaction type converts back into public MPT balance. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTConvertBack.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfMPTAmount, soeREQUIRED},
|
||||
{sfHolderEncryptedAmount, soeREQUIRED},
|
||||
{sfIssuerEncryptedAmount, soeREQUIRED},
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfBlindingFactor, soeREQUIRED},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
{sfBalanceCommitment, soeREQUIRED},
|
||||
}))
|
||||
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSenderEncryptedAmount, soeREQUIRED},
|
||||
{sfDestinationEncryptedAmount, soeREQUIRED},
|
||||
{sfIssuerEncryptedAmount, soeREQUIRED},
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
{sfAmountCommitment, soeREQUIRED},
|
||||
{sfBalanceCommitment, soeREQUIRED},
|
||||
{sfCredentialIDs, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback,
|
||||
Delegation::delegable,
|
||||
featureConfidentialTransfer,
|
||||
noPriv,
|
||||
({
|
||||
{sfMPTokenIssuanceID, soeREQUIRED},
|
||||
{sfHolder, soeREQUIRED},
|
||||
{sfMPTAmount, soeREQUIRED},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
}))
|
||||
|
||||
/** This system-generated transaction type is used to update the status of the various amendments.
|
||||
|
||||
For details, see: https://xrpl.org/amendments.html
|
||||
|
||||
@@ -137,6 +137,7 @@ JSS(authorized_credentials); // in: ledger_entry DepositPreauth
|
||||
JSS(auth_accounts); // out: amm_info
|
||||
JSS(auth_change); // out: AccountInfo
|
||||
JSS(auth_change_queued); // out: AccountInfo
|
||||
JSS(auditor_encrypted_balance); // out: mpt_holders (confidential MPT)
|
||||
JSS(available); // out: ValidatorList
|
||||
JSS(avg_bps_recv); // out: Peers
|
||||
JSS(avg_bps_sent); // out: Peers
|
||||
@@ -161,9 +162,6 @@ JSS(build_path); // in: TransactionSign
|
||||
JSS(build_version); // out: NetworkOPs
|
||||
JSS(cancel_after); // out: AccountChannels
|
||||
JSS(can_delete); // out: CanDelete
|
||||
JSS(mpt_amount); // out: mpt_holders
|
||||
JSS(mpt_issuance_id); // in: Payment, mpt_holders
|
||||
JSS(mptoken_index); // out: mpt_holders
|
||||
JSS(changes); // out: BookChanges
|
||||
JSS(channel_id); // out: AccountChannels
|
||||
JSS(channels); // out: AccountChannels
|
||||
@@ -185,165 +183,170 @@ JSS(command); // in: RPCHandler
|
||||
JSS(common); // out: RPC server_definitions
|
||||
JSS(complete); // out: NetworkOPs, InboundLedger
|
||||
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
JSS(converge_time); // out: NetworkOPs
|
||||
JSS(converge_time_s); // out: NetworkOPs
|
||||
JSS(cookie); // out: NetworkOPs
|
||||
JSS(count); // in: AccountTx*, ValidatorList
|
||||
JSS(counters); // in/out: retrieve counters
|
||||
JSS(credentials); // in: deposit_authorized
|
||||
JSS(credential_type); // in: LedgerEntry DepositPreauth
|
||||
JSS(ctid); // in/out: Tx RPC
|
||||
JSS(currency_a); // out: BookChanges
|
||||
JSS(currency_b); // out: BookChanges
|
||||
JSS(currency); // in: paths/PathRequest, STAmount
|
||||
// out: STPathSet, STAmount, AccountLines
|
||||
JSS(current); // out: OwnerInfo
|
||||
JSS(current_activities); //
|
||||
JSS(current_ledger_size); // out: TxQ
|
||||
JSS(current_queue_size); // out: TxQ
|
||||
JSS(data); // out: LedgerData
|
||||
JSS(date); // out: tx/Transaction, NetworkOPs
|
||||
JSS(dbKBLedger); // out: getCounts
|
||||
JSS(dbKBTotal); // out: getCounts
|
||||
JSS(dbKBTransaction); // out: getCounts
|
||||
JSS(debug_signing); // in: TransactionSign
|
||||
JSS(deletion_blockers_only); // in: AccountObjects
|
||||
JSS(delivered_amount); // out: insertDeliveredAmount
|
||||
JSS(deposit_authorized); // out: deposit_authorized
|
||||
JSS(deprecated); //
|
||||
JSS(descending); // in: AccountTx*
|
||||
JSS(description); // in/out: Reservations
|
||||
JSS(destination); // in: nft_buy_offers, nft_sell_offers
|
||||
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
|
||||
// out: AccountChannels
|
||||
JSS(destination_amount); // in: PathRequest, RipplePathFind
|
||||
JSS(destination_currencies); // in: PathRequest, RipplePathFind
|
||||
JSS(destination_tag); // in: PathRequest
|
||||
// out: AccountChannels
|
||||
JSS(details); // out: Manifest, server_info
|
||||
JSS(dir_entry); // out: DirectoryEntryIterator
|
||||
JSS(dir_index); // out: DirectoryEntryIterator
|
||||
JSS(dir_root); // out: DirectoryEntryIterator
|
||||
JSS(discounted_fee); // out: amm_info
|
||||
JSS(domain); // out: ValidatorInfo, Manifest
|
||||
JSS(drops); // out: TxQ
|
||||
JSS(duration_us); // out: NetworkOPs
|
||||
JSS(effective); // out: ValidatorList
|
||||
// in: UNL
|
||||
JSS(enabled); // out: AmendmentTable
|
||||
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(entire_set); // out: get_aggregate_price
|
||||
JSS(ephemeral_key); // out: ValidatorInfo
|
||||
// in/out: Manifest
|
||||
JSS(error); // out: error
|
||||
JSS(errored); //
|
||||
JSS(error_code); // out: error
|
||||
JSS(error_exception); // out: Submit
|
||||
JSS(error_message); // out: error
|
||||
JSS(expand); // in: handler/Ledger
|
||||
JSS(expected_date); // out: any (warnings)
|
||||
JSS(expected_date_UTC); // out: any (warnings)
|
||||
JSS(expected_ledger_size); // out: TxQ
|
||||
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
|
||||
JSS(fail_hard); // in: Sign, Submit
|
||||
JSS(failed); // out: InboundLedger
|
||||
JSS(feature); // in: Feature
|
||||
JSS(features); // out: Feature
|
||||
JSS(fee_base); // out: NetworkOPs
|
||||
JSS(fee_div_max); // in: TransactionSign
|
||||
JSS(fee_level); // out: AccountInfo
|
||||
JSS(fee_mult_max); // in: TransactionSign
|
||||
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
|
||||
JSS(fetch_pack); // out: NetworkOPs
|
||||
JSS(FIELDS); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(first); // out: rpc/Version
|
||||
JSS(finished); //
|
||||
JSS(fix_txns); // in: LedgerCleaner
|
||||
JSS(flags); // out: AccountOffers, NetworkOPs
|
||||
JSS(forward); // in: AccountTx
|
||||
JSS(freeze); // out: AccountLines
|
||||
JSS(freeze_peer); // out: AccountLines
|
||||
JSS(deep_freeze); // out: AccountLines
|
||||
JSS(deep_freeze_peer); // out: AccountLines
|
||||
JSS(frozen_balances); // out: GatewayBalances
|
||||
JSS(full); // in: LedgerClearer, handlers/Ledger
|
||||
JSS(full_reply); // out: PathFind
|
||||
JSS(fullbelow_size); // out: GetCounts
|
||||
JSS(git); // out: server_info
|
||||
JSS(good); // out: RPCVersion
|
||||
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
|
||||
JSS(have_header); // out: InboundLedger
|
||||
JSS(have_state); // out: InboundLedger
|
||||
JSS(have_transactions); // out: InboundLedger
|
||||
JSS(high); // out: BookChanges
|
||||
JSS(highest_sequence); // out: AccountInfo
|
||||
JSS(highest_ticket); // out: AccountInfo
|
||||
JSS(historical_perminute); // historical_perminute.
|
||||
JSS(holders); // out: MPTHolders
|
||||
JSS(hostid); // out: NetworkOPs
|
||||
JSS(hotwallet); // in: GatewayBalances
|
||||
JSS(id); // websocket.
|
||||
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
|
||||
JSS(ignore_default); // in: AccountLines
|
||||
JSS(in); // out: OverlayImpl
|
||||
JSS(inLedger); // out: tx/Transaction
|
||||
JSS(inbound); // out: PeerImp
|
||||
JSS(index); // in: LedgerEntry
|
||||
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
|
||||
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
|
||||
JSS(initial_sync_duration_us); //
|
||||
JSS(internal_command); // in: Internal
|
||||
JSS(invalid_API_version); // out: Many, when a request has an invalid version
|
||||
JSS(io_latency_ms); // out: NetworkOPs
|
||||
JSS(ip); // in: Connect, out: OverlayImpl
|
||||
JSS(is_burned); // out: nft_info (clio)
|
||||
JSS(isSerialized); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(isSigningField); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(isVLEncoded); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
|
||||
// out: STPathSet, STAmount
|
||||
JSS(job); //
|
||||
JSS(job_queue); //
|
||||
JSS(jobs); //
|
||||
JSS(jsonrpc); // json version
|
||||
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
|
||||
JSS(kept); // out: SubmitTransaction
|
||||
JSS(key); // out
|
||||
JSS(key_type); // in/out: WalletPropose, TransactionSign
|
||||
JSS(latency); // out: PeerImp
|
||||
JSS(last); // out: RPCVersion
|
||||
JSS(last_close); // out: NetworkOPs
|
||||
JSS(last_refresh_time); // out: ValidatorSite
|
||||
JSS(last_refresh_status); // out: ValidatorSite
|
||||
JSS(last_refresh_message); // out: ValidatorSite
|
||||
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
|
||||
// out: NetworkOPs, PeerImp
|
||||
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
|
||||
// AccountLines
|
||||
JSS(ledger_data); // out: LedgerHeader
|
||||
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
|
||||
// TransactionEntry, handlers/Ledger
|
||||
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
|
||||
// AccountLines
|
||||
JSS(ledger_hit_rate); // out: GetCounts
|
||||
JSS(ledger_index); // in/out: many
|
||||
JSS(ledger_index_max); // in, out: AccountTx*
|
||||
JSS(ledger_index_min); // in, out: AccountTx*
|
||||
JSS(ledger_max); // in, out: AccountTx*
|
||||
JSS(ledger_min); // in, out: AccountTx*
|
||||
JSS(ledger_time); // out: NetworkOPs
|
||||
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
|
||||
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
|
||||
JSS(levels); // LogLevels
|
||||
JSS(confidential_balance_inbox); // out: mpt_holders (confidential MPT)
|
||||
JSS(confidential_balance_spending); // out: mpt_holders (confidential MPT)
|
||||
JSS(confidential_balance_version); // out: mpt_holders (confidential MPT)
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
JSS(converge_time); // out: NetworkOPs
|
||||
JSS(converge_time_s); // out: NetworkOPs
|
||||
JSS(cookie); // out: NetworkOPs
|
||||
JSS(count); // in: AccountTx*, ValidatorList
|
||||
JSS(counters); // in/out: retrieve counters
|
||||
JSS(credentials); // in: deposit_authorized
|
||||
JSS(credential_type); // in: LedgerEntry DepositPreauth
|
||||
JSS(ctid); // in/out: Tx RPC
|
||||
JSS(currency_a); // out: BookChanges
|
||||
JSS(currency_b); // out: BookChanges
|
||||
JSS(currency); // in: paths/PathRequest, STAmount
|
||||
// out: STPathSet, STAmount, AccountLines
|
||||
JSS(current); // out: OwnerInfo
|
||||
JSS(current_activities); //
|
||||
JSS(current_ledger_size); // out: TxQ
|
||||
JSS(current_queue_size); // out: TxQ
|
||||
JSS(data); // out: LedgerData
|
||||
JSS(date); // out: tx/Transaction, NetworkOPs
|
||||
JSS(dbKBLedger); // out: getCounts
|
||||
JSS(dbKBTotal); // out: getCounts
|
||||
JSS(dbKBTransaction); // out: getCounts
|
||||
JSS(debug_signing); // in: TransactionSign
|
||||
JSS(deletion_blockers_only); // in: AccountObjects
|
||||
JSS(delivered_amount); // out: insertDeliveredAmount
|
||||
JSS(deposit_authorized); // out: deposit_authorized
|
||||
JSS(deprecated); //
|
||||
JSS(descending); // in: AccountTx*
|
||||
JSS(description); // in/out: Reservations
|
||||
JSS(destination); // in: nft_buy_offers, nft_sell_offers
|
||||
JSS(destination_account); // in: PathRequest, RipplePathFind, account_lines
|
||||
// out: AccountChannels
|
||||
JSS(destination_amount); // in: PathRequest, RipplePathFind
|
||||
JSS(destination_currencies); // in: PathRequest, RipplePathFind
|
||||
JSS(destination_tag); // in: PathRequest
|
||||
// out: AccountChannels
|
||||
JSS(details); // out: Manifest, server_info
|
||||
JSS(dir_entry); // out: DirectoryEntryIterator
|
||||
JSS(dir_index); // out: DirectoryEntryIterator
|
||||
JSS(dir_root); // out: DirectoryEntryIterator
|
||||
JSS(discounted_fee); // out: amm_info
|
||||
JSS(domain); // out: ValidatorInfo, Manifest
|
||||
JSS(drops); // out: TxQ
|
||||
JSS(duration_us); // out: NetworkOPs
|
||||
JSS(effective); // out: ValidatorList
|
||||
// in: UNL
|
||||
JSS(enabled); // out: AmendmentTable
|
||||
JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit
|
||||
JSS(entire_set); // out: get_aggregate_price
|
||||
JSS(ephemeral_key); // out: ValidatorInfo
|
||||
// in/out: Manifest
|
||||
JSS(error); // out: error
|
||||
JSS(errored); //
|
||||
JSS(error_code); // out: error
|
||||
JSS(error_exception); // out: Submit
|
||||
JSS(error_message); // out: error
|
||||
JSS(expand); // in: handler/Ledger
|
||||
JSS(expected_date); // out: any (warnings)
|
||||
JSS(expected_date_UTC); // out: any (warnings)
|
||||
JSS(expected_ledger_size); // out: TxQ
|
||||
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
|
||||
JSS(fail_hard); // in: Sign, Submit
|
||||
JSS(failed); // out: InboundLedger
|
||||
JSS(feature); // in: Feature
|
||||
JSS(features); // out: Feature
|
||||
JSS(fee_base); // out: NetworkOPs
|
||||
JSS(fee_div_max); // in: TransactionSign
|
||||
JSS(fee_level); // out: AccountInfo
|
||||
JSS(fee_mult_max); // in: TransactionSign
|
||||
JSS(fee_ref); // out: NetworkOPs, DEPRECATED
|
||||
JSS(fetch_pack); // out: NetworkOPs
|
||||
JSS(FIELDS); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(first); // out: rpc/Version
|
||||
JSS(finished); //
|
||||
JSS(fix_txns); // in: LedgerCleaner
|
||||
JSS(flags); // out: AccountOffers, NetworkOPs
|
||||
JSS(forward); // in: AccountTx
|
||||
JSS(freeze); // out: AccountLines
|
||||
JSS(freeze_peer); // out: AccountLines
|
||||
JSS(deep_freeze); // out: AccountLines
|
||||
JSS(deep_freeze_peer); // out: AccountLines
|
||||
JSS(frozen_balances); // out: GatewayBalances
|
||||
JSS(full); // in: LedgerClearer, handlers/Ledger
|
||||
JSS(full_reply); // out: PathFind
|
||||
JSS(fullbelow_size); // out: GetCounts
|
||||
JSS(git); // out: server_info
|
||||
JSS(good); // out: RPCVersion
|
||||
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
|
||||
JSS(have_header); // out: InboundLedger
|
||||
JSS(have_state); // out: InboundLedger
|
||||
JSS(have_transactions); // out: InboundLedger
|
||||
JSS(high); // out: BookChanges
|
||||
JSS(highest_sequence); // out: AccountInfo
|
||||
JSS(highest_ticket); // out: AccountInfo
|
||||
JSS(historical_perminute); // historical_perminute.
|
||||
JSS(holders); // out: MPTHolders
|
||||
JSS(holder_encryption_key); // out: mpt_holders (confidential MPT)
|
||||
JSS(hostid); // out: NetworkOPs
|
||||
JSS(hotwallet); // in: GatewayBalances
|
||||
JSS(id); // websocket.
|
||||
JSS(ident); // in: AccountCurrencies, AccountInfo, OwnerInfo
|
||||
JSS(ignore_default); // in: AccountLines
|
||||
JSS(in); // out: OverlayImpl
|
||||
JSS(inLedger); // out: tx/Transaction
|
||||
JSS(inbound); // out: PeerImp
|
||||
JSS(index); // in: LedgerEntry
|
||||
// out: STLedgerEntry, LedgerEntry, TxHistory, LedgerData
|
||||
JSS(info); // out: ServerInfo, ConsensusInfo, FetchInfo
|
||||
JSS(initial_sync_duration_us); //
|
||||
JSS(internal_command); // in: Internal
|
||||
JSS(invalid_API_version); // out: Many, when a request has an invalid version
|
||||
JSS(io_latency_ms); // out: NetworkOPs
|
||||
JSS(ip); // in: Connect, out: OverlayImpl
|
||||
JSS(is_burned); // out: nft_info (clio)
|
||||
JSS(isSerialized); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(isSigningField); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(isVLEncoded); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(issuer); // in: RipplePathFind, Subscribe, Unsubscribe, BookOffers
|
||||
// out: STPathSet, STAmount
|
||||
JSS(issuer_encrypted_balance); // out: mpt_holders (confidential MPT)
|
||||
JSS(job); //
|
||||
JSS(job_queue); //
|
||||
JSS(jobs); //
|
||||
JSS(jsonrpc); // json version
|
||||
JSS(jq_trans_overflow); // JobQueue transaction limit overflow.
|
||||
JSS(kept); // out: SubmitTransaction
|
||||
JSS(key); // out
|
||||
JSS(key_type); // in/out: WalletPropose, TransactionSign
|
||||
JSS(latency); // out: PeerImp
|
||||
JSS(last); // out: RPCVersion
|
||||
JSS(last_close); // out: NetworkOPs
|
||||
JSS(last_refresh_time); // out: ValidatorSite
|
||||
JSS(last_refresh_status); // out: ValidatorSite
|
||||
JSS(last_refresh_message); // out: ValidatorSite
|
||||
JSS(ledger); // in: NetworkOPs, LedgerCleaner, RPCHelpers
|
||||
// out: NetworkOPs, PeerImp
|
||||
JSS(ledger_current_index); // out: NetworkOPs, RPCHelpers, LedgerCurrent, LedgerAccept,
|
||||
// AccountLines
|
||||
JSS(ledger_data); // out: LedgerHeader
|
||||
JSS(ledger_hash); // in: RPCHelpers, LedgerRequest, RipplePathFind,
|
||||
// TransactionEntry, handlers/Ledger
|
||||
// out: NetworkOPs, RPCHelpers, LedgerClosed, LedgerData,
|
||||
// AccountLines
|
||||
JSS(ledger_hit_rate); // out: GetCounts
|
||||
JSS(ledger_index); // in/out: many
|
||||
JSS(ledger_index_max); // in, out: AccountTx*
|
||||
JSS(ledger_index_min); // in, out: AccountTx*
|
||||
JSS(ledger_max); // in, out: AccountTx*
|
||||
JSS(ledger_min); // in, out: AccountTx*
|
||||
JSS(ledger_time); // out: NetworkOPs
|
||||
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
|
||||
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
|
||||
JSS(levels); // LogLevels
|
||||
JSS(limit); // in/out: AccountTx*, AccountOffers, AccountLines, AccountObjects
|
||||
// in: LedgerData, BookOffers
|
||||
JSS(limit_peer); // out: AccountLines
|
||||
@@ -401,6 +404,11 @@ JSS(min_ledger); // in: LedgerCleaner
|
||||
JSS(minimum_fee); // out: TxQ
|
||||
JSS(minimum_level); // out: TxQ
|
||||
JSS(missingCommand); // error
|
||||
JSS(mpt_amount); // out: mpt_holders
|
||||
JSS(mpt_issuance_id); // in: Payment, mpt_holders
|
||||
JSS(mptoken_index); // out: mpt_holders
|
||||
JSS(mpt_issuance_id_a); // out: BookChanges
|
||||
JSS(mpt_issuance_id_b); // out: BookChanges
|
||||
JSS(name); // out: AmendmentTableImpl, PeerImp
|
||||
JSS(needed_state_hashes); // out: InboundLedger
|
||||
JSS(needed_transaction_hashes); // out: InboundLedger
|
||||
|
||||
@@ -117,6 +117,30 @@ public:
|
||||
return this->sle_->isFieldPresent(sfTakerPaysIssuer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerPaysMPT (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT192::type::value_type>
|
||||
getTakerPaysMPT() const
|
||||
{
|
||||
if (hasTakerPaysMPT())
|
||||
return this->sle_->at(sfTakerPaysMPT);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfTakerPaysMPT is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasTakerPaysMPT() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfTakerPaysMPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerGetsCurrency (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -165,6 +189,30 @@ public:
|
||||
return this->sle_->isFieldPresent(sfTakerGetsIssuer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerGetsMPT (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT192::type::value_type>
|
||||
getTakerGetsMPT() const
|
||||
{
|
||||
if (hasTakerGetsMPT())
|
||||
return this->sle_->at(sfTakerGetsMPT);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfTakerGetsMPT is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasTakerGetsMPT() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfTakerGetsMPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfExchangeRate (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -427,6 +475,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerPaysMPT (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
DirectoryNodeBuilder&
|
||||
setTakerPaysMPT(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfTakerPaysMPT] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerGetsCurrency (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
@@ -449,6 +508,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerGetsMPT (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
DirectoryNodeBuilder&
|
||||
setTakerGetsMPT(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfTakerGetsMPT] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfExchangeRate (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -147,6 +147,150 @@ public:
|
||||
{
|
||||
return this->sle_->at(sfPreviousTxnLgrSeq);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfConfidentialBalanceInbox (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getConfidentialBalanceInbox() const
|
||||
{
|
||||
if (hasConfidentialBalanceInbox())
|
||||
return this->sle_->at(sfConfidentialBalanceInbox);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfConfidentialBalanceInbox is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasConfidentialBalanceInbox() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfConfidentialBalanceInbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfConfidentialBalanceSpending (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getConfidentialBalanceSpending() const
|
||||
{
|
||||
if (hasConfidentialBalanceSpending())
|
||||
return this->sle_->at(sfConfidentialBalanceSpending);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfConfidentialBalanceSpending is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasConfidentialBalanceSpending() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfConfidentialBalanceSpending);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfConfidentialBalanceVersion (soeDEFAULT)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getConfidentialBalanceVersion() const
|
||||
{
|
||||
if (hasConfidentialBalanceVersion())
|
||||
return this->sle_->at(sfConfidentialBalanceVersion);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfConfidentialBalanceVersion is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasConfidentialBalanceVersion() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfConfidentialBalanceVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptedBalance (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getIssuerEncryptedBalance() const
|
||||
{
|
||||
if (hasIssuerEncryptedBalance())
|
||||
return this->sle_->at(sfIssuerEncryptedBalance);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerEncryptedBalance is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerEncryptedBalance() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfIssuerEncryptedBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptedBalance (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptedBalance() const
|
||||
{
|
||||
if (hasAuditorEncryptedBalance())
|
||||
return this->sle_->at(sfAuditorEncryptedBalance);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptedBalance is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptedBalance() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfAuditorEncryptedBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolderEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getHolderEncryptionKey() const
|
||||
{
|
||||
if (hasHolderEncryptionKey())
|
||||
return this->sle_->at(sfHolderEncryptionKey);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfHolderEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasHolderEncryptionKey() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfHolderEncryptionKey);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -270,6 +414,72 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfConfidentialBalanceInbox (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setConfidentialBalanceInbox(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfConfidentialBalanceInbox] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfConfidentialBalanceSpending (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setConfidentialBalanceSpending(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfConfidentialBalanceSpending] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfConfidentialBalanceVersion (soeDEFAULT)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setConfidentialBalanceVersion(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfConfidentialBalanceVersion] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptedBalance (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setIssuerEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptedBalance] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptedBalance (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setAuditorEncryptedBalance(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptedBalance] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolderEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolderEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the completed MPToken wrapper.
|
||||
* @param index The ledger entry index.
|
||||
|
||||
@@ -278,6 +278,78 @@ public:
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfMutableFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getIssuerEncryptionKey() const
|
||||
{
|
||||
if (hasIssuerEncryptionKey())
|
||||
return this->sle_->at(sfIssuerEncryptionKey);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerEncryptionKey() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfIssuerEncryptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptionKey() const
|
||||
{
|
||||
if (hasAuditorEncryptionKey())
|
||||
return this->sle_->at(sfAuditorEncryptionKey);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptionKey() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfAuditorEncryptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfConfidentialOutstandingAmount (soeDEFAULT)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT64::type::value_type>
|
||||
getConfidentialOutstandingAmount() const
|
||||
{
|
||||
if (hasConfidentialOutstandingAmount())
|
||||
return this->sle_->at(sfConfidentialOutstandingAmount);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfConfidentialOutstandingAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasConfidentialOutstandingAmount() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfConfidentialOutstandingAmount);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -469,6 +541,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenIssuanceBuilder&
|
||||
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenIssuanceBuilder&
|
||||
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfConfidentialOutstandingAmount (soeDEFAULT)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenIssuanceBuilder&
|
||||
setConfidentialOutstandingAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
|
||||
{
|
||||
object_[sfConfidentialOutstandingAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the completed MPTokenIssuance wrapper.
|
||||
* @param index The ledger entry index.
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -192,6 +194,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMBidBuilder&
|
||||
@@ -203,6 +206,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMBidBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class AMMClawbackBuilder;
|
||||
* Type: ttAMM_CLAWBACK (31)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureAMMClawback
|
||||
* Privileges: mayDeleteAcct | overrideFreeze
|
||||
* Privileges: mayDeleteAcct | overrideFreeze | mayAuthorizeMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use AMMClawbackBuilder to construct new transactions.
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -71,6 +72,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -82,6 +84,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -166,6 +169,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMClawbackBuilder&
|
||||
@@ -177,6 +181,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMClawbackBuilder&
|
||||
@@ -188,6 +193,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMClawbackBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class AMMCreateBuilder;
|
||||
* Type: ttAMM_CREATE (35)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureAMM
|
||||
* Privileges: createPseudoAcct
|
||||
* Privileges: createPseudoAcct | mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use AMMCreateBuilder to construct new transactions.
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -129,6 +131,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMCreateBuilder&
|
||||
@@ -140,6 +143,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMCreateBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class AMMDeleteBuilder;
|
||||
* Type: ttAMM_DELETE (40)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureAMM
|
||||
* Privileges: mustDeleteAcct
|
||||
* Privileges: mustDeleteAcct | mayDeleteMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use AMMDeleteBuilder to construct new transactions.
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -116,6 +118,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDeleteBuilder&
|
||||
@@ -127,6 +130,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDeleteBuilder&
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -97,6 +100,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount2 (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -246,6 +250,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDepositBuilder&
|
||||
@@ -257,6 +262,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDepositBuilder&
|
||||
@@ -268,6 +274,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDepositBuilder&
|
||||
@@ -279,6 +286,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount2 (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMDepositBuilder&
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -129,6 +131,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMVoteBuilder&
|
||||
@@ -140,6 +143,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMVoteBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class AMMWithdrawBuilder;
|
||||
* Type: ttAMM_WITHDRAW (37)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureAMM
|
||||
* Privileges: mayDeleteAcct
|
||||
* Privileges: mayDeleteAcct | mayAuthorizeMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use AMMWithdrawBuilder to construct new transactions.
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -97,6 +100,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount2 (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -220,6 +224,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMWithdrawBuilder&
|
||||
@@ -231,6 +236,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAsset2 (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMWithdrawBuilder&
|
||||
@@ -242,6 +248,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMWithdrawBuilder&
|
||||
@@ -253,6 +260,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount2 (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
AMMWithdrawBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class CheckCashBuilder;
|
||||
* Type: ttCHECK_CASH (17)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: uint256{}
|
||||
* Privileges: noPriv
|
||||
* Privileges: mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use CheckCashBuilder to construct new transactions.
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -86,6 +87,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfDeliverMin (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -166,6 +168,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
CheckCashBuilder&
|
||||
@@ -177,6 +180,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfDeliverMin (soeOPTIONAL)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
CheckCashBuilder&
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfSendMax (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -205,6 +206,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfSendMax (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
CheckCreateBuilder&
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTClawbackBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTClawback
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_CLAWBACK (89)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureConfidentialTransfer
|
||||
* Privileges: noPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTClawbackBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTClawback : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CLAWBACK;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTClawback transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTClawback(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawback");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolder (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_ACCOUNT::type::value_type
|
||||
getHolder() const
|
||||
{
|
||||
return this->tx_->at(sfHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT64::type::value_type
|
||||
getMPTAmount() const
|
||||
{
|
||||
return this->tx_->at(sfMPTAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfZKProof (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getZKProof() const
|
||||
{
|
||||
return this->tx_->at(sfZKProof);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTClawback transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses Json::Value internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTClawbackBuilder : public TransactionBuilderBase<ConfidentialMPTClawbackBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTClawbackBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param holder The sfHolder field value.
|
||||
* @param mPTAmount The sfMPTAmount field value.
|
||||
* @param zKProof The sfZKProof field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& holder, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTClawbackBuilder>(ttCONFIDENTIAL_MPT_CLAWBACK, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
setHolder(holder);
|
||||
setMPTAmount(mPTAmount);
|
||||
setZKProof(zKProof);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTClawbackBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CLAWBACK)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawbackBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/** @brief Transaction-specific field setters */
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolder (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder&
|
||||
setHolder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolder] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder&
|
||||
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfZKProof (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTClawbackBuilder&
|
||||
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfZKProof] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTClawback wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTClawback
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTClawback{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
@@ -0,0 +1,336 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTConvertBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTConvert
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_CONVERT (85)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureConfidentialTransfer
|
||||
* Privileges: noPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTConvertBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTConvert : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTConvert transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTConvert(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvert");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT64::type::value_type
|
||||
getMPTAmount() const
|
||||
{
|
||||
return this->tx_->at(sfMPTAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolderEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getHolderEncryptionKey() const
|
||||
{
|
||||
if (hasHolderEncryptionKey())
|
||||
{
|
||||
return this->tx_->at(sfHolderEncryptionKey);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfHolderEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasHolderEncryptionKey() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfHolderEncryptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolderEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getHolderEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfHolderEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getIssuerEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfIssuerEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptedAmount() const
|
||||
{
|
||||
if (hasAuditorEncryptedAmount())
|
||||
{
|
||||
return this->tx_->at(sfAuditorEncryptedAmount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptedAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBlindingFactor (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT256::type::value_type
|
||||
getBlindingFactor() const
|
||||
{
|
||||
return this->tx_->at(sfBlindingFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfZKProof (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getZKProof() const
|
||||
{
|
||||
if (hasZKProof())
|
||||
{
|
||||
return this->tx_->at(sfZKProof);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfZKProof is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasZKProof() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfZKProof);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTConvert transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses Json::Value internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTConvertBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTConvertBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param mPTAmount The sfMPTAmount field value.
|
||||
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
|
||||
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
|
||||
* @param blindingFactor The sfBlindingFactor field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTConvertBuilder>(ttCONFIDENTIAL_MPT_CONVERT, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
setMPTAmount(mPTAmount);
|
||||
setHolderEncryptedAmount(holderEncryptedAmount);
|
||||
setIssuerEncryptedAmount(issuerEncryptedAmount);
|
||||
setBlindingFactor(blindingFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTConvertBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/** @brief Transaction-specific field setters */
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolderEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolderEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolderEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolderEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBlindingFactor (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBlindingFactor] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfZKProof (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBuilder&
|
||||
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfZKProof] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTConvert wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTConvert
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTConvert{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
@@ -0,0 +1,310 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTConvertBackBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTConvertBack
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureConfidentialTransfer
|
||||
* Privileges: noPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTConvertBackBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTConvertBack : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT_BACK;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTConvertBack transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTConvertBack(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBack");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT64::type::value_type
|
||||
getMPTAmount() const
|
||||
{
|
||||
return this->tx_->at(sfMPTAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolderEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getHolderEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfHolderEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getIssuerEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfIssuerEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptedAmount() const
|
||||
{
|
||||
if (hasAuditorEncryptedAmount())
|
||||
{
|
||||
return this->tx_->at(sfAuditorEncryptedAmount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptedAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBlindingFactor (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT256::type::value_type
|
||||
getBlindingFactor() const
|
||||
{
|
||||
return this->tx_->at(sfBlindingFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfZKProof (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getZKProof() const
|
||||
{
|
||||
return this->tx_->at(sfZKProof);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBalanceCommitment (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getBalanceCommitment() const
|
||||
{
|
||||
return this->tx_->at(sfBalanceCommitment);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTConvertBack transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses Json::Value internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTConvertBackBuilder : public TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTConvertBackBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param mPTAmount The sfMPTAmount field value.
|
||||
* @param holderEncryptedAmount The sfHolderEncryptedAmount field value.
|
||||
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
|
||||
* @param blindingFactor The sfBlindingFactor field value.
|
||||
* @param zKProof The sfZKProof field value.
|
||||
* @param balanceCommitment The sfBalanceCommitment field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_UINT64::type::value_type> const& mPTAmount, std::decay_t<typename SF_VL::type::value_type> const& holderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_UINT256::type::value_type> const& blindingFactor, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTConvertBackBuilder>(ttCONFIDENTIAL_MPT_CONVERT_BACK, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
setMPTAmount(mPTAmount);
|
||||
setHolderEncryptedAmount(holderEncryptedAmount);
|
||||
setIssuerEncryptedAmount(issuerEncryptedAmount);
|
||||
setBlindingFactor(blindingFactor);
|
||||
setZKProof(zKProof);
|
||||
setBalanceCommitment(balanceCommitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTConvertBackBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT_BACK)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBackBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/** @brief Transaction-specific field setters */
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setMPTAmount(std::decay_t<typename SF_UINT64::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolderEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setHolderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolderEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBlindingFactor (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setBlindingFactor(std::decay_t<typename SF_UINT256::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBlindingFactor] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfZKProof (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfZKProof] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBalanceCommitment (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTConvertBackBuilder&
|
||||
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBalanceCommitment] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTConvertBack wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTConvertBack
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTConvertBack{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
@@ -0,0 +1,129 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTMergeInboxBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTMergeInbox
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureConfidentialTransfer
|
||||
* Privileges: noPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTMergeInboxBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTMergeInbox : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MERGE_INBOX;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTMergeInbox transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTMergeInbox(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInbox");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTMergeInbox transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses Json::Value internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTMergeInboxBuilder : public TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTMergeInboxBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTMergeInboxBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTMergeInboxBuilder>(ttCONFIDENTIAL_MPT_MERGE_INBOX, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTMergeInboxBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTMergeInboxBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MERGE_INBOX)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInboxBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/** @brief Transaction-specific field setters */
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMergeInboxBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTMergeInbox wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTMergeInbox
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTMergeInbox{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
371
include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h
Normal file
371
include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h
Normal file
@@ -0,0 +1,371 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTSendBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTSend
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_SEND (88)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: featureConfidentialTransfer
|
||||
* Privileges: noPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTSendBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTSend : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_SEND;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTSend transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTSend(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSend");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestination (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_ACCOUNT::type::value_type
|
||||
getDestination() const
|
||||
{
|
||||
return this->tx_->at(sfDestination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfSenderEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getSenderEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfSenderEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestinationEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getDestinationEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfDestinationEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getIssuerEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->at(sfIssuerEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptedAmount() const
|
||||
{
|
||||
if (hasAuditorEncryptedAmount())
|
||||
{
|
||||
return this->tx_->at(sfAuditorEncryptedAmount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptedAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfZKProof (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getZKProof() const
|
||||
{
|
||||
return this->tx_->at(sfZKProof);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAmountCommitment (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getAmountCommitment() const
|
||||
{
|
||||
return this->tx_->at(sfAmountCommitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBalanceCommitment (soeREQUIRED)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getBalanceCommitment() const
|
||||
{
|
||||
return this->tx_->at(sfBalanceCommitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfCredentialIDs (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
|
||||
getCredentialIDs() const
|
||||
{
|
||||
if (hasCredentialIDs())
|
||||
{
|
||||
return this->tx_->at(sfCredentialIDs);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfCredentialIDs is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasCredentialIDs() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfCredentialIDs);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTSend transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses Json::Value internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTSendBuilder : public TransactionBuilderBase<ConfidentialMPTSendBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTSendBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param destination The sfDestination field value.
|
||||
* @param senderEncryptedAmount The sfSenderEncryptedAmount field value.
|
||||
* @param destinationEncryptedAmount The sfDestinationEncryptedAmount field value.
|
||||
* @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value.
|
||||
* @param zKProof The sfZKProof field value.
|
||||
* @param amountCommitment The sfAmountCommitment field value.
|
||||
* @param balanceCommitment The sfBalanceCommitment field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_ACCOUNT::type::value_type> const& destination, std::decay_t<typename SF_VL::type::value_type> const& senderEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& destinationEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& issuerEncryptedAmount, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::decay_t<typename SF_VL::type::value_type> const& amountCommitment, std::decay_t<typename SF_VL::type::value_type> const& balanceCommitment, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTSendBuilder>(ttCONFIDENTIAL_MPT_SEND, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
setDestination(destination);
|
||||
setSenderEncryptedAmount(senderEncryptedAmount);
|
||||
setDestinationEncryptedAmount(destinationEncryptedAmount);
|
||||
setIssuerEncryptedAmount(issuerEncryptedAmount);
|
||||
setZKProof(zKProof);
|
||||
setAmountCommitment(amountCommitment);
|
||||
setBalanceCommitment(balanceCommitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTSendBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_SEND)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTSendBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/** @brief Transaction-specific field setters */
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestination (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setDestination(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestination] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfSenderEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setSenderEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfSenderEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestinationEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setDestinationEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestinationEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptedAmount (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptedAmount (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfZKProof (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfZKProof] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAmountCommitment (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setAmountCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAmountCommitment] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBalanceCommitment (soeREQUIRED)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setBalanceCommitment(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBalanceCommitment] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfCredentialIDs (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTSendBuilder&
|
||||
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
|
||||
{
|
||||
object_[sfCredentialIDs] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTSend wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTSend
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTSend{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
@@ -187,6 +187,58 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfMutableFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getIssuerEncryptionKey() const
|
||||
{
|
||||
if (hasIssuerEncryptionKey())
|
||||
{
|
||||
return this->tx_->at(sfIssuerEncryptionKey);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerEncryptionKey() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfIssuerEncryptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptionKey (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptionKey() const
|
||||
{
|
||||
if (hasAuditorEncryptionKey())
|
||||
{
|
||||
return this->tx_->at(sfAuditorEncryptionKey);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptionKey is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptionKey() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfAuditorEncryptionKey);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -297,6 +349,28 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenIssuanceSetBuilder&
|
||||
setIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptionKey (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenIssuanceSetBuilder&
|
||||
setAuditorEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptionKey] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the MPTokenIssuanceSet wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -21,7 +21,7 @@ class OfferCreateBuilder;
|
||||
* Type: ttOFFER_CREATE (7)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: uint256{}
|
||||
* Privileges: noPriv
|
||||
* Privileges: mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use OfferCreateBuilder to construct new transactions.
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerPays (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerGets (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -194,6 +196,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerPays (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
OfferCreateBuilder&
|
||||
@@ -205,6 +208,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerGets (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
OfferCreateBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class PaymentBuilder;
|
||||
* Type: ttPAYMENT (0)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: uint256{}
|
||||
* Privileges: createAcct
|
||||
* Privileges: createAcct | mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use PaymentBuilder to construct new transactions.
|
||||
|
||||
@@ -398,7 +398,9 @@ using InvariantChecks = std::tuple<
|
||||
ValidPseudoAccounts,
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault>;
|
||||
ValidVault,
|
||||
ValidConfidentialMPToken,
|
||||
ValidMPTPayment>;
|
||||
|
||||
/**
|
||||
* @brief get a tuple of all invariant checks
|
||||
|
||||
@@ -44,6 +44,7 @@ enum Privilege {
|
||||
mayDeleteMPT = 0x0400, // The transaction MAY delete an MPT object. May not create.
|
||||
mustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
|
||||
mayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
|
||||
mayCreateMPT = 0x2000, // The transaction MAY create an MPT object, except for issuer.
|
||||
};
|
||||
|
||||
constexpr Privilege
|
||||
|
||||
@@ -28,4 +28,75 @@ public:
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
|
||||
};
|
||||
|
||||
/** Verify:
|
||||
* - OutstandingAmount <= MaximumAmount for any MPT
|
||||
* - OutstandingAmount after = OutstandingAmount before +
|
||||
* sum (MPT after - MPT before) - this is total MPT credit/debit
|
||||
*/
|
||||
class ValidMPTPayment
|
||||
{
|
||||
enum Order { Before = 0, After = 1 };
|
||||
struct MPTData
|
||||
{
|
||||
std::array<std::int64_t, After + 1> outstanding{};
|
||||
// sum (MPT after - MPT before)
|
||||
std::int64_t mptAmount{0};
|
||||
};
|
||||
|
||||
// true if OutstandingAmount > MaximumAmount in after for any MPT
|
||||
bool overflow_{false};
|
||||
// mptid:MPTData
|
||||
hash_map<uint192, MPTData> data_;
|
||||
|
||||
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: Confidential MPToken consistency
|
||||
*
|
||||
* - Convert/ConvertBack symmetry:
|
||||
* Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X)
|
||||
* - Cannot delete MPToken with non-zero confidential state:
|
||||
* Cannot delete if sfIssuerEncryptedBalance exists
|
||||
* Cannot delete if sfConfidentialBalanceInbox and sfConfidentialBalanceSpending exist
|
||||
* - Privacy flag consistency:
|
||||
* MPToken can only have encrypted fields if lsfMPTCanConfidentialAmount is set on
|
||||
* issuance.
|
||||
* - Encrypted field existence consistency:
|
||||
* If sfConfidentialBalanceSpending/sfConfidentialBalanceInbox exists, then
|
||||
* sfIssuerEncryptedBalance must also exist (and vice versa).
|
||||
* - COA <= OutstandingAmount:
|
||||
* Confidential outstanding balance cannot exceed total outstanding.
|
||||
* - Verifies sfConfidentialBalanceVersion is changed whenever sfConfidentialBalanceSpending is
|
||||
* modified on an MPToken.
|
||||
*/
|
||||
class ValidConfidentialMPToken
|
||||
{
|
||||
struct Changes
|
||||
{
|
||||
std::int64_t mptAmountDelta = 0;
|
||||
std::int64_t coaDelta = 0;
|
||||
std::int64_t outstandingDelta = 0;
|
||||
SLE::const_pointer issuance;
|
||||
bool deletedWithEncrypted = false;
|
||||
bool badConsistency = false;
|
||||
bool badCOA = false;
|
||||
bool requiresPrivacyFlag = false;
|
||||
bool badVersion = false;
|
||||
};
|
||||
std::map<uint192, Changes> changes_;
|
||||
|
||||
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&);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AMMUtils.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/tx/transactors/dex/AMMContext.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename TIn, typename TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
class AMMOffer;
|
||||
|
||||
/** AMMLiquidity class provides AMM offers to BookStep class.
|
||||
@@ -35,8 +35,8 @@ private:
|
||||
AMMContext& ammContext_;
|
||||
AccountID const ammAccountID_;
|
||||
std::uint32_t const tradingFee_;
|
||||
Issue const issueIn_;
|
||||
Issue const issueOut_;
|
||||
Asset const assetIn_;
|
||||
Asset const assetOut_;
|
||||
// Initial AMM pool balances
|
||||
TAmounts<TIn, TOut> const initialBalances_;
|
||||
beast::Journal const j_;
|
||||
@@ -46,8 +46,8 @@ public:
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
std::uint32_t tradingFee,
|
||||
Issue const& in,
|
||||
Issue const& out,
|
||||
Asset const& in,
|
||||
Asset const& out,
|
||||
AMMContext& ammContext,
|
||||
beast::Journal j);
|
||||
~AMMLiquidity() = default;
|
||||
@@ -87,16 +87,16 @@ public:
|
||||
return ammContext_;
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueIn() const
|
||||
Asset const&
|
||||
assetIn() const
|
||||
{
|
||||
return issueIn_;
|
||||
return assetIn_;
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueOut() const
|
||||
Asset const&
|
||||
assetOut() const
|
||||
{
|
||||
return issueOut_;
|
||||
return assetOut_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
@@ -16,7 +17,7 @@ class QualityFunction;
|
||||
* methods for use in generic BookStep methods. AMMOffer amounts
|
||||
* are changed indirectly in BookStep limiting steps.
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
class AMMOffer
|
||||
{
|
||||
private:
|
||||
@@ -52,8 +53,11 @@ public:
|
||||
return quality_;
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueIn() const;
|
||||
Asset const&
|
||||
assetIn() const;
|
||||
|
||||
Asset const&
|
||||
assetOut() const;
|
||||
|
||||
AccountID const&
|
||||
owner() const;
|
||||
@@ -99,7 +103,8 @@ public:
|
||||
static TER
|
||||
send(Args&&... args)
|
||||
{
|
||||
return accountSend(std::forward<Args>(args)..., WaiveTransferFee::Yes);
|
||||
return accountSend(
|
||||
std::forward<Args>(args)..., WaiveTransferFee::Yes, AllowMPTOverflow::Yes);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -12,28 +14,15 @@
|
||||
|
||||
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>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
class TOffer
|
||||
{
|
||||
private:
|
||||
SLE::pointer m_entry;
|
||||
Quality m_quality{};
|
||||
AccountID m_account;
|
||||
Asset assetIn_;
|
||||
Asset assetOut_;
|
||||
|
||||
TAmounts<TIn, TOut> m_amounts{};
|
||||
void
|
||||
@@ -113,10 +102,10 @@ public:
|
||||
return m_entry->key();
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueIn() const;
|
||||
Issue const&
|
||||
issueOut() const;
|
||||
Asset const&
|
||||
assetIn() const;
|
||||
Asset const&
|
||||
assetOut() const;
|
||||
|
||||
TAmounts<TIn, TOut>
|
||||
limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const;
|
||||
@@ -131,8 +120,8 @@ public:
|
||||
bool
|
||||
isFunded() const
|
||||
{
|
||||
// Offer owner is issuer; they have unlimited funds
|
||||
return m_account == issueOut().account;
|
||||
// Offer owner is issuer; they have unlimited funds if IOU
|
||||
return m_account == assetOut_.getIssuer() && assetOut_.holds<Issue>();
|
||||
}
|
||||
|
||||
static std::pair<std::uint32_t, std::uint32_t>
|
||||
@@ -167,9 +156,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using Offer = TOffer<>;
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
TOffer<TIn, TOut>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
: m_entry(entry), m_quality(quality), m_account(m_entry->getAccountID(sfAccount))
|
||||
{
|
||||
@@ -177,33 +164,26 @@ TOffer<TIn, TOut>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
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();
|
||||
assetIn_ = tp.asset();
|
||||
assetOut_ = tg.asset();
|
||||
}
|
||||
|
||||
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>
|
||||
template <StepAmount TIn, StepAmount 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
|
||||
if constexpr (std::is_same_v<TIn, XRPAmount>)
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in));
|
||||
else
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, assetIn_));
|
||||
|
||||
if constexpr (std::is_same_v<TOut, XRPAmount>)
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out));
|
||||
else
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, assetOut_));
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp)
|
||||
const
|
||||
@@ -213,7 +193,7 @@ TOffer<TIn, TOut>::limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const&
|
||||
return quality().ceil_out_strict(offerAmount, limit, roundUp);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp)
|
||||
const
|
||||
@@ -228,75 +208,29 @@ TOffer<TIn, TOut>::limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& li
|
||||
return m_quality.ceil_in(offerAmount, limit);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
template <typename... Args>
|
||||
TER
|
||||
TOffer<TIn, TOut>::send(Args&&... args)
|
||||
{
|
||||
return accountSend(std::forward<Args>(args)...);
|
||||
return accountSend(std::forward<Args>(args)..., WaiveTransferFee::No, AllowMPTOverflow::Yes);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<STAmount, STAmount>::setFieldAmounts()
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
Asset const&
|
||||
TOffer<TIn, TOut>::assetIn() const
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, m_amounts.in);
|
||||
m_entry->setFieldAmount(sfTakerGets, m_amounts.out);
|
||||
return assetIn_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<IOUAmount, IOUAmount>::setFieldAmounts()
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
Asset const&
|
||||
TOffer<TIn, TOut>::assetOut() const
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, issIn_));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, issOut_));
|
||||
return assetOut_;
|
||||
}
|
||||
|
||||
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>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, TOffer<TIn, TOut> const& offer)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/tx/paths/BookTip.h>
|
||||
#include <xrpl/tx/paths/Offer.h>
|
||||
|
||||
@@ -11,7 +12,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
class TOfferStreamBase
|
||||
{
|
||||
public:
|
||||
@@ -64,6 +65,7 @@ protected:
|
||||
permRmOffer(uint256 const& offerIndex) = 0;
|
||||
|
||||
template <class TTakerPays, class TTakerGets>
|
||||
requires ValidTaker<TTakerPays, TTakerGets>
|
||||
bool
|
||||
shouldRmSmallIncreasedQOffer() const;
|
||||
|
||||
@@ -105,33 +107,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** 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.
|
||||
@@ -149,7 +124,7 @@ public:
|
||||
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>
|
||||
template <StepAmount TIn, StepAmount TOut>
|
||||
class FlowOfferStream : public TOfferStreamBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
#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
|
||||
|
||||
54
include/xrpl/tx/paths/detail/EitherAmount.h
Normal file
54
include/xrpl/tx/paths/detail/EitherAmount.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct EitherAmount
|
||||
{
|
||||
std::variant<XRPAmount, IOUAmount, MPTAmount> amount;
|
||||
|
||||
explicit EitherAmount() = default;
|
||||
|
||||
template <StepAmount T>
|
||||
explicit EitherAmount(T const& a) : amount(a)
|
||||
{
|
||||
}
|
||||
|
||||
template <StepAmount T>
|
||||
[[nodiscard]] bool
|
||||
holds() const
|
||||
{
|
||||
return std::holds_alternative<T>(amount);
|
||||
}
|
||||
|
||||
template <StepAmount T>
|
||||
[[nodiscard]] T const&
|
||||
get() const
|
||||
{
|
||||
if (!holds<T>())
|
||||
Throw<std::logic_error>("EitherAmount doesn't hold requested amount");
|
||||
return std::get<T>(amount);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, EitherAmount const& amt)
|
||||
{
|
||||
std::visit([&]<StepAmount T>(T const& a) { stream << to_string(a); }, amt.amount);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <StepAmount T>
|
||||
T const&
|
||||
get(EitherAmount const& amt)
|
||||
{
|
||||
return amt.get<T>();
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -208,14 +208,14 @@ struct FlowDebugInfo
|
||||
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);
|
||||
return xrpl::to_string(a.get<XRPAmount>());
|
||||
};
|
||||
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);
|
||||
return xrpl::to_string(a.get<IOUAmount>());
|
||||
};
|
||||
write_list(amts, get_val, delim);
|
||||
};
|
||||
|
||||
@@ -50,8 +50,7 @@ checkFreeze(
|
||||
if (!sleAmm)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (isLPTokenFrozen(
|
||||
view, src, (*sleAmm)[sfAsset].get<Issue>(), (*sleAmm)[sfAsset2].get<Issue>()))
|
||||
if (isLPTokenFrozen(view, src, (*sleAmm)[sfAsset], (*sleAmm)[sfAsset2]))
|
||||
{
|
||||
return terNO_LINE;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Concepts.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 <xrpl/tx/paths/detail/EitherAmount.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
@@ -44,6 +44,7 @@ issues(DebtDirection dir)
|
||||
BookStepIX is an IOU/XRP offer book
|
||||
BookStepXI is an XRP/IOU offer book
|
||||
XRPEndpointStep is the source or destination account for XRP
|
||||
MPTEndpointStep is the source or destination account for MPT
|
||||
|
||||
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
|
||||
@@ -339,8 +340,8 @@ std::pair<TER, STPath>
|
||||
normalizePath(
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
Asset const& deliver,
|
||||
std::optional<Asset> const& sendMaxAsset,
|
||||
STPath const& path);
|
||||
|
||||
/**
|
||||
@@ -355,7 +356,7 @@ normalizePath(
|
||||
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 sendMaxAsset 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.
|
||||
@@ -372,9 +373,9 @@ toStrand(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
Asset const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
std::optional<Asset> const& sendMaxAsset,
|
||||
STPath const& path,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
@@ -413,9 +414,9 @@ toStrands(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
Asset const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMax,
|
||||
std::optional<Asset> const& sendMax,
|
||||
STPathSet const& paths,
|
||||
bool addDefaultPath,
|
||||
bool ownerPaysTransferFee,
|
||||
@@ -425,7 +426,7 @@ toStrands(
|
||||
beast::Journal j);
|
||||
|
||||
/// @cond INTERNAL
|
||||
template <class TIn, class TOut, class TDerived>
|
||||
template <StepAmount TIn, StepAmount TOut, class TDerived>
|
||||
struct StepImp : public Step
|
||||
{
|
||||
explicit StepImp() = default;
|
||||
@@ -493,8 +494,16 @@ public:
|
||||
// Check equal with tolerance
|
||||
bool
|
||||
checkNear(IOUAmount const& expected, IOUAmount const& actual);
|
||||
bool
|
||||
checkNear(XRPAmount const& expected, XRPAmount const& actual);
|
||||
inline bool
|
||||
checkNear(MPTAmount const& expected, MPTAmount const& actual)
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
inline bool
|
||||
checkNear(XRPAmount const& expected, XRPAmount const& actual)
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
@@ -505,7 +514,7 @@ 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
|
||||
Asset const strandDeliver; ///< Asset 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
|
||||
@@ -522,11 +531,11 @@ struct StrandContext
|
||||
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;
|
||||
std::array<boost::container::flat_set<Asset>, 2>& seenDirectAssets;
|
||||
/** A strand may not include an offer that output the same issue more
|
||||
than once
|
||||
*/
|
||||
boost::container::flat_set<Issue>& seenBookOuts;
|
||||
boost::container::flat_set<Asset>& seenBookOuts;
|
||||
AMMContext& ammContext;
|
||||
std::optional<uint256> domainID; // the domain the order book will use
|
||||
beast::Journal const j;
|
||||
@@ -539,15 +548,15 @@ struct StrandContext
|
||||
// replicates the source or destination.
|
||||
AccountID const& strandSrc_,
|
||||
AccountID const& strandDst_,
|
||||
Issue const& strandDeliver_,
|
||||
Asset 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
|
||||
std::array<boost::container::flat_set<Asset>, 2>&
|
||||
seenDirectAssets_, ///< For detecting currency loops
|
||||
boost::container::flat_set<Asset>& seenBookOuts_, ///< For detecting book loops
|
||||
AMMContext& ammContext_,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j_); ///< Journal for logging
|
||||
@@ -563,6 +572,13 @@ directStepEqual(
|
||||
AccountID const& dst,
|
||||
Currency const& currency);
|
||||
|
||||
bool
|
||||
mptEndpointStepEqual(
|
||||
Step const& step,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
MPTID const& mptid);
|
||||
|
||||
bool
|
||||
xrpEndpointStepEqual(Step const& step, AccountID const& acc);
|
||||
|
||||
@@ -577,6 +593,13 @@ make_DirectStepI(
|
||||
AccountID const& dst,
|
||||
Currency const& c);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_MPTEndpointStep(
|
||||
StrandContext const& ctx,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
MPTID const& a);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out);
|
||||
|
||||
@@ -589,9 +612,30 @@ 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>
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepMM(StrandContext const& ctx, MPTIssue const& in, MPTIssue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepMX(StrandContext const& ctx, MPTIssue const& in);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepXM(StrandContext const& ctx, MPTIssue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepMI(StrandContext const& ctx, MPTIssue const& in, Issue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepIM(StrandContext const& ctx, Issue const& in, MPTIssue const& out);
|
||||
|
||||
template <StepAmount InAmt, StepAmount OutAmt>
|
||||
bool
|
||||
isDirectXrpToXrp(Strand const& strand);
|
||||
isDirectXrpToXrp(Strand const& strand)
|
||||
{
|
||||
if constexpr (std::is_same_v<InAmt, XRPAmount> && std::is_same_v<OutAmt, XRPAmount>)
|
||||
return strand.size() == 2;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -246,7 +246,7 @@ flow(
|
||||
EitherAmount stepIn(*strand[0]->cachedIn());
|
||||
for (auto i = 0; i < s; ++i)
|
||||
{
|
||||
bool valid;
|
||||
bool valid = false;
|
||||
std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
|
||||
if (!valid)
|
||||
{
|
||||
@@ -379,8 +379,10 @@ limitOut(
|
||||
return XRPAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
|
||||
return IOUAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, MPTAmount>)
|
||||
return MPTAmount{*out};
|
||||
else
|
||||
return STAmount{remainingOut.issue(), out->mantissa(), out->exponent()};
|
||||
return STAmount{remainingOut.asset(), out->mantissa(), out->exponent()};
|
||||
}();
|
||||
// A tiny difference could be due to the round off
|
||||
if (withinRelativeDistance(out, remainingOut, Number(1, -9)))
|
||||
@@ -534,7 +536,7 @@ public:
|
||||
@return Actual amount in and out from the strands, errors, and payment
|
||||
sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
template <StepAmount TInAmt, StepAmount TOutAmt>
|
||||
FlowResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(xrpl::PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ private:
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
Issue const& lptIssue,
|
||||
Asset const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
};
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ public:
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHanding,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
@@ -132,6 +133,7 @@ public:
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
@@ -141,8 +143,8 @@ public:
|
||||
Sandbox& sb,
|
||||
std::shared_ptr<SLE> const ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
beast::Journal const& journal);
|
||||
|
||||
private:
|
||||
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
ApplyFlags const flags,
|
||||
AccountID const id,
|
||||
beast::Journal const j,
|
||||
Issue const& issue);
|
||||
Asset const& asset);
|
||||
|
||||
// Use the payment flow code to perform offer crossing.
|
||||
std::pair<TER, Amounts>
|
||||
|
||||
@@ -33,23 +33,22 @@ public:
|
||||
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,
|
||||
});
|
||||
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
|
||||
|
||||
42
include/xrpl/tx/transactors/token/ConfidentialMPTClawback.h
Normal file
42
include/xrpl/tx/transactors/token/ConfidentialMPTClawback.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Allows an MPT issuer to clawback confidential balances from a holder.
|
||||
*
|
||||
* This transaction enables the issuer of an MPToken Issuance (with clawback
|
||||
* enabled) to reclaim confidential tokens from a holder's account. Unlike
|
||||
* regular clawback, the issuer cannot see the holder's balance directly.
|
||||
* Instead, the issuer must provide a zero-knowledge proof that demonstrates
|
||||
* they know the exact encrypted balance amount.
|
||||
*
|
||||
* @par Cryptographic Operations:
|
||||
* - **Equality Proof Verification**: Verifies that the issuer's revealed
|
||||
* amount matches the holder's encrypted balance using the issuer's
|
||||
* ElGamal private key.
|
||||
*
|
||||
* @see ConfidentialMPTSend, ConfidentialMPTConvert
|
||||
*/
|
||||
class ConfidentialMPTClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit ConfidentialMPTClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
44
include/xrpl/tx/transactors/token/ConfidentialMPTConvert.h
Normal file
44
include/xrpl/tx/transactors/token/ConfidentialMPTConvert.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Converts public (plaintext) MPT balance to confidential (encrypted)
|
||||
* balance.
|
||||
*
|
||||
* This transaction allows a token holder to convert their publicly visible
|
||||
* MPToken balance into an encrypted confidential balance. Once converted,
|
||||
* the balance can only be spent using ConfidentialMPTSend transactions and
|
||||
* remains hidden from public view on the ledger.
|
||||
*
|
||||
* @par Cryptographic Operations:
|
||||
* - **Schnorr Proof Verification**: When registering a new ElGamal public key,
|
||||
* verifies proof of knowledge of the corresponding private key.
|
||||
* - **Revealed Amount Verification**: Verifies that the provided encrypted
|
||||
* amounts (for holder, issuer, and optionally auditor) all encrypt the
|
||||
* same plaintext amount using the provided blinding factor.
|
||||
*
|
||||
* @see ConfidentialMPTConvertBack, ConfidentialMPTSend
|
||||
*/
|
||||
class ConfidentialMPTConvert : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit ConfidentialMPTConvert(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Converts confidential (encrypted) MPT balance back to public
|
||||
* (plaintext) balance.
|
||||
*
|
||||
* This transaction allows a token holder to convert their encrypted
|
||||
* confidential balance back into a publicly visible MPToken balance. The
|
||||
* holder must prove they have sufficient confidential balance without
|
||||
* revealing the actual balance amount.
|
||||
*
|
||||
* @par Cryptographic Operations:
|
||||
* - **Revealed Amount Verification**: Verifies that the provided encrypted
|
||||
* amounts correctly encrypt the conversion amount.
|
||||
* - **Pedersen Linkage Proof**: Verifies that the provided balance commitment
|
||||
* correctly links to the holder's encrypted spending balance.
|
||||
* - **Bulletproof Range Proof**: Verifies that the remaining balance (after
|
||||
* conversion) is non-negative, ensuring the holder has sufficient funds.
|
||||
*
|
||||
* @see ConfidentialMPTConvert, ConfidentialMPTSend
|
||||
*/
|
||||
class ConfidentialMPTConvertBack : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit ConfidentialMPTConvertBack(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Merges the confidential inbox balance into the spending balance.
|
||||
*
|
||||
* In the confidential transfer system, incoming funds are deposited into an
|
||||
* "inbox" balance that the recipient cannot immediately spend. This prevents
|
||||
* front-running attacks where an attacker could invalidate a pending
|
||||
* transaction by sending funds to the sender. This transaction merges the
|
||||
* inbox into the spending balance, making those funds available for spending.
|
||||
*
|
||||
* @par Cryptographic Operations:
|
||||
* - **Homomorphic Addition**: Adds the encrypted inbox balance to the
|
||||
* encrypted spending balance using ElGamal homomorphic properties.
|
||||
* - **Zero Encryption**: Resets the inbox to an encryption of zero.
|
||||
*
|
||||
* @note This transaction requires no zero-knowledge proofs because it only
|
||||
* combines encrypted values that the holder already owns. The
|
||||
* homomorphic properties of ElGamal encryption ensure correctness.
|
||||
*
|
||||
* @see ConfidentialMPTSend, ConfidentialMPTConvert
|
||||
*/
|
||||
class ConfidentialMPTMergeInbox : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit ConfidentialMPTMergeInbox(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/tx/transactors/token/ConfidentialMPTSend.h
Normal file
55
include/xrpl/tx/transactors/token/ConfidentialMPTSend.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Transfers confidential MPT tokens between holders privately.
|
||||
*
|
||||
* This transaction enables private token transfers where the transfer amount
|
||||
* is hidden from public view. Both sender and recipient must have initialized
|
||||
* confidential balances. The transaction provides encrypted amounts for all
|
||||
* parties (sender, destination, issuer, and optionally auditor) along with
|
||||
* zero-knowledge proofs that verify correctness without revealing the amount.
|
||||
*
|
||||
* @par Cryptographic Operations:
|
||||
* - **Multi-Ciphertext Equality Proof**: Verifies that all encrypted amounts
|
||||
* (sender, destination, issuer, auditor) encrypt the same plaintext value.
|
||||
* - **Amount Pedersen Linkage Proof**: Verifies that the amount commitment
|
||||
* correctly links to the sender's encrypted amount.
|
||||
* - **Balance Pedersen Linkage Proof**: Verifies that the balance commitment
|
||||
* correctly links to the sender's encrypted spending balance.
|
||||
* - **Bulletproof Range Proof**: Verifies remaining balance and
|
||||
* transfer amount are non-negative.
|
||||
*
|
||||
* @note Funds are deposited into the destination's inbox, not spending
|
||||
* balance. The recipient must call ConfidentialMPTMergeInbox to make
|
||||
* received funds spendable.
|
||||
*
|
||||
* @see ConfidentialMPTMergeInbox, ConfidentialMPTConvert,
|
||||
* ConfidentialMPTConvertBack
|
||||
*/
|
||||
class ConfidentialMPTSend : public Transactor
|
||||
{
|
||||
/// Size of two Pedersen linkage proofs (amount + balance)
|
||||
static constexpr std::size_t doublePedersenProofLength = 2 * ecPedersenProofLength;
|
||||
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit ConfidentialMPTSend(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -59,7 +59,8 @@ Logs::File::open(boost::filesystem::path const& path)
|
||||
bool wasOpened = false;
|
||||
|
||||
// VFALCO TODO Make this work with Unicode file paths
|
||||
std::unique_ptr<std::ofstream> stream(new std::ofstream(path.c_str(), std::fstream::app));
|
||||
std::unique_ptr<std::ofstream> stream =
|
||||
std::make_unique<std::ofstream>(path.c_str(), std::fstream::app);
|
||||
|
||||
if (stream->good())
|
||||
{
|
||||
|
||||
@@ -43,13 +43,14 @@ AcceptedLedgerTx::AcceptedLedgerTx(
|
||||
auto const amount = mTxn->getFieldAmount(sfTakerGets);
|
||||
|
||||
// If the offer create is not self funded then add the owner balance
|
||||
if (account != amount.issue().account)
|
||||
if (account != amount.getIssuer())
|
||||
{
|
||||
auto const ownerFunds = accountFunds(
|
||||
*ledger,
|
||||
account,
|
||||
amount,
|
||||
fhIGNORE_FREEZE,
|
||||
ahIGNORE_AUTH,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
mJson[jss::transaction][jss::owner_funds] = ownerFunds.getText();
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
auto
|
||||
DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency const& c) -> Key
|
||||
DeferredCredits::makeKeyIOU(AccountID const& a1, AccountID const& a2, Currency const& c) -> KeyIOU
|
||||
{
|
||||
if (a1 < a2)
|
||||
{
|
||||
@@ -19,21 +21,23 @@ DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency cons
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::credit(
|
||||
DeferredCredits::creditIOU(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditSenderBalance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::credit : sender is not receiver");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::credit : positive amount");
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::creditIOU : sender is not receiver");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditIOU : positive amount");
|
||||
XRPL_ASSERT(
|
||||
amount.holds<Issue>(), "xrpl::detail::DeferredCredits::creditIOU : amount is for Issue");
|
||||
|
||||
auto const k = makeKey(sender, receiver, amount.getCurrency());
|
||||
auto i = credits_.find(k);
|
||||
if (i == credits_.end())
|
||||
auto const k = makeKeyIOU(sender, receiver, amount.get<Issue>().currency);
|
||||
auto i = creditsIOU_.find(k);
|
||||
if (i == creditsIOU_.end())
|
||||
{
|
||||
Value v;
|
||||
ValueIOU v;
|
||||
|
||||
if (sender < receiver)
|
||||
{
|
||||
@@ -48,7 +52,7 @@ DeferredCredits::credit(
|
||||
v.lowAcctOrigBalance = -preCreditSenderBalance;
|
||||
}
|
||||
|
||||
credits_[k] = v;
|
||||
creditsIOU_[k] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,6 +69,93 @@ DeferredCredits::credit(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::creditMPT(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amount.holds<MPTIssue>(),
|
||||
"xrpl::detail::DeferredCredits::creditMPT : amount is for MPTIssue");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditMPT : positive amount");
|
||||
XRPL_ASSERT(
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::creditMPT : sender is not receiver");
|
||||
|
||||
auto const mptAmtVal = amount.mpt().value();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
bool const isSenderIssuer = sender == issuer;
|
||||
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
if (i == creditsMPT_.end())
|
||||
{
|
||||
IssuerValueMPT v;
|
||||
if (isSenderIssuer)
|
||||
{
|
||||
v.credit = mptAmtVal;
|
||||
v.holders[receiver].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.holders[sender].debit = mptAmtVal;
|
||||
v.holders[sender].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
v.origBalance = preCreditBalanceIssuer;
|
||||
creditsMPT_.emplace(mptID, std::move(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
// only record the balance the first time, do not record it here
|
||||
auto& v = i->second;
|
||||
if (isSenderIssuer)
|
||||
{
|
||||
v.credit += mptAmtVal;
|
||||
if (!v.holders.contains(receiver))
|
||||
{
|
||||
v.holders[receiver].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!v.holders.contains(sender))
|
||||
{
|
||||
v.holders[sender].debit = mptAmtVal;
|
||||
v.holders[sender].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.holders[sender].debit += mptAmtVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::issuerSelfDebitMPT(
|
||||
MPTIssue const& issue,
|
||||
std::uint64_t amount,
|
||||
std::int64_t origBalance)
|
||||
{
|
||||
auto const& mptID = issue.getMptID();
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
|
||||
if (i == creditsMPT_.end())
|
||||
{
|
||||
IssuerValueMPT v;
|
||||
v.origBalance = origBalance;
|
||||
v.selfDebit = amount;
|
||||
creditsMPT_.emplace(mptID, std::move(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
i->second.selfDebit += amount;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next)
|
||||
{
|
||||
@@ -88,16 +179,16 @@ DeferredCredits::ownerCount(AccountID const& id) const
|
||||
|
||||
// Get the adjustments for the balance between main and other.
|
||||
auto
|
||||
DeferredCredits::adjustments(
|
||||
DeferredCredits::adjustmentsIOU(
|
||||
AccountID const& main,
|
||||
AccountID const& other,
|
||||
Currency const& currency) const -> std::optional<Adjustment>
|
||||
Currency const& currency) const -> std::optional<AdjustmentIOU>
|
||||
{
|
||||
std::optional<Adjustment> result;
|
||||
std::optional<AdjustmentIOU> result;
|
||||
|
||||
Key const k = makeKey(main, other, currency);
|
||||
auto i = credits_.find(k);
|
||||
if (i == credits_.end())
|
||||
KeyIOU const k = makeKeyIOU(main, other, currency);
|
||||
auto i = creditsIOU_.find(k);
|
||||
if (i == creditsIOU_.end())
|
||||
return result;
|
||||
|
||||
auto const& v = i->second;
|
||||
@@ -112,12 +203,21 @@ DeferredCredits::adjustments(
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
DeferredCredits::adjustmentsMPT(xrpl::MPTID const& mptID) const -> std::optional<AdjustmentMPT>
|
||||
{
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
if (i == creditsMPT_.end())
|
||||
return std::nullopt;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::apply(DeferredCredits& to)
|
||||
{
|
||||
for (auto const& i : credits_)
|
||||
for (auto const& i : creditsIOU_)
|
||||
{
|
||||
auto r = to.credits_.emplace(i);
|
||||
auto r = to.creditsIOU_.emplace(i);
|
||||
if (!r.second)
|
||||
{
|
||||
auto& toVal = r.first->second;
|
||||
@@ -128,6 +228,30 @@ DeferredCredits::apply(DeferredCredits& to)
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& i : creditsMPT_)
|
||||
{
|
||||
auto r = to.creditsMPT_.emplace(i);
|
||||
if (!r.second)
|
||||
{
|
||||
auto& toVal = r.first->second;
|
||||
auto const& fromVal = i.second;
|
||||
toVal.credit += fromVal.credit;
|
||||
toVal.selfDebit += fromVal.selfDebit;
|
||||
for (auto& [k, v] : fromVal.holders)
|
||||
{
|
||||
if (toVal.holders.find(k) == toVal.holders.end())
|
||||
{
|
||||
toVal.holders[k] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
toVal.holders[k].debit += v.debit;
|
||||
}
|
||||
}
|
||||
// Do not update the orig balance, it's already correct
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& i : ownerCounts_)
|
||||
{
|
||||
auto r = to.ownerCounts_.emplace(i);
|
||||
@@ -143,11 +267,13 @@ DeferredCredits::apply(DeferredCredits& to)
|
||||
} // namespace detail
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHook(
|
||||
PaymentSandbox::balanceHookIOU(
|
||||
AccountID const& account,
|
||||
AccountID const& issuer,
|
||||
STAmount const& amount) const
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "balanceHookIOU: amount is for Issue");
|
||||
|
||||
/*
|
||||
There are two algorithms here. The pre-switchover algorithm takes the
|
||||
current amount and subtracts the recorded credits. The post-switchover
|
||||
@@ -159,14 +285,14 @@ PaymentSandbox::balanceHook(
|
||||
magnitudes, (B+C)-C may not equal B.
|
||||
*/
|
||||
|
||||
auto const currency = amount.getCurrency();
|
||||
auto const& currency = amount.get<Issue>().currency;
|
||||
|
||||
auto delta = amount.zeroed();
|
||||
auto lastBal = amount;
|
||||
auto minBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustments(account, issuer, currency))
|
||||
if (auto adj = curSB->tab_.adjustmentsIOU(account, issuer, currency))
|
||||
{
|
||||
delta += adj->debits;
|
||||
lastBal = adj->origBalance;
|
||||
@@ -180,13 +306,13 @@ PaymentSandbox::balanceHook(
|
||||
// to compute usable balance just slightly above what the ledger
|
||||
// calculates (but always less than the actual balance).
|
||||
auto adjustedAmt = std::min({amount, lastBal - delta, minBal});
|
||||
adjustedAmt.setIssuer(amount.getIssuer());
|
||||
adjustedAmt.get<Issue>().account = amount.getIssuer();
|
||||
|
||||
if (isXRP(issuer) && adjustedAmt < beast::zero)
|
||||
{
|
||||
// A calculated negative XRP balance is not an error case. Consider a
|
||||
// payment snippet that credits a large XRP amount and then debits the
|
||||
// same amount. The credit can't be used but we subtract the debit and
|
||||
// same amount. The credit can't be used, but we subtract the debit and
|
||||
// calculate a negative value. It's not an error case.
|
||||
adjustedAmt.clear();
|
||||
}
|
||||
@@ -194,6 +320,64 @@ PaymentSandbox::balanceHook(
|
||||
return adjustedAmt;
|
||||
}
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHookMPT(AccountID const& account, MPTIssue const& issue, std::int64_t amount)
|
||||
const
|
||||
{
|
||||
auto const& issuer = issue.getIssuer();
|
||||
bool const accountIsHolder = account != issuer;
|
||||
|
||||
std::int64_t delta = 0;
|
||||
std::int64_t lastBal = amount;
|
||||
std::int64_t minBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustmentsMPT(issue))
|
||||
{
|
||||
if (accountIsHolder)
|
||||
{
|
||||
if (auto const i = adj->holders.find(account); i != adj->holders.end())
|
||||
{
|
||||
delta += i->second.debit;
|
||||
lastBal = i->second.origBalance;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delta += adj->credit;
|
||||
lastBal = adj->origBalance;
|
||||
}
|
||||
minBal = std::min(lastBal, minBal);
|
||||
}
|
||||
}
|
||||
|
||||
// The adjusted amount should never be larger than the balance.
|
||||
|
||||
auto const adjustedAmt = std::min({amount, lastBal - delta, minBal});
|
||||
|
||||
return adjustedAmt > 0 ? STAmount{issue, adjustedAmt} : STAmount{issue};
|
||||
}
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHookSelfIssueMPT(xrpl::MPTIssue const& issue, std::int64_t amount) const
|
||||
{
|
||||
std::int64_t selfDebited = 0;
|
||||
std::int64_t lastBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustmentsMPT(issue))
|
||||
{
|
||||
selfDebited += adj->selfDebit;
|
||||
lastBal = adj->origBalance;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastBal > selfDebited)
|
||||
return STAmount{issue, lastBal - selfDebited};
|
||||
|
||||
return STAmount{issue};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) const
|
||||
{
|
||||
@@ -207,13 +391,39 @@ PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) co
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::creditHook(
|
||||
PaymentSandbox::creditHookIOU(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditBalance)
|
||||
{
|
||||
tab_.credit(from, to, amount, preCreditBalance);
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "creditHookIOU: amount is for Issue");
|
||||
|
||||
tab_.creditIOU(from, to, amount, preCreditBalance);
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::creditHookMPT(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer)
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<MPTIssue>(), "creditHookMPT: amount is for MPTIssue");
|
||||
|
||||
tab_.creditMPT(from, to, amount, preCreditBalanceHolder, preCreditBalanceIssuer);
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::issuerSelfDebitHookMPT(
|
||||
MPTIssue const& issue,
|
||||
std::uint64_t amount,
|
||||
std::int64_t origBalance)
|
||||
{
|
||||
XRPL_ASSERT(amount > 0, "PaymentSandbox::issuerSelfDebitHookMPT: amount must be > 0");
|
||||
|
||||
tab_.issuerSelfDebitMPT(issue, amount, origBalance);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -346,7 +556,7 @@ PaymentSandbox::balanceChanges(ReadView const& view) const
|
||||
}
|
||||
// The following are now set, put them in the map
|
||||
auto delta = newBalance - oldBalance;
|
||||
auto const cur = newBalance.getCurrency();
|
||||
auto const cur = newBalance.get<Issue>().currency;
|
||||
result[std::make_tuple(lowID, highID, cur)] = delta;
|
||||
auto r = result.emplace(std::make_tuple(lowID, lowID, cur), delta);
|
||||
if (r.second)
|
||||
|
||||
@@ -84,11 +84,10 @@ bool
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& asset,
|
||||
Issue const& asset2)
|
||||
Asset const& asset,
|
||||
Asset const& asset2)
|
||||
{
|
||||
return isFrozen(view, account, asset.currency, asset.account) ||
|
||||
isFrozen(view, account, asset2.currency, asset2.account);
|
||||
return isFrozen(view, account, asset) || isFrozen(view, account, asset2);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -334,22 +333,19 @@ withdrawToDestExceedsLimit(
|
||||
if (from == to || to == issuer || isXRP(issuer))
|
||||
return tesSUCCESS;
|
||||
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return amount.asset().visit(
|
||||
[&](Issue const& issue) -> TER {
|
||||
auto const& currency = issue.currency;
|
||||
auto const owed = creditBalance(view, to, issuer, currency);
|
||||
if (owed <= beast::zero)
|
||||
{
|
||||
auto const& currency = issue.currency;
|
||||
auto const owed = creditBalance(view, to, issuer, currency);
|
||||
if (owed <= beast::zero)
|
||||
{
|
||||
auto const limit = creditLimit(view, to, issuer, currency);
|
||||
if (-owed >= limit || amount > (limit + owed))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
auto const limit = creditLimit(view, to, issuer, currency);
|
||||
if (-owed >= limit || amount > (limit + owed))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
},
|
||||
amount.asset().value());
|
||||
[](MPTIssue const&) -> TER { return tesSUCCESS; });
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
//
|
||||
#include <xrpl/ledger/View.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
STAmount
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Issue const& lptIssue)
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Asset const& lptIssue)
|
||||
{
|
||||
// AMM invariant: sqrt(asset1 * asset2) >= LPTokensBalance
|
||||
auto const rounding = isFeatureEnabled(fixAMMv1_3) ? Number::downward : Number::getround();
|
||||
@@ -32,7 +34,7 @@ lpTokensOut(
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
{
|
||||
auto const t = lptAMMBalance * (r - c) / (1 + c);
|
||||
return toSTAmount(lptAMMBalance.issue(), t);
|
||||
return toSTAmount(lptAMMBalance.asset(), t);
|
||||
}
|
||||
|
||||
// minimize tokens out
|
||||
@@ -68,7 +70,7 @@ ammAssetIn(
|
||||
auto const c = d * d - f2 * f2;
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
{
|
||||
return toSTAmount(asset1Balance.issue(), asset1Balance * solveQuadraticEq(a, b, c));
|
||||
return toSTAmount(asset1Balance.asset(), asset1Balance * solveQuadraticEq(a, b, c));
|
||||
}
|
||||
|
||||
// maximize deposit
|
||||
@@ -93,7 +95,7 @@ lpTokensIn(
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
{
|
||||
auto const t = lptAMMBalance * (c - root2(c * c - 4 * fr)) / 2;
|
||||
return toSTAmount(lptAMMBalance.issue(), t);
|
||||
return toSTAmount(lptAMMBalance.asset(), t);
|
||||
}
|
||||
|
||||
// maximize tokens in
|
||||
@@ -123,7 +125,7 @@ ammAssetOut(
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
{
|
||||
auto const b = assetBalance * (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1);
|
||||
return toSTAmount(assetBalance.issue(), b);
|
||||
return toSTAmount(assetBalance.asset(), b);
|
||||
}
|
||||
|
||||
// minimize withdraw
|
||||
@@ -183,8 +185,8 @@ adjustAmountsByLPTokens(
|
||||
if (amount2)
|
||||
{
|
||||
Number const fr = lpTokensActual / lpTokens;
|
||||
auto const amountActual = toSTAmount(amount.issue(), fr * amount);
|
||||
auto const amount2Actual = toSTAmount(amount2->issue(), fr * *amount2);
|
||||
auto const amountActual = toSTAmount(amount.asset(), fr * amount);
|
||||
auto const amount2Actual = toSTAmount(amount2->asset(), fr * *amount2);
|
||||
if (!ammRoundingEnabled)
|
||||
{
|
||||
return std::make_tuple(
|
||||
@@ -253,7 +255,7 @@ multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm)
|
||||
{
|
||||
NumberRoundModeGuard const g(rm);
|
||||
auto const t = amount * frac;
|
||||
return toSTAmount(amount.issue(), t, rm);
|
||||
return toSTAmount(amount.asset(), t, rm);
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -265,13 +267,13 @@ getRoundedAsset(
|
||||
IsDeposit isDeposit)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
return toSTAmount(balance.issue(), noRoundCb());
|
||||
return toSTAmount(balance.asset(), noRoundCb());
|
||||
|
||||
auto const rm = detail::getAssetRounding(isDeposit);
|
||||
if (isDeposit == IsDeposit::Yes)
|
||||
return multiply(balance, productCb(), rm);
|
||||
NumberRoundModeGuard const g(rm);
|
||||
return toSTAmount(balance.issue(), productCb(), rm);
|
||||
return toSTAmount(balance.asset(), productCb(), rm);
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -282,7 +284,7 @@ getRoundedLPTokens(
|
||||
IsDeposit isDeposit)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
return toSTAmount(balance.issue(), balance * frac);
|
||||
return toSTAmount(balance.asset(), balance * frac);
|
||||
|
||||
auto const rm = detail::getLPTokenRounding(isDeposit);
|
||||
auto const tokens = multiply(balance, frac, rm);
|
||||
@@ -298,14 +300,14 @@ getRoundedLPTokens(
|
||||
IsDeposit isDeposit)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
return toSTAmount(lptAMMBalance.issue(), noRoundCb());
|
||||
return toSTAmount(lptAMMBalance.asset(), noRoundCb());
|
||||
|
||||
auto const tokens = [&] {
|
||||
auto const rm = detail::getLPTokenRounding(isDeposit);
|
||||
if (isDeposit == IsDeposit::Yes)
|
||||
{
|
||||
NumberRoundModeGuard const g(rm);
|
||||
return toSTAmount(lptAMMBalance.issue(), productCb(), rm);
|
||||
return toSTAmount(lptAMMBalance.asset(), productCb(), rm);
|
||||
}
|
||||
return multiply(lptAMMBalance, productCb(), rm);
|
||||
}();
|
||||
@@ -376,4 +378,535 @@ adjustFracByTokens(
|
||||
return tokens / lptAMMBalance;
|
||||
}
|
||||
|
||||
std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal const j)
|
||||
{
|
||||
auto const assetInBalance =
|
||||
accountHolds(view, ammAccountID, asset1, freezeHandling, authHandling, j);
|
||||
auto const assetOutBalance =
|
||||
accountHolds(view, ammAccountID, asset2, freezeHandling, authHandling, j);
|
||||
return std::make_pair(assetInBalance, assetOutBalance);
|
||||
}
|
||||
|
||||
Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
|
||||
ammHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
std::optional<Asset> const& optAsset1,
|
||||
std::optional<Asset> const& optAsset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal const j)
|
||||
{
|
||||
auto const assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
|
||||
auto const asset1 = ammSle[sfAsset];
|
||||
auto const asset2 = ammSle[sfAsset2];
|
||||
if (optAsset1 && optAsset2)
|
||||
{
|
||||
if (invalidAMMAssetPair(
|
||||
*optAsset1, *optAsset2, std::make_optional(std::make_pair(asset1, asset2))))
|
||||
{
|
||||
// This error can only be hit if the AMM is corrupted
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.debug()) << "ammHolds: Invalid optAsset1 or optAsset2 " << *optAsset1 << " "
|
||||
<< *optAsset2;
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return std::make_optional(std::make_pair(*optAsset1, *optAsset2));
|
||||
}
|
||||
auto const singleAsset = [&asset1, &asset2, &j](
|
||||
Asset checkIssue,
|
||||
char const* label) -> std::optional<std::pair<Asset, Asset>> {
|
||||
if (checkIssue == asset1)
|
||||
{
|
||||
return std::make_optional(std::make_pair(asset1, asset2));
|
||||
}
|
||||
if (checkIssue == asset2)
|
||||
{
|
||||
return std::make_optional(std::make_pair(asset2, asset1));
|
||||
}
|
||||
// Unreachable unless AMM corrupted.
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue;
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
if (optAsset1)
|
||||
{
|
||||
return singleAsset(*optAsset1, "optAsset1");
|
||||
}
|
||||
if (optAsset2)
|
||||
{
|
||||
// Cannot have Amount2 without Amount.
|
||||
return singleAsset(*optAsset2, "optAsset2"); // LCOV_EXCL_LINE
|
||||
}
|
||||
return std::make_optional(std::make_pair(asset1, asset2));
|
||||
}();
|
||||
if (!assets)
|
||||
return Unexpected(tecAMM_INVALID_TOKENS);
|
||||
auto const [amount1, amount2] = ammPoolHolds(
|
||||
view,
|
||||
ammSle.getAccountID(sfAccount),
|
||||
assets->first,
|
||||
assets->second,
|
||||
freezeHandling,
|
||||
authHandling,
|
||||
j);
|
||||
return std::make_tuple(amount1, amount2, ammSle[sfLPTokenBalance]);
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
{
|
||||
// This function looks similar to `accountHolds`. However, it only checks if
|
||||
// a LPToken holder has enough balance. On the other hand, `accountHolds`
|
||||
// checks if the underlying assets of LPToken are frozen with the
|
||||
// fixFrozenLPTokenTransfer amendment
|
||||
|
||||
auto const currency = ammLPTCurrency(asset1, asset2);
|
||||
STAmount amount;
|
||||
|
||||
auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
|
||||
if (!sle)
|
||||
{
|
||||
amount.clear(Issue{currency, ammAccount});
|
||||
JLOG(j.trace()) << "ammLPHolds: no SLE "
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
else if (isFrozen(view, lpAccount, currency, ammAccount))
|
||||
{
|
||||
amount.clear(Issue{currency, ammAccount});
|
||||
JLOG(j.trace()) << "ammLPHolds: frozen currency "
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = sle->getFieldAmount(sfBalance);
|
||||
if (lpAccount > ammAccount)
|
||||
{
|
||||
// Put balance in account terms.
|
||||
amount.negate();
|
||||
}
|
||||
amount.get<Issue>().account = ammAccount;
|
||||
|
||||
JLOG(j.trace()) << "ammLPHolds:"
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
|
||||
return view.balanceHookIOU(lpAccount, ammAccount, amount);
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
{
|
||||
return ammLPHolds(view, ammSle[sfAsset], ammSle[sfAsset2], ammSle[sfAccount], lpAccount, j);
|
||||
}
|
||||
|
||||
std::uint16_t
|
||||
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
XRPL_ASSERT(
|
||||
!view.rules().enabled(fixInnerObjTemplate) || ammSle.isFieldPresent(sfAuctionSlot),
|
||||
"xrpl::getTradingFee : auction present");
|
||||
if (ammSle.isFieldPresent(sfAuctionSlot))
|
||||
{
|
||||
auto const& auctionSlot = safe_downcast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
|
||||
// Not expired
|
||||
if (auto const expiration = auctionSlot[~sfExpiration];
|
||||
duration_cast<seconds>(view.header().parentCloseTime.time_since_epoch()).count() <
|
||||
expiration)
|
||||
{
|
||||
if (auctionSlot[~sfAccount] == account)
|
||||
return auctionSlot[sfDiscountedFee];
|
||||
if (auctionSlot.isFieldPresent(sfAuthAccounts))
|
||||
{
|
||||
for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts))
|
||||
{
|
||||
if (acct[~sfAccount] == account)
|
||||
return auctionSlot[sfDiscountedFee];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ammSle[sfTradingFee];
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const& asset)
|
||||
{
|
||||
// Get the actual AMM balance without factoring in the balance hook
|
||||
return asset.visit(
|
||||
[&](MPTIssue const& issue) {
|
||||
if (auto const sle = view.read(keylet::mptoken(issue, ammAccountID));
|
||||
sle && !isFrozen(view, ammAccountID, issue))
|
||||
return STAmount{issue, (*sle)[sfMPTAmount]};
|
||||
return STAmount{asset};
|
||||
},
|
||||
[&](Issue const& issue) {
|
||||
if (isXRP(issue))
|
||||
{
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
return (*sle)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
auto const sle =
|
||||
view.read(keylet::line(ammAccountID, issue.account, issue.currency));
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
{
|
||||
STAmount amount = (*sle)[sfBalance];
|
||||
if (ammAccountID > issue.account)
|
||||
amount.negate();
|
||||
amount.get<Issue>().account = issue.account;
|
||||
return amount;
|
||||
}
|
||||
return STAmount{asset};
|
||||
});
|
||||
}
|
||||
|
||||
static TER
|
||||
deleteAMMTrustLines(
|
||||
Sandbox& sb,
|
||||
AccountID const& ammAccountID,
|
||||
std::uint16_t maxTrustlinesToDelete,
|
||||
beast::Journal j)
|
||||
{
|
||||
return cleanupOnAccountDelete(
|
||||
sb,
|
||||
keylet::ownerDir(ammAccountID),
|
||||
[&](LedgerEntryType nodeType,
|
||||
uint256 const&,
|
||||
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
|
||||
// Skip AMM and MPToken
|
||||
if (nodeType == ltAMM || nodeType == ltMPTOKEN)
|
||||
return {tesSUCCESS, SkipEntry::Yes};
|
||||
|
||||
if (nodeType == ltRIPPLE_STATE)
|
||||
{
|
||||
// Trustlines must have zero balance
|
||||
if (sleItem->getFieldAmount(sfBalance) != beast::zero)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMObjects: deleting trustline with "
|
||||
"non-zero balance.";
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return {deleteAMMTrustLine(sb, sleItem, ammAccountID, j), SkipEntry::No};
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMObjects: deleting non-trustline or non-MPT " << nodeType;
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
},
|
||||
j,
|
||||
maxTrustlinesToDelete);
|
||||
}
|
||||
|
||||
static TER
|
||||
deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j)
|
||||
{
|
||||
return cleanupOnAccountDelete(
|
||||
sb,
|
||||
keylet::ownerDir(ammAccountID),
|
||||
[&](LedgerEntryType nodeType,
|
||||
uint256 const&,
|
||||
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
|
||||
// Skip AMM
|
||||
if (nodeType == ltAMM)
|
||||
return {tesSUCCESS, SkipEntry::Yes};
|
||||
|
||||
if (nodeType == ltMPTOKEN)
|
||||
{
|
||||
// MPT must have zero balance
|
||||
if (sleItem->getFieldU64(sfMPTAmount) != 0 ||
|
||||
(*sleItem)[~sfLockedAmount].value_or(0) != 0)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMObjects: deleting MPT with "
|
||||
"non-zero balance.";
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return {deleteAMMMPToken(sb, sleItem, ammAccountID, j), SkipEntry::No};
|
||||
}
|
||||
if (nodeType == ltRIPPLE_STATE)
|
||||
{
|
||||
// Trustlines should have been deleted
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMObjects: trustlines should have been deleted";
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMObjects: deleting non-trustline or non-MPT " << nodeType;
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
},
|
||||
j,
|
||||
3); // At most two MPToken plus AMM object
|
||||
}
|
||||
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Journal j)
|
||||
{
|
||||
auto ammSle = sb.peek(keylet::amm(asset, asset2));
|
||||
if (!ammSle)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist " << asset << " " << asset2;
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
|
||||
if (!sleAMMRoot)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
|
||||
<< to_string(ammAccountID);
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// Delete AMM's MPTokens only if all trustlines are deleted. If trustlines
|
||||
// are not deleted then AMM can be re-created with Deposit and
|
||||
// AMM's MPToken(s) must exist.
|
||||
if (auto const ter = deleteAMMMPTokens(sb, ammAccountID, j); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
|
||||
if (!sb.dirRemove(ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
|
||||
<< toBase58(ammAccountID);
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
sb.erase(ammSle);
|
||||
sb.erase(sleAMMRoot);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Asset const& lptAsset,
|
||||
std::uint16_t tfee)
|
||||
{
|
||||
auto const& rules = view.rules();
|
||||
// AMM creator gets the voting slot.
|
||||
STArray voteSlots;
|
||||
STObject voteEntry = STObject::makeInnerObject(sfVoteEntry);
|
||||
if (tfee != 0)
|
||||
voteEntry.setFieldU16(sfTradingFee, tfee);
|
||||
voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
|
||||
voteEntry.setAccountID(sfAccount, account);
|
||||
voteSlots.push_back(voteEntry);
|
||||
ammSle->setFieldArray(sfVoteSlots, voteSlots);
|
||||
// AMM creator gets the auction slot for free.
|
||||
// AuctionSlot is created on AMMCreate and updated on AMMDeposit
|
||||
// when AMM is in an empty state
|
||||
if (rules.enabled(fixInnerObjTemplate) && !ammSle->isFieldPresent(sfAuctionSlot))
|
||||
{
|
||||
STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot);
|
||||
ammSle->set(std::move(auctionSlot));
|
||||
}
|
||||
STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
|
||||
auctionSlot.setAccountID(sfAccount, account);
|
||||
// current + sec in 24h
|
||||
auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
view.header().parentCloseTime.time_since_epoch())
|
||||
.count() +
|
||||
TOTAL_TIME_SLOT_SECS;
|
||||
auctionSlot.setFieldU32(sfExpiration, expiration);
|
||||
auctionSlot.setFieldAmount(sfPrice, STAmount{lptAsset, 0});
|
||||
// Set the fee
|
||||
if (tfee != 0)
|
||||
{
|
||||
ammSle->setFieldU16(sfTradingFee, tfee);
|
||||
}
|
||||
else if (ammSle->isFieldPresent(sfTradingFee))
|
||||
{
|
||||
ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
|
||||
{
|
||||
auctionSlot.setFieldU16(sfDiscountedFee, dfee);
|
||||
}
|
||||
else if (auctionSlot.isFieldPresent(sfDiscountedFee))
|
||||
{
|
||||
auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
Expected<bool, TER>
|
||||
isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount)
|
||||
{
|
||||
// Liquidity Provider (LP) must have one LPToken trustline
|
||||
std::uint8_t nLPTokenTrustLines = 0;
|
||||
// AMM account has at most two IOU (pool tokens, not LPToken) trustlines.
|
||||
// One or both trustlines could be to the LP if LP is the issuer,
|
||||
// or a different account if LP is not an issuer. For instance,
|
||||
// if AMM has two tokens USD and EUR and LP is not the issuer of the tokens
|
||||
// then the trustlines are between AMM account and the issuer.
|
||||
// There is one LPToken trustline for each LP. Only remaining LP has
|
||||
// exactly one LPToken trustlines and at most two IOU trustline for each
|
||||
// pool token. One or both tokens could be MPT.
|
||||
std::uint8_t nIOUTrustLines = 0;
|
||||
// There are at most two MPT objects, one for each side of the pool.
|
||||
std::uint8_t nMPT = 0;
|
||||
// There is only one AMM object
|
||||
bool hasAMM = false;
|
||||
// AMM LP has at most three trustlines, at most two MPTs, and only one
|
||||
// AMM object must exist. If there are more than four objects then
|
||||
// it's either an error or there are more than one LP. Ten pages should
|
||||
// be sufficient to include four objects.
|
||||
std::uint8_t limit = 10;
|
||||
auto const root = keylet::ownerDir(ammIssue.account);
|
||||
auto currentIndex = root;
|
||||
|
||||
// Iterate over AMM owner directory objects.
|
||||
while (limit-- >= 1)
|
||||
{
|
||||
auto const ownerDir = view.read(currentIndex);
|
||||
if (!ownerDir)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
for (auto const& key : ownerDir->getFieldV256(sfIndexes))
|
||||
{
|
||||
auto const sle = view.read(keylet::child(key));
|
||||
if (!sle)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const entryType = sle->getFieldU16(sfLedgerEntryType);
|
||||
// Only one AMM object
|
||||
if (entryType == ltAMM)
|
||||
{
|
||||
if (hasAMM)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
hasAMM = true;
|
||||
continue;
|
||||
}
|
||||
if (entryType == ltMPTOKEN)
|
||||
{
|
||||
++nMPT;
|
||||
continue;
|
||||
}
|
||||
if (entryType != ltRIPPLE_STATE)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const lowLimit = sle->getFieldAmount(sfLowLimit);
|
||||
auto const highLimit = sle->getFieldAmount(sfHighLimit);
|
||||
auto const isLPTrustline =
|
||||
lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount;
|
||||
auto const isLPTokenTrustline =
|
||||
lowLimit.asset() == ammIssue || highLimit.asset() == ammIssue;
|
||||
|
||||
// Liquidity Provider trustline
|
||||
if (isLPTrustline)
|
||||
{
|
||||
// LPToken trustline
|
||||
if (isLPTokenTrustline)
|
||||
{
|
||||
// LP has exactly one LPToken trustline
|
||||
if (++nLPTokenTrustLines > 1)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
// AMM account has at most two IOU trustlines
|
||||
else if (++nIOUTrustLines > 2)
|
||||
{
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
// Another Liquidity Provider LPToken trustline
|
||||
else if (isLPTokenTrustline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// AMM account has at most two IOU trustlines
|
||||
else if (++nIOUTrustLines > 2)
|
||||
{
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
|
||||
if (uNodeNext == 0)
|
||||
{
|
||||
if (nLPTokenTrustLines != 1 || (nIOUTrustLines == 0 && nMPT == 0) ||
|
||||
(nIOUTrustLines > 2 || nMPT > 2) || (nIOUTrustLines + nMPT) > 2)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
return true;
|
||||
}
|
||||
currentIndex = keylet::page(root, uNodeNext);
|
||||
}
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
Expected<bool, TER>
|
||||
verifyAndAdjustLPTokenBalance(
|
||||
Sandbox& sb,
|
||||
STAmount const& lpTokens,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account)
|
||||
{
|
||||
auto const res = isOnlyLiquidityProvider(sb, lpTokens.get<Issue>(), account);
|
||||
if (!res.has_value())
|
||||
{
|
||||
return Unexpected<TER>(res.error());
|
||||
}
|
||||
|
||||
if (res.value())
|
||||
{
|
||||
if (withinRelativeDistance(
|
||||
lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3}))
|
||||
{
|
||||
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
|
||||
sb.update(ammSle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unexpected<TER>(tecAMM_INVALID_TOKENS);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,462 +0,0 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AMMUtils.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal const j)
|
||||
{
|
||||
auto const assetInBalance = accountHolds(view, ammAccountID, issue1, freezeHandling, j);
|
||||
auto const assetOutBalance = accountHolds(view, ammAccountID, issue2, freezeHandling, j);
|
||||
return std::make_pair(assetInBalance, assetOutBalance);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto const issues = [&]() -> std::optional<std::pair<Issue, Issue>> {
|
||||
auto const issue1 = ammSle[sfAsset].get<Issue>();
|
||||
auto const issue2 = ammSle[sfAsset2].get<Issue>();
|
||||
if (optIssue1 && optIssue2)
|
||||
{
|
||||
if (invalidAMMAssetPair(
|
||||
*optIssue1, *optIssue2, std::make_optional(std::make_pair(issue1, issue2))))
|
||||
{
|
||||
// This error can only be hit if the AMM is corrupted
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.debug()) << "ammHolds: Invalid optIssue1 or optIssue2 " << *optIssue1 << " "
|
||||
<< *optIssue2;
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return std::make_optional(std::make_pair(*optIssue1, *optIssue2));
|
||||
}
|
||||
auto const singleIssue = [&issue1, &issue2, &j](
|
||||
Issue checkIssue,
|
||||
char const* label) -> std::optional<std::pair<Issue, Issue>> {
|
||||
if (checkIssue == issue1)
|
||||
{
|
||||
return std::make_optional(std::make_pair(issue1, issue2));
|
||||
}
|
||||
if (checkIssue == issue2)
|
||||
{
|
||||
return std::make_optional(std::make_pair(issue2, issue1));
|
||||
}
|
||||
// Unreachable unless AMM corrupted.
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue;
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
if (optIssue1)
|
||||
{
|
||||
return singleIssue(*optIssue1, "optIssue1");
|
||||
}
|
||||
if (optIssue2)
|
||||
{
|
||||
// Cannot have Amount2 without Amount.
|
||||
return singleIssue(*optIssue2, "optIssue2"); // LCOV_EXCL_LINE
|
||||
}
|
||||
return std::make_optional(std::make_pair(issue1, issue2));
|
||||
}();
|
||||
if (!issues)
|
||||
return Unexpected(tecAMM_INVALID_TOKENS);
|
||||
auto const [asset1, asset2] = ammPoolHolds(
|
||||
view, ammSle.getAccountID(sfAccount), issues->first, issues->second, freezeHandling, j);
|
||||
return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]);
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Currency const& cur1,
|
||||
Currency const& cur2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
{
|
||||
// This function looks similar to `accountHolds`. However, it only checks if
|
||||
// a LPToken holder has enough balance. On the other hand, `accountHolds`
|
||||
// checks if the underlying assets of LPToken are frozen with the
|
||||
// fixFrozenLPTokenTransfer amendment
|
||||
|
||||
auto const currency = ammLPTCurrency(cur1, cur2);
|
||||
STAmount amount;
|
||||
|
||||
auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
|
||||
if (!sle)
|
||||
{
|
||||
amount.clear(Issue{currency, ammAccount});
|
||||
JLOG(j.trace()) << "ammLPHolds: no SLE "
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
else if (isFrozen(view, lpAccount, currency, ammAccount))
|
||||
{
|
||||
amount.clear(Issue{currency, ammAccount});
|
||||
JLOG(j.trace()) << "ammLPHolds: frozen currency "
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = sle->getFieldAmount(sfBalance);
|
||||
if (lpAccount > ammAccount)
|
||||
{
|
||||
// Put balance in account terms.
|
||||
amount.negate();
|
||||
}
|
||||
amount.setIssuer(ammAccount);
|
||||
|
||||
JLOG(j.trace()) << "ammLPHolds:"
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
|
||||
return view.balanceHook(lpAccount, ammAccount, amount);
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
{
|
||||
return ammLPHolds(
|
||||
view,
|
||||
ammSle[sfAsset].get<Issue>().currency,
|
||||
ammSle[sfAsset2].get<Issue>().currency,
|
||||
ammSle[sfAccount],
|
||||
lpAccount,
|
||||
j);
|
||||
}
|
||||
|
||||
std::uint16_t
|
||||
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
XRPL_ASSERT(
|
||||
!view.rules().enabled(fixInnerObjTemplate) || ammSle.isFieldPresent(sfAuctionSlot),
|
||||
"xrpl::getTradingFee : auction present");
|
||||
if (ammSle.isFieldPresent(sfAuctionSlot))
|
||||
{
|
||||
auto const& auctionSlot = safe_downcast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
|
||||
// Not expired
|
||||
if (auto const expiration = auctionSlot[~sfExpiration];
|
||||
duration_cast<seconds>(view.header().parentCloseTime.time_since_epoch()).count() <
|
||||
expiration)
|
||||
{
|
||||
if (auctionSlot[~sfAccount] == account)
|
||||
return auctionSlot[sfDiscountedFee];
|
||||
if (auctionSlot.isFieldPresent(sfAuthAccounts))
|
||||
{
|
||||
for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts))
|
||||
{
|
||||
if (acct[~sfAccount] == account)
|
||||
return auctionSlot[sfDiscountedFee];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ammSle[sfTradingFee];
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const& issue)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
{
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
return (*sle)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
{
|
||||
auto amount = (*sle)[sfBalance];
|
||||
if (ammAccountID > issue.account)
|
||||
amount.negate();
|
||||
amount.setIssuer(issue.account);
|
||||
return amount;
|
||||
}
|
||||
|
||||
return STAmount{issue};
|
||||
}
|
||||
|
||||
static TER
|
||||
deleteAMMTrustLines(
|
||||
Sandbox& sb,
|
||||
AccountID const& ammAccountID,
|
||||
std::uint16_t maxTrustlinesToDelete,
|
||||
beast::Journal j)
|
||||
{
|
||||
return cleanupOnAccountDelete(
|
||||
sb,
|
||||
keylet::ownerDir(ammAccountID),
|
||||
[&](LedgerEntryType nodeType,
|
||||
uint256 const&,
|
||||
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
|
||||
// Skip AMM
|
||||
if (nodeType == LedgerEntryType::ltAMM)
|
||||
return {tesSUCCESS, SkipEntry::Yes};
|
||||
// Should only have the trustlines
|
||||
if (nodeType != LedgerEntryType::ltRIPPLE_STATE)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMTrustLines: deleting non-trustline " << nodeType;
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Trustlines must have zero balance
|
||||
if (sleItem->getFieldAmount(sfBalance) != beast::zero)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMTrustLines: deleting trustline with "
|
||||
"non-zero balance.";
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return {deleteAMMTrustLine(sb, sleItem, ammAccountID, j), SkipEntry::No};
|
||||
},
|
||||
j,
|
||||
maxTrustlinesToDelete);
|
||||
}
|
||||
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Journal j)
|
||||
{
|
||||
auto ammSle = sb.peek(keylet::amm(asset, asset2));
|
||||
if (!ammSle)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist " << asset << " " << asset2;
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
|
||||
if (!sleAMMRoot)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
|
||||
<< to_string(ammAccountID);
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, maxDeletableAMMTrustLines, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
|
||||
if (!sb.dirRemove(ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
|
||||
<< toBase58(ammAccountID);
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
sb.erase(ammSle);
|
||||
sb.erase(sleAMMRoot);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Issue const& lptIssue,
|
||||
std::uint16_t tfee)
|
||||
{
|
||||
auto const& rules = view.rules();
|
||||
// AMM creator gets the voting slot.
|
||||
STArray voteSlots;
|
||||
STObject voteEntry = STObject::makeInnerObject(sfVoteEntry);
|
||||
if (tfee != 0)
|
||||
voteEntry.setFieldU16(sfTradingFee, tfee);
|
||||
voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
|
||||
voteEntry.setAccountID(sfAccount, account);
|
||||
voteSlots.push_back(voteEntry);
|
||||
ammSle->setFieldArray(sfVoteSlots, voteSlots);
|
||||
// AMM creator gets the auction slot for free.
|
||||
// AuctionSlot is created on AMMCreate and updated on AMMDeposit
|
||||
// when AMM is in an empty state
|
||||
if (rules.enabled(fixInnerObjTemplate) && !ammSle->isFieldPresent(sfAuctionSlot))
|
||||
{
|
||||
STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot);
|
||||
ammSle->set(std::move(auctionSlot));
|
||||
}
|
||||
STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
|
||||
auctionSlot.setAccountID(sfAccount, account);
|
||||
// current + sec in 24h
|
||||
auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
view.header().parentCloseTime.time_since_epoch())
|
||||
.count() +
|
||||
TOTAL_TIME_SLOT_SECS;
|
||||
auctionSlot.setFieldU32(sfExpiration, expiration);
|
||||
auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
|
||||
// Set the fee
|
||||
if (tfee != 0)
|
||||
{
|
||||
ammSle->setFieldU16(sfTradingFee, tfee);
|
||||
}
|
||||
else if (ammSle->isFieldPresent(sfTradingFee))
|
||||
{
|
||||
ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
|
||||
{
|
||||
auctionSlot.setFieldU16(sfDiscountedFee, dfee);
|
||||
}
|
||||
else if (auctionSlot.isFieldPresent(sfDiscountedFee))
|
||||
{
|
||||
auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
Expected<bool, TER>
|
||||
isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount)
|
||||
{
|
||||
// Liquidity Provider (LP) must have one LPToken trustline
|
||||
std::uint8_t nLPTokenTrustLines = 0;
|
||||
// There are at most two IOU trustlines. One or both could be to the LP
|
||||
// if LP is the issuer, or a different account if LP is not an issuer.
|
||||
// For instance, if AMM has two tokens USD and EUR and LP is not the issuer
|
||||
// of the tokens then the trustlines are between AMM account and the issuer.
|
||||
std::uint8_t nIOUTrustLines = 0;
|
||||
// There is only one AMM object
|
||||
bool hasAMM = false;
|
||||
// AMM LP has at most three trustlines and only one AMM object must exist.
|
||||
// If there are more than five objects then it's either an error or
|
||||
// there are more than one LP. Ten pages should be sufficient to include
|
||||
// five objects.
|
||||
std::uint8_t limit = 10;
|
||||
auto const root = keylet::ownerDir(ammIssue.account);
|
||||
auto currentIndex = root;
|
||||
|
||||
// Iterate over AMM owner directory objects.
|
||||
while (limit-- >= 1)
|
||||
{
|
||||
auto const ownerDir = view.read(currentIndex);
|
||||
if (!ownerDir)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
for (auto const& key : ownerDir->getFieldV256(sfIndexes))
|
||||
{
|
||||
auto const sle = view.read(keylet::child(key));
|
||||
if (!sle)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
// Only one AMM object
|
||||
if (sle->getFieldU16(sfLedgerEntryType) == ltAMM)
|
||||
{
|
||||
if (hasAMM)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
hasAMM = true;
|
||||
continue;
|
||||
}
|
||||
if (sle->getFieldU16(sfLedgerEntryType) != ltRIPPLE_STATE)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const lowLimit = sle->getFieldAmount(sfLowLimit);
|
||||
auto const highLimit = sle->getFieldAmount(sfHighLimit);
|
||||
auto const isLPTrustline =
|
||||
lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount;
|
||||
auto const isLPTokenTrustline =
|
||||
lowLimit.issue() == ammIssue || highLimit.issue() == ammIssue;
|
||||
|
||||
// Liquidity Provider trustline
|
||||
if (isLPTrustline)
|
||||
{
|
||||
// LPToken trustline
|
||||
if (isLPTokenTrustline)
|
||||
{
|
||||
if (++nLPTokenTrustLines > 1)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
else if (++nIOUTrustLines > 2)
|
||||
{
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
// Another Liquidity Provider LPToken trustline
|
||||
else if (isLPTokenTrustline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (++nIOUTrustLines > 2)
|
||||
{
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
|
||||
if (uNodeNext == 0)
|
||||
{
|
||||
if (nLPTokenTrustLines != 1 || nIOUTrustLines == 0 || nIOUTrustLines > 2)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
return true;
|
||||
}
|
||||
currentIndex = keylet::page(root, uNodeNext);
|
||||
}
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
Expected<bool, TER>
|
||||
verifyAndAdjustLPTokenBalance(
|
||||
Sandbox& sb,
|
||||
STAmount const& lpTokens,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account)
|
||||
{
|
||||
auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account);
|
||||
if (!res.has_value())
|
||||
{
|
||||
return Unexpected<TER>(res.error());
|
||||
}
|
||||
|
||||
if (res.value())
|
||||
{
|
||||
if (withinRelativeDistance(
|
||||
lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3}))
|
||||
{
|
||||
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
|
||||
sb.update(ammSle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unexpected<TER>(tecAMM_INVALID_TOKENS);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -80,7 +80,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
|
||||
auto const fullBalance = sle->getFieldAmount(sfBalance);
|
||||
|
||||
auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
|
||||
auto const balance = view.balanceHookIOU(id, xrpAccount(), fullBalance);
|
||||
|
||||
STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
|
||||
|
||||
|
||||
@@ -258,6 +258,13 @@ removeEmptyHolding(
|
||||
(view.rules().enabled(fixSecurity3_1_3) && (*mptoken)[~sfLockedAmount].value_or(0) != 0))
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
// Don't delete if the token still has confidential balances
|
||||
if (mptoken->isFieldPresent(sfConfidentialBalanceInbox) ||
|
||||
mptoken->isFieldPresent(sfConfidentialBalanceSpending))
|
||||
{
|
||||
return tecHAS_OBLIGATIONS;
|
||||
}
|
||||
|
||||
return authorizeMPToken(
|
||||
view,
|
||||
{}, // priorBalance
|
||||
@@ -306,18 +313,11 @@ requireAuth(
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const asset = sleVault->at(sfAsset);
|
||||
if (auto const err = std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return requireAuth(view, issue, account, authType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return requireAuth(view, issue, account, authType, depth + 1);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
if (auto const err = asset.visit(
|
||||
[&](Issue const& issue) { return requireAuth(view, issue, account, authType); },
|
||||
[&](MPTIssue const& issue) {
|
||||
return requireAuth(view, issue, account, authType, depth + 1);
|
||||
});
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
@@ -487,6 +487,21 @@ canTransfer(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
canTrade(ReadView const& view, Asset const& asset)
|
||||
{
|
||||
return asset.visit(
|
||||
[&](Issue const&) -> TER { return tesSUCCESS; },
|
||||
[&](MPTIssue const& mptIssue) -> TER {
|
||||
auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTrade))
|
||||
return tecNO_PERMISSION;
|
||||
return tesSUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
TER
|
||||
lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j)
|
||||
{
|
||||
@@ -770,4 +785,149 @@ createMPToken(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkCreateMPT(
|
||||
xrpl::ApplyView& view,
|
||||
xrpl::MPTIssue const& mptIssue,
|
||||
xrpl::AccountID const& holder,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (mptIssue.getIssuer() == holder)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const mptIssuanceID = keylet::mptIssuance(mptIssue.getMptID());
|
||||
auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder);
|
||||
if (!view.exists(mptokenID))
|
||||
{
|
||||
if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, 0);
|
||||
!isTesSuccess(err))
|
||||
{
|
||||
return err;
|
||||
}
|
||||
auto const sleAcct = view.peek(keylet::account(holder));
|
||||
if (!sleAcct)
|
||||
{
|
||||
return tecINTERNAL;
|
||||
}
|
||||
adjustOwnerCount(view, sleAcct, 1, j);
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
maxMPTAmount(SLE const& sleIssuance)
|
||||
{
|
||||
return sleIssuance[~sfMaximumAmount].value_or(maxMPTokenAmount);
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(SLE const& sleIssuance)
|
||||
{
|
||||
auto const max = maxMPTAmount(sleIssuance);
|
||||
auto const outstanding = sleIssuance[sfOutstandingAmount];
|
||||
return max - outstanding;
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID)
|
||||
{
|
||||
auto const sle = view.read(keylet::mptIssuance(mptID));
|
||||
if (!sle)
|
||||
Throw<std::runtime_error>(transHuman(tecINTERNAL));
|
||||
return availableMPTAmount(*sle);
|
||||
}
|
||||
|
||||
bool
|
||||
isMPTOverflow(
|
||||
std::int64_t sendAmount,
|
||||
std::uint64_t outstandingAmount,
|
||||
std::int64_t maximumAmount,
|
||||
AllowMPTOverflow allowOverflow)
|
||||
{
|
||||
std::uint64_t const limit = (allowOverflow == AllowMPTOverflow::Yes)
|
||||
? std::numeric_limits<std::uint64_t>::max()
|
||||
: maximumAmount;
|
||||
return (sendAmount > maximumAmount || outstandingAmount > (limit - sendAmount));
|
||||
}
|
||||
|
||||
STAmount
|
||||
issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue)
|
||||
{
|
||||
STAmount amount{issue};
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(issue));
|
||||
if (!sle)
|
||||
return amount;
|
||||
auto const available = availableMPTAmount(*sle);
|
||||
return view.balanceHookSelfIssueMPT(issue, available);
|
||||
}
|
||||
|
||||
void
|
||||
issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount)
|
||||
{
|
||||
auto const available = availableMPTAmount(view, issue);
|
||||
view.issuerSelfDebitHookMPT(issue, amount, available);
|
||||
}
|
||||
|
||||
static TER
|
||||
checkMPTAllowed(ReadView const& view, TxType txType, Asset const& asset, AccountID const& accountID)
|
||||
{
|
||||
if (!asset.holds<MPTIssue>())
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issuanceID = asset.get<MPTIssue>().getMptID();
|
||||
auto const validTx = txType == ttAMM_CREATE || txType == ttAMM_DEPOSIT ||
|
||||
txType == ttAMM_WITHDRAW || txType == ttOFFER_CREATE || txType == ttCHECK_CREATE ||
|
||||
txType == ttCHECK_CASH || txType == ttPAYMENT;
|
||||
XRPL_ASSERT(validTx, "xrpl::checkMPTAllowed : all MPT tx or DEX");
|
||||
if (!validTx)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const& issuer = asset.getIssuer();
|
||||
if (!view.exists(keylet::account(issuer)))
|
||||
return tecNO_ISSUER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const issuanceKey = keylet::mptIssuance(issuanceID);
|
||||
auto const issuanceSle = view.read(issuanceKey);
|
||||
if (!issuanceSle)
|
||||
return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
|
||||
|
||||
auto const flags = issuanceSle->getFlags();
|
||||
|
||||
if ((flags & lsfMPTLocked) != 0u)
|
||||
return tecLOCKED; // LCOV_EXCL_LINE
|
||||
// Offer crossing and Payment
|
||||
if ((flags & lsfMPTCanTrade) == 0)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (accountID != issuer)
|
||||
{
|
||||
if ((flags & lsfMPTCanTransfer) == 0)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const mptSle = view.read(keylet::mptoken(issuanceKey.key, accountID));
|
||||
// Allow to succeed since some tx create MPToken if it doesn't exist.
|
||||
// Tx's have their own check for missing MPToken.
|
||||
if (!mptSle)
|
||||
return tesSUCCESS;
|
||||
|
||||
if (mptSle->isFlag(lsfMPTLocked))
|
||||
return tecLOCKED;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkMPTTxAllowed(
|
||||
ReadView const& view,
|
||||
TxType txType,
|
||||
Asset const& asset,
|
||||
AccountID const& accountID)
|
||||
{
|
||||
// use isDEXAllowed for payment/offer crossing
|
||||
XRPL_ASSERT(txType != ttPAYMENT, "xrpl::checkMPTTxAllowed : not payment");
|
||||
return checkMPTAllowed(view, txType, asset, accountID);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -855,15 +855,15 @@ tokenOfferCreatePreclaim(
|
||||
if (view.rules().enabled(featureNFTokenMintOffer))
|
||||
{
|
||||
if (nftIssuer != amount.getIssuer() &&
|
||||
!view.read(keylet::line(nftIssuer, amount.issue())))
|
||||
!view.read(keylet::line(nftIssuer, amount.get<Issue>())))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
else if (!view.exists(keylet::line(nftIssuer, amount.issue())))
|
||||
else if (!view.exists(keylet::line(nftIssuer, amount.get<Issue>())))
|
||||
{
|
||||
return tecNO_LINE;
|
||||
}
|
||||
|
||||
if (isFrozen(view, nftIssuer, amount.getCurrency(), amount.getIssuer()))
|
||||
if (isFrozen(view, nftIssuer, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ tokenOfferCreatePreclaim(
|
||||
return tefNFTOKEN_IS_NOT_TRANSFERABLE;
|
||||
}
|
||||
|
||||
if (isFrozen(view, acctID, amount.getCurrency(), amount.getIssuer()))
|
||||
if (isFrozen(view, acctID, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
// If this is an offer to buy the token, the account must have the
|
||||
|
||||
@@ -33,11 +33,14 @@ creditLimit(
|
||||
if (sleRippleState)
|
||||
{
|
||||
result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit);
|
||||
result.setIssuer(account);
|
||||
result.get<Issue>().account = account;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match");
|
||||
XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match");
|
||||
XRPL_ASSERT(
|
||||
result.get<Issue>().currency == currency,
|
||||
"xrpl::creditLimit : result currency "
|
||||
"match");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -63,11 +66,14 @@ creditBalance(
|
||||
result = sleRippleState->getFieldAmount(sfBalance);
|
||||
if (account < issuer)
|
||||
result.negate();
|
||||
result.setIssuer(account);
|
||||
result.get<Issue>().account = account;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match");
|
||||
XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match");
|
||||
XRPL_ASSERT(
|
||||
result.get<Issue>().currency == currency,
|
||||
"xrpl::creditBalance : result currency "
|
||||
"match");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -222,7 +228,7 @@ trustCreate(
|
||||
sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
|
||||
sleRippleState->setFieldAmount(
|
||||
bSetHigh ? sfLowLimit : sfHighLimit,
|
||||
STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
|
||||
STAmount(Issue{saBalance.get<Issue>().currency, bSetDst ? uSrcAccountID : uDstAccountID}));
|
||||
|
||||
if (uQualityIn != 0u)
|
||||
sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
|
||||
@@ -261,7 +267,7 @@ trustCreate(
|
||||
// ONLY: Create ripple balance.
|
||||
sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
|
||||
|
||||
view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
|
||||
view.creditHookIOU(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -367,7 +373,7 @@ issueIOU(
|
||||
"xrpl::issueIOU : neither account nor issuer is XRP");
|
||||
|
||||
// Consistency check
|
||||
XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue");
|
||||
XRPL_ASSERT(issue == amount.get<Issue>(), "xrpl::issueIOU : matching issue");
|
||||
|
||||
// Can't send to self!
|
||||
XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account");
|
||||
@@ -392,7 +398,7 @@ issueIOU(
|
||||
auto const must_delete = updateTrustLine(
|
||||
view, state, bSenderHigh, issue.account, start_balance, final_balance, j);
|
||||
|
||||
view.creditHook(issue.account, account, amount, start_balance);
|
||||
view.creditHookIOU(issue.account, account, amount, start_balance);
|
||||
|
||||
if (bSenderHigh)
|
||||
final_balance.negate();
|
||||
@@ -422,7 +428,7 @@ issueIOU(
|
||||
STAmount const limit(Issue{issue.currency, account});
|
||||
STAmount final_balance = amount;
|
||||
|
||||
final_balance.setIssuer(noAccount());
|
||||
final_balance.get<Issue>().account = noAccount();
|
||||
|
||||
auto const receiverAccount = view.peek(keylet::account(account));
|
||||
if (!receiverAccount)
|
||||
@@ -461,7 +467,7 @@ redeemIOU(
|
||||
"xrpl::redeemIOU : neither account nor issuer is XRP");
|
||||
|
||||
// Consistency check
|
||||
XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue");
|
||||
XRPL_ASSERT(issue == amount.get<Issue>(), "xrpl::redeemIOU : matching issue");
|
||||
|
||||
// Can't send to self!
|
||||
XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account");
|
||||
@@ -484,7 +490,7 @@ redeemIOU(
|
||||
auto const must_delete =
|
||||
updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j);
|
||||
|
||||
view.creditHook(account, issue.account, amount, start_balance);
|
||||
view.creditHookIOU(account, issue.account, amount, start_balance);
|
||||
|
||||
if (bSenderHigh)
|
||||
final_balance.negate();
|
||||
@@ -757,4 +763,20 @@ deleteAMMTrustLine(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
deleteAMMMPToken(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> sleMpt,
|
||||
AccountID const& ammAccountID,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (!view.dirRemove(
|
||||
keylet::ownerDir(ammAccountID), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
view.erase(sleMpt);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -23,8 +23,8 @@ bool
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& asset,
|
||||
Issue const& asset2);
|
||||
Asset const& asset,
|
||||
Asset const& asset2);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
@@ -35,18 +35,9 @@ isLPTokenFrozen(
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, Asset const& asset)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return isGlobalFrozen(view, issue.getIssuer());
|
||||
}
|
||||
else
|
||||
{
|
||||
return isGlobalFrozen(view, issue);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
return asset.visit(
|
||||
[&](Issue const& issue) { return isGlobalFrozen(view, issue.getIssuer()); },
|
||||
[&](MPTIssue const& issue) { return isGlobalFrozen(view, issue); });
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -103,18 +94,9 @@ isAnyFrozen(
|
||||
Asset const& asset,
|
||||
int depth)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return isAnyFrozen(view, accounts, issue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return isAnyFrozen(view, accounts, issue, depth);
|
||||
}
|
||||
},
|
||||
asset.value());
|
||||
return asset.visit(
|
||||
[&](Issue const& issue) { return isAnyFrozen(view, accounts, issue); },
|
||||
[&](MPTIssue const& issue) { return isAnyFrozen(view, accounts, issue, depth); });
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -190,11 +172,7 @@ getLineIfUsable(
|
||||
auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID]));
|
||||
|
||||
if (!sleAmm ||
|
||||
isLPTokenFrozen(
|
||||
view,
|
||||
account,
|
||||
(*sleAmm)[sfAsset].get<Issue>(),
|
||||
(*sleAmm)[sfAsset2].get<Issue>()))
|
||||
isLPTokenFrozen(view, account, (*sleAmm)[sfAsset], (*sleAmm)[sfAsset2]))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -230,7 +208,7 @@ getTrustLineBalance(
|
||||
{
|
||||
amount += sle->getFieldAmount(oppositeField);
|
||||
}
|
||||
amount.setIssuer(issuer);
|
||||
amount.get<Issue>().account = issuer;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -240,7 +218,7 @@ getTrustLineBalance(
|
||||
JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account)
|
||||
<< " amount=" << amount.getFullText();
|
||||
|
||||
return view.balanceHook(account, issuer, amount);
|
||||
return view.balanceHookIOU(account, issuer, amount);
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -298,6 +276,9 @@ accountHolds(
|
||||
SpendableHandling includeFullBalance)
|
||||
{
|
||||
bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
|
||||
STAmount amount{mptIssue};
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
bool const mptokensV2 = view.rules().enabled(featureMPTokensV2);
|
||||
|
||||
if (returnSpendable && account == mptIssue.getIssuer())
|
||||
{
|
||||
@@ -307,16 +288,14 @@ accountHolds(
|
||||
|
||||
if (!issuance)
|
||||
{
|
||||
return STAmount{mptIssue};
|
||||
return amount;
|
||||
}
|
||||
return STAmount{
|
||||
mptIssue,
|
||||
issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) -
|
||||
issuance->at(sfOutstandingAmount)};
|
||||
auto const available = availableMPTAmount(*issuance);
|
||||
if (!mptokensV2)
|
||||
return STAmount{mptIssue, available};
|
||||
return view.balanceHookMPT(issuer, mptIssue, available);
|
||||
}
|
||||
|
||||
STAmount amount;
|
||||
|
||||
auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account));
|
||||
|
||||
if (!sleMpt)
|
||||
@@ -334,7 +313,8 @@ accountHolds(
|
||||
// Only if auth check is needed, as it needs to do an additional read
|
||||
// operation. Note featureSingleAssetVault will affect error codes.
|
||||
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
|
||||
view.rules().enabled(featureSingleAssetVault))
|
||||
(view.rules().enabled(featureSingleAssetVault) ||
|
||||
view.rules().enabled(featureConfidentialTransfer)))
|
||||
{
|
||||
if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
|
||||
!isTesSuccess(err))
|
||||
@@ -352,6 +332,8 @@ accountHolds(
|
||||
}
|
||||
}
|
||||
|
||||
if (view.rules().enabled(featureMPTokensV2))
|
||||
return view.balanceHookMPT(account, mptIssue, amount.mpt().value());
|
||||
return amount;
|
||||
}
|
||||
|
||||
@@ -365,19 +347,14 @@ accountHolds(
|
||||
beast::Journal j,
|
||||
SpendableHandling includeFullBalance)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& value) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance);
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
{
|
||||
return accountHolds(
|
||||
view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
|
||||
}
|
||||
return asset.visit(
|
||||
[&](Issue const& issue) {
|
||||
return accountHolds(view, account, issue, zeroIfFrozen, j, includeFullBalance);
|
||||
},
|
||||
asset.value());
|
||||
[&](MPTIssue const& issue) {
|
||||
return accountHolds(
|
||||
view, account, issue, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
|
||||
});
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -388,28 +365,38 @@ accountFunds(
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j)
|
||||
{
|
||||
XRPL_ASSERT(saDefault.holds<Issue>(), "xrpl::accountFunds: saDefault holds Issue");
|
||||
|
||||
if (!saDefault.native() && saDefault.getIssuer() == id)
|
||||
return saDefault;
|
||||
|
||||
return accountHolds(
|
||||
view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j);
|
||||
view, id, saDefault.get<Issue>().currency, saDefault.getIssuer(), freezeHandling, j);
|
||||
}
|
||||
|
||||
STAmount
|
||||
accountFunds(
|
||||
ReadView const& view,
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal j)
|
||||
{
|
||||
return saDefault.asset().visit(
|
||||
[&](Issue const&) { return accountFunds(view, id, saDefault, freezeHandling, j); },
|
||||
[&](MPTIssue const&) {
|
||||
return accountHolds(
|
||||
view, id, saDefault.asset(), freezeHandling, authHandling, j, shFULL_BALANCE);
|
||||
});
|
||||
}
|
||||
|
||||
Rate
|
||||
transferRate(ReadView const& view, STAmount const& amount)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return transferRate(view, issue.getIssuer());
|
||||
}
|
||||
else
|
||||
{
|
||||
return transferRate(view, issue.getMptID());
|
||||
}
|
||||
},
|
||||
amount.asset().value());
|
||||
return amount.asset().visit(
|
||||
[&](Issue const& issue) { return transferRate(view, issue.getIssuer()); },
|
||||
[&](MPTIssue const& issue) { return transferRate(view, issue.getMptID()); });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -522,7 +509,7 @@ directSendNoFeeIOU(
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const& issuer = saAmount.getIssuer();
|
||||
Currency const& currency = saAmount.getCurrency();
|
||||
Currency const& currency = saAmount.get<Issue>().currency;
|
||||
|
||||
// Make sure issuer is involved.
|
||||
XRPL_ASSERT(
|
||||
@@ -551,7 +538,7 @@ directSendNoFeeIOU(
|
||||
if (bSenderHigh)
|
||||
saBalance.negate(); // Put balance in sender terms.
|
||||
|
||||
view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
|
||||
view.creditHookIOU(uSenderID, uReceiverID, saAmount, saBalance);
|
||||
|
||||
STAmount const saBefore = saBalance;
|
||||
|
||||
@@ -622,7 +609,7 @@ directSendNoFeeIOU(
|
||||
STAmount const saReceiverLimit(Issue{currency, uReceiverID});
|
||||
STAmount saBalance{saAmount};
|
||||
|
||||
saBalance.setIssuer(noAccount());
|
||||
saBalance.get<Issue>().account = noAccount();
|
||||
|
||||
JLOG(j.debug()) << "directSendNoFeeIOU: "
|
||||
"create line: "
|
||||
@@ -859,7 +846,7 @@ accountSendIOU(
|
||||
else
|
||||
{
|
||||
auto const sndBal = sender->getFieldAmount(sfBalance);
|
||||
view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
|
||||
view.creditHookIOU(uSenderID, xrpAccount(), saAmount, sndBal);
|
||||
|
||||
// Decrement XRP balance.
|
||||
sender->setFieldAmount(sfBalance, sndBal - saAmount);
|
||||
@@ -872,7 +859,7 @@ accountSendIOU(
|
||||
// Increment XRP balance.
|
||||
auto const rcvBal = receiver->getFieldAmount(sfBalance);
|
||||
receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
|
||||
view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
|
||||
view.creditHookIOU(xrpAccount(), uReceiverID, saAmount, -rcvBal);
|
||||
|
||||
view.update(receiver);
|
||||
}
|
||||
@@ -975,7 +962,7 @@ accountSendMultiIOU(
|
||||
// Increment XRP balance.
|
||||
auto const rcvBal = receiver->getFieldAmount(sfBalance);
|
||||
receiver->setFieldAmount(sfBalance, rcvBal + amount);
|
||||
view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
|
||||
view.creditHookIOU(xrpAccount(), receiverID, amount, -rcvBal);
|
||||
|
||||
view.update(receiver);
|
||||
|
||||
@@ -1003,7 +990,7 @@ accountSendMultiIOU(
|
||||
return TER{tecFAILED_PROCESSING};
|
||||
}
|
||||
auto const sndBal = sender->getFieldAmount(sfBalance);
|
||||
view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal);
|
||||
view.creditHookIOU(senderID, xrpAccount(), takeFromSender, sndBal);
|
||||
|
||||
// Decrement XRP balance.
|
||||
sender->setFieldAmount(sfBalance, sndBal - takeFromSender);
|
||||
@@ -1037,9 +1024,20 @@ directSendNoFeeMPT(
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto const maxAmount = maxMPTAmount(*sleIssuance);
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const available = availableMPTAmount(*sleIssuance);
|
||||
auto const amt = saAmount.mpt().value();
|
||||
|
||||
if (uSenderID == issuer)
|
||||
{
|
||||
(*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
|
||||
if (view.rules().enabled(featureMPTokensV2))
|
||||
{
|
||||
if (isMPTOverflow(amt, outstanding, maxAmount, AllowMPTOverflow::Yes))
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
(*sleIssuance)[sfOutstandingAmount] += amt;
|
||||
view.update(sleIssuance);
|
||||
}
|
||||
else
|
||||
@@ -1047,11 +1045,11 @@ directSendNoFeeMPT(
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
|
||||
if (auto sle = view.peek(mptokenID))
|
||||
{
|
||||
auto const amt = sle->getFieldU64(sfMPTAmount);
|
||||
auto const pay = saAmount.mpt().value();
|
||||
if (amt < pay)
|
||||
auto const senderBalance = sle->getFieldU64(sfMPTAmount);
|
||||
if (senderBalance < amt)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
(*sle)[sfMPTAmount] = amt - pay;
|
||||
view.creditHookMPT(uSenderID, uReceiverID, saAmount, (*sle)[sfMPTAmount], available);
|
||||
(*sle)[sfMPTAmount] = senderBalance - amt;
|
||||
view.update(sle);
|
||||
}
|
||||
else
|
||||
@@ -1062,11 +1060,9 @@ directSendNoFeeMPT(
|
||||
|
||||
if (uReceiverID == issuer)
|
||||
{
|
||||
auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
|
||||
auto const redeem = saAmount.mpt().value();
|
||||
if (outstanding >= redeem)
|
||||
if (outstanding >= amt)
|
||||
{
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - amt);
|
||||
view.update(sleIssuance);
|
||||
}
|
||||
else
|
||||
@@ -1079,7 +1075,8 @@ directSendNoFeeMPT(
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
|
||||
if (auto sle = view.peek(mptokenID))
|
||||
{
|
||||
(*sle)[sfMPTAmount] += saAmount.mpt().value();
|
||||
view.creditHookMPT(uSenderID, uReceiverID, saAmount, (*sle)[sfMPTAmount], available);
|
||||
(*sle)[sfMPTAmount] += amt;
|
||||
view.update(sle);
|
||||
}
|
||||
else
|
||||
@@ -1099,7 +1096,8 @@ directSendNoLimitMPT(
|
||||
STAmount const& saAmount,
|
||||
STAmount& saActual,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
WaiveTransferFee waiveFee,
|
||||
AllowMPTOverflow allowOverflow)
|
||||
{
|
||||
XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::directSendNoLimitMPT : sender is not receiver");
|
||||
|
||||
@@ -1117,9 +1115,13 @@ directSendNoLimitMPT(
|
||||
if (uSenderID == issuer)
|
||||
{
|
||||
auto const sendAmount = saAmount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount ||
|
||||
sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
auto const maxAmount = maxMPTAmount(*sle);
|
||||
auto const outstanding = sle->getFieldU64(sfOutstandingAmount);
|
||||
auto const mptokensV2 = view.rules().enabled(featureMPTokensV2);
|
||||
allowOverflow = (allowOverflow == AllowMPTOverflow::Yes && mptokensV2)
|
||||
? AllowMPTOverflow::Yes
|
||||
: AllowMPTOverflow::No;
|
||||
if (isMPTOverflow(sendAmount, outstanding, maxAmount, allowOverflow))
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
@@ -1233,7 +1235,8 @@ directSendNoLimitMultiMPT(
|
||||
}
|
||||
|
||||
// Direct send: redeeming MPTs and/or sending own MPTs.
|
||||
if (auto const ter = directSendNoFeeMPT(view, senderID, receiverID, amount, j))
|
||||
if (auto const ter = directSendNoFeeMPT(view, senderID, receiverID, amount, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because directSendNoFeeMPT took it.
|
||||
@@ -1252,13 +1255,15 @@ directSendNoLimitMultiMPT(
|
||||
<< to_string(receiverID) << " : deliver=" << amount.getFullText()
|
||||
<< " cost=" << actualSend.getFullText();
|
||||
|
||||
if (auto const terResult = directSendNoFeeMPT(view, issuer, receiverID, amount, j))
|
||||
return terResult;
|
||||
if (auto const ter = directSendNoFeeMPT(view, issuer, receiverID, amount, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
}
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = directSendNoFeeMPT(view, senderID, issuer, takeFromSender, j))
|
||||
return terResult;
|
||||
if (auto const ter = directSendNoFeeMPT(view, senderID, issuer, takeFromSender, j);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -1271,7 +1276,8 @@ accountSendMPT(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
WaiveTransferFee waiveFee,
|
||||
AllowMPTOverflow allowOverflow)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
|
||||
@@ -1285,7 +1291,8 @@ accountSendMPT(
|
||||
|
||||
STAmount saActual{saAmount.asset()};
|
||||
|
||||
return directSendNoLimitMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
|
||||
return directSendNoLimitMPT(
|
||||
view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee, allowOverflow);
|
||||
}
|
||||
|
||||
static TER
|
||||
@@ -1317,19 +1324,14 @@ directSendNoFee(
|
||||
bool bCheckIssuer,
|
||||
beast::Journal j)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPL_ASSERT(!bCheckIssuer, "xrpl::directSendNoFee : not checking issuer");
|
||||
return directSendNoFeeMPT(view, uSenderID, uReceiverID, saAmount, j);
|
||||
}
|
||||
return saAmount.asset().visit(
|
||||
[&](Issue const&) {
|
||||
return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
|
||||
},
|
||||
saAmount.asset().value());
|
||||
[&](MPTIssue const&) {
|
||||
XRPL_ASSERT(!bCheckIssuer, "xrpl::directSendNoFee : not checking issuer");
|
||||
return directSendNoFeeMPT(view, uSenderID, uReceiverID, saAmount, j);
|
||||
});
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -1339,20 +1341,17 @@ accountSend(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
WaiveTransferFee waiveFee,
|
||||
AllowMPTOverflow allowOverflow)
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
|
||||
}
|
||||
else
|
||||
{
|
||||
return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
|
||||
}
|
||||
return saAmount.asset().visit(
|
||||
[&](Issue const&) {
|
||||
return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
|
||||
},
|
||||
saAmount.asset().value());
|
||||
[&](MPTIssue const&) {
|
||||
return accountSendMPT(
|
||||
view, uSenderID, uReceiverID, saAmount, j, waiveFee, allowOverflow);
|
||||
});
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -1366,18 +1365,13 @@ accountSendMulti(
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided");
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
{
|
||||
return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee);
|
||||
}
|
||||
else
|
||||
{
|
||||
return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee);
|
||||
}
|
||||
return asset.visit(
|
||||
[&](Issue const& issue) {
|
||||
return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee);
|
||||
},
|
||||
asset.value());
|
||||
[&](MPTIssue const& issue) {
|
||||
return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee);
|
||||
});
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -21,12 +21,23 @@
|
||||
namespace xrpl {
|
||||
|
||||
Currency
|
||||
ammLPTCurrency(Currency const& cur1, Currency const& cur2)
|
||||
ammLPTCurrency(Asset const& asset1, Asset const& asset2)
|
||||
{
|
||||
// AMM LPToken is 0x03 plus 19 bytes of the hash
|
||||
std::int32_t constexpr AMMCurrencyCode = 0x03;
|
||||
auto const [minC, maxC] = std::minmax(cur1, cur2);
|
||||
auto const hash = sha512Half(minC, maxC);
|
||||
auto const& [minA, maxA] = std::minmax(asset1, asset2);
|
||||
uint256 const hash = std::visit(
|
||||
[](auto&& issue1, auto&& issue2) {
|
||||
auto fromIss = []<ValidIssueType T>(T const& issue) {
|
||||
if constexpr (std::is_same_v<T, Issue>)
|
||||
return issue.currency;
|
||||
if constexpr (std::is_same_v<T, MPTIssue>)
|
||||
return issue.getMptID();
|
||||
};
|
||||
return sha512Half(fromIss(issue1), fromIss(issue2));
|
||||
},
|
||||
minA.value(),
|
||||
maxA.value());
|
||||
Currency currency;
|
||||
*currency.begin() = AMMCurrencyCode;
|
||||
std::copy(hash.begin(), hash.begin() + currency.size() - 1, currency.begin() + 1);
|
||||
@@ -34,34 +45,45 @@ ammLPTCurrency(Currency const& cur1, Currency const& cur2)
|
||||
}
|
||||
|
||||
Issue
|
||||
ammLPTIssue(Currency const& cur1, Currency const& cur2, AccountID const& ammAccountID)
|
||||
ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID)
|
||||
{
|
||||
return Issue(ammLPTCurrency(cur1, cur2), ammAccountID);
|
||||
return Issue(ammLPTCurrency(asset1, asset2), ammAccountID);
|
||||
}
|
||||
|
||||
NotTEC
|
||||
invalidAMMAsset(Issue const& issue, std::optional<std::pair<Issue, Issue>> const& pair)
|
||||
invalidAMMAsset(Asset const& asset, std::optional<std::pair<Asset, Asset>> const& pair)
|
||||
{
|
||||
if (badCurrency() == issue.currency)
|
||||
return temBAD_CURRENCY;
|
||||
if (isXRP(issue) && issue.account.isNonZero())
|
||||
return temBAD_ISSUER;
|
||||
if (pair && issue != pair->first && issue != pair->second)
|
||||
auto const err = asset.visit(
|
||||
[](MPTIssue const& issue) -> std::optional<NotTEC> {
|
||||
if (issue.getIssuer() == beast::zero)
|
||||
return temBAD_MPT;
|
||||
return std::nullopt;
|
||||
},
|
||||
[](Issue const& issue) -> std::optional<NotTEC> {
|
||||
if (badCurrency() == issue.currency)
|
||||
return temBAD_CURRENCY;
|
||||
if (isXRP(issue) && issue.getIssuer().isNonZero())
|
||||
return temBAD_ISSUER;
|
||||
return std::nullopt;
|
||||
});
|
||||
if (err)
|
||||
return *err;
|
||||
if (pair && asset != pair->first && asset != pair->second)
|
||||
return temBAD_AMM_TOKENS;
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
invalidAMMAssetPair(
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair)
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair)
|
||||
{
|
||||
if (issue1 == issue2)
|
||||
if (asset1 == asset2)
|
||||
return temBAD_AMM_TOKENS;
|
||||
if (auto const res = invalidAMMAsset(issue1, pair))
|
||||
if (auto const res = invalidAMMAsset(asset1, pair))
|
||||
return res;
|
||||
if (auto const res = invalidAMMAsset(issue2, pair))
|
||||
if (auto const res = invalidAMMAsset(asset2, pair))
|
||||
return res;
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -69,10 +91,10 @@ invalidAMMAssetPair(
|
||||
NotTEC
|
||||
invalidAMMAmount(
|
||||
STAmount const& amount,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair,
|
||||
bool validZero)
|
||||
{
|
||||
if (auto const res = invalidAMMAsset(amount.issue(), pair))
|
||||
if (auto const res = invalidAMMAsset(amount.asset(), pair))
|
||||
return res;
|
||||
if (amount < beast::zero || (!validZero && amount == beast::zero))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
@@ -62,4 +62,11 @@ assetFromJson(Json::Value const& v)
|
||||
return mptIssueFromJson(v);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Asset const& x)
|
||||
{
|
||||
std::visit([&]<ValidIssueType TIss>(TIss const& issue) { os << issue; }, x.value());
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
445
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
445
src/libxrpl/protocol/ConfidentialTransfer.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <utility/mpt_utility.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Converts an XRPL AccountID to mpt-crypto lib C struct.
|
||||
*
|
||||
* @param account The AccountID.
|
||||
* @return The equivalent mpt-crypto lib account_id struct.
|
||||
*/
|
||||
account_id
|
||||
toAccountId(AccountID const& account)
|
||||
{
|
||||
account_id res;
|
||||
std::memcpy(res.bytes, account.data(), kMPT_ACCOUNT_ID_SIZE);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts an XRPL uint192 to mpt-crypto lib C struct.
|
||||
*
|
||||
* @param i The XRPL MPTokenIssuance ID.
|
||||
* @return The equivalent mpt-crypto lib mpt_issuance_id struct.
|
||||
*/
|
||||
mpt_issuance_id
|
||||
toIssuanceId(uint192 const& issuance)
|
||||
{
|
||||
mpt_issuance_id res;
|
||||
std::memcpy(res.bytes, issuance.data(), kMPT_ISSUANCE_ID_SIZE);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint256
|
||||
getSendContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
AccountID const& destination,
|
||||
std::uint32_t version)
|
||||
{
|
||||
uint256 result;
|
||||
mpt_get_send_context_hash(
|
||||
toAccountId(account),
|
||||
toIssuanceId(issuanceID),
|
||||
sequence,
|
||||
toAccountId(destination),
|
||||
version,
|
||||
result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint256
|
||||
getClawbackContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
AccountID const& holder)
|
||||
{
|
||||
uint256 result;
|
||||
mpt_get_clawback_context_hash(
|
||||
toAccountId(account),
|
||||
toIssuanceId(issuanceID),
|
||||
sequence,
|
||||
toAccountId(holder),
|
||||
result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint256
|
||||
getConvertContextHash(AccountID const& account, uint192 const& issuanceID, std::uint32_t sequence)
|
||||
{
|
||||
uint256 result;
|
||||
mpt_get_convert_context_hash(
|
||||
toAccountId(account), toIssuanceId(issuanceID), sequence, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint256
|
||||
getConvertBackContextHash(
|
||||
AccountID const& account,
|
||||
uint192 const& issuanceID,
|
||||
std::uint32_t sequence,
|
||||
std::uint32_t version)
|
||||
{
|
||||
uint256 result;
|
||||
mpt_get_convert_back_context_hash(
|
||||
toAccountId(account), toIssuanceId(issuanceID), sequence, version, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<EcPair>
|
||||
makeEcPair(Slice const& buffer)
|
||||
{
|
||||
if (buffer.length() != 2 * ecGamalEncryptedLength)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto parsePubKey = [](Slice const& slice, secp256k1_pubkey& out) {
|
||||
return secp256k1_ec_pubkey_parse(
|
||||
secp256k1Context(),
|
||||
&out,
|
||||
reinterpret_cast<unsigned char const*>(slice.data()),
|
||||
slice.length());
|
||||
};
|
||||
|
||||
Slice const s1{buffer.data(), ecGamalEncryptedLength};
|
||||
Slice const s2{buffer.data() + ecGamalEncryptedLength, ecGamalEncryptedLength};
|
||||
|
||||
EcPair pair{};
|
||||
if (parsePubKey(s1, pair.c1) != 1 || parsePubKey(s2, pair.c2) != 1)
|
||||
return std::nullopt;
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
std::optional<Buffer>
|
||||
serializeEcPair(EcPair const& pair)
|
||||
{
|
||||
auto serializePubKey = [](secp256k1_pubkey const& pub, unsigned char* out) {
|
||||
size_t outLen = ecGamalEncryptedLength; // 33 bytes
|
||||
int const ret = secp256k1_ec_pubkey_serialize(
|
||||
secp256k1Context(), out, &outLen, &pub, SECP256K1_EC_COMPRESSED);
|
||||
return ret == 1 && outLen == ecGamalEncryptedLength;
|
||||
};
|
||||
|
||||
Buffer buffer(ecGamalEncryptedTotalLength);
|
||||
unsigned char* ptr = buffer.data();
|
||||
bool const res1 = serializePubKey(pair.c1, ptr);
|
||||
bool const res2 = serializePubKey(pair.c2, ptr + ecGamalEncryptedLength);
|
||||
|
||||
if (!res1 || !res2)
|
||||
return std::nullopt;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool
|
||||
isValidCiphertext(Slice const& buffer)
|
||||
{
|
||||
return makeEcPair(buffer).has_value();
|
||||
}
|
||||
|
||||
bool
|
||||
isValidCompressedECPoint(Slice const& buffer)
|
||||
{
|
||||
if (buffer.size() != compressedECPointLength)
|
||||
return false;
|
||||
|
||||
// Compressed EC points must start with 0x02 or 0x03
|
||||
if (buffer[0] != ecCompressedPrefixEvenY && buffer[0] != ecCompressedPrefixOddY)
|
||||
return false;
|
||||
|
||||
secp256k1_pubkey point;
|
||||
return secp256k1_ec_pubkey_parse(secp256k1Context(), &point, buffer.data(), buffer.size()) == 1;
|
||||
}
|
||||
|
||||
std::optional<Buffer>
|
||||
homomorphicAdd(Slice const& a, Slice const& b)
|
||||
{
|
||||
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
|
||||
return std::nullopt;
|
||||
|
||||
auto const pairA = makeEcPair(a);
|
||||
auto const pairB = makeEcPair(b);
|
||||
|
||||
if (!pairA || !pairB)
|
||||
return std::nullopt;
|
||||
|
||||
EcPair sum{};
|
||||
if (auto res = secp256k1_elgamal_add(
|
||||
secp256k1Context(), &sum.c1, &sum.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
|
||||
res != 1)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return serializeEcPair(sum);
|
||||
}
|
||||
|
||||
std::optional<Buffer>
|
||||
homomorphicSubtract(Slice const& a, Slice const& b)
|
||||
{
|
||||
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
|
||||
return std::nullopt;
|
||||
|
||||
auto const pairA = makeEcPair(a);
|
||||
auto const pairB = makeEcPair(b);
|
||||
|
||||
if (!pairA || !pairB)
|
||||
return std::nullopt;
|
||||
|
||||
EcPair diff{};
|
||||
if (auto res = secp256k1_elgamal_subtract(
|
||||
secp256k1Context(), &diff.c1, &diff.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
|
||||
res != 1)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return serializeEcPair(diff);
|
||||
}
|
||||
|
||||
Buffer
|
||||
generateBlindingFactor()
|
||||
{
|
||||
unsigned char blindingFactor[ecBlindingFactorLength];
|
||||
|
||||
// todo: might need to be updated using another RNG
|
||||
if (RAND_bytes(blindingFactor, ecBlindingFactorLength) != 1)
|
||||
Throw<std::runtime_error>("Failed to generate random number");
|
||||
|
||||
return Buffer(blindingFactor, ecBlindingFactorLength);
|
||||
}
|
||||
|
||||
std::optional<Buffer>
|
||||
encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor)
|
||||
{
|
||||
if (blindingFactor.size() != ecBlindingFactorLength || pubKeySlice.size() != ecPubKeyLength)
|
||||
return std::nullopt;
|
||||
|
||||
Buffer out(ecGamalEncryptedTotalLength);
|
||||
if (mpt_encrypt_amount(amt, pubKeySlice.data(), blindingFactor.data(), out.data()) != 0)
|
||||
return std::nullopt;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<Buffer>
|
||||
encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId)
|
||||
{
|
||||
if (pubKeySlice.size() != ecPubKeyLength)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
EcPair pair{};
|
||||
secp256k1_pubkey pubKey;
|
||||
if (auto res = secp256k1_ec_pubkey_parse(
|
||||
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
|
||||
res != 1)
|
||||
{
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (auto res = generate_canonical_encrypted_zero(
|
||||
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, account.data(), mptId.data());
|
||||
res != 1)
|
||||
{
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return serializeEcPair(pair);
|
||||
}
|
||||
|
||||
TER
|
||||
verifyRevealedAmount(
|
||||
uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
ConfidentialRecipient const& holder,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor)
|
||||
{
|
||||
if (blindingFactor.size() != ecBlindingFactorLength ||
|
||||
holder.publicKey.size() != ecPubKeyLength ||
|
||||
holder.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
|
||||
issuer.publicKey.size() != ecPubKeyLength ||
|
||||
issuer.encryptedAmount.size() != ecGamalEncryptedTotalLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto toParticipant = [](ConfidentialRecipient const& r) {
|
||||
mpt_confidential_participant p;
|
||||
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
|
||||
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
|
||||
return p;
|
||||
};
|
||||
|
||||
auto const holderP = toParticipant(holder);
|
||||
auto const issuerP = toParticipant(issuer);
|
||||
|
||||
mpt_confidential_participant auditorP;
|
||||
mpt_confidential_participant const* auditorPtr = nullptr;
|
||||
if (auditor)
|
||||
{
|
||||
if (auditor->publicKey.size() != ecPubKeyLength ||
|
||||
auditor->encryptedAmount.size() != ecGamalEncryptedTotalLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
auditorP = toParticipant(*auditor);
|
||||
auditorPtr = &auditorP;
|
||||
}
|
||||
|
||||
if (mpt_verify_revealed_amount(amount, blindingFactor.data(), &holderP, &issuerP, auditorPtr) !=
|
||||
0)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object)
|
||||
{
|
||||
// Current usage of this function is only for ConfidentialMPTConvert and
|
||||
// ConfidentialMPTConvertBack transactions, which already enforce that these fields
|
||||
// are present.
|
||||
if (!object.isFieldPresent(sfHolderEncryptedAmount) ||
|
||||
!object.isFieldPresent(sfIssuerEncryptedAmount))
|
||||
return temMALFORMED; // LCOV_EXCL_LINE
|
||||
|
||||
if (object[sfHolderEncryptedAmount].length() != ecGamalEncryptedTotalLength ||
|
||||
object[sfIssuerEncryptedAmount].length() != ecGamalEncryptedTotalLength)
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
bool const hasAuditor = object.isFieldPresent(sfAuditorEncryptedAmount);
|
||||
if (hasAuditor && object[sfAuditorEncryptedAmount].length() != ecGamalEncryptedTotalLength)
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
if (!isValidCiphertext(object[sfHolderEncryptedAmount]) ||
|
||||
!isValidCiphertext(object[sfIssuerEncryptedAmount]))
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
if (hasAuditor && !isValidCiphertext(object[sfAuditorEncryptedAmount]))
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 const& contextHash)
|
||||
{
|
||||
if (proofSlice.size() != ecSchnorrProofLength || pubKeySlice.size() != ecPubKeyLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (mpt_verify_convert_proof(proofSlice.data(), pubKeySlice.data(), contextHash.data()) != 0)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifyClawbackEqualityProof(
|
||||
uint64_t const amount,
|
||||
Slice const& proof,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& ciphertext,
|
||||
uint256 const& contextHash)
|
||||
{
|
||||
if (ciphertext.size() != ecGamalEncryptedTotalLength || pubKeySlice.size() != ecPubKeyLength ||
|
||||
proof.size() != ecEqualityProofLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (mpt_verify_clawback_proof(
|
||||
proof.data(), amount, pubKeySlice.data(), ciphertext.data(), contextHash.data()) != 0)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifySendProof(
|
||||
Slice const& proof,
|
||||
ConfidentialRecipient const& sender,
|
||||
ConfidentialRecipient const& destination,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor,
|
||||
Slice const& spendingBalance,
|
||||
Slice const& amountCommitment,
|
||||
Slice const& balanceCommitment,
|
||||
uint256 const& contextHash)
|
||||
{
|
||||
auto const recipientCount = getConfidentialRecipientCount(auditor.has_value());
|
||||
auto const expectedProofSize = getEqualityProofSize(recipientCount) +
|
||||
2 * ecPedersenProofLength + ecDoubleBulletproofLength;
|
||||
|
||||
if (proof.size() != expectedProofSize || sender.publicKey.size() != ecPubKeyLength ||
|
||||
sender.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
|
||||
destination.publicKey.size() != ecPubKeyLength ||
|
||||
destination.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
|
||||
issuer.publicKey.size() != ecPubKeyLength ||
|
||||
issuer.encryptedAmount.size() != ecGamalEncryptedTotalLength ||
|
||||
spendingBalance.size() != ecGamalEncryptedTotalLength ||
|
||||
amountCommitment.size() != ecPedersenCommitmentLength ||
|
||||
balanceCommitment.size() != ecPedersenCommitmentLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto makeParticipant = [](ConfidentialRecipient const& r) {
|
||||
mpt_confidential_participant p;
|
||||
std::memcpy(p.pubkey, r.publicKey.data(), kMPT_PUBKEY_SIZE);
|
||||
std::memcpy(p.ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE);
|
||||
return p;
|
||||
};
|
||||
|
||||
std::vector<mpt_confidential_participant> participants(recipientCount);
|
||||
participants[0] = makeParticipant(sender);
|
||||
participants[1] = makeParticipant(destination);
|
||||
participants[2] = makeParticipant(issuer);
|
||||
if (auditor)
|
||||
{
|
||||
if (auditor->publicKey.size() != ecPubKeyLength ||
|
||||
auditor->encryptedAmount.size() != ecGamalEncryptedTotalLength)
|
||||
return tecINTERNAL;
|
||||
participants[3] = makeParticipant(*auditor);
|
||||
}
|
||||
|
||||
if (mpt_verify_send_proof(
|
||||
proof.data(),
|
||||
proof.size(),
|
||||
participants.data(),
|
||||
static_cast<uint8_t>(recipientCount),
|
||||
spendingBalance.data(),
|
||||
amountCommitment.data(),
|
||||
balanceCommitment.data(),
|
||||
contextHash.data()) != 0)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifyConvertBackProof(
|
||||
Slice const& proof,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& spendingBalance,
|
||||
Slice const& balanceCommitment,
|
||||
uint64_t amount,
|
||||
uint256 const& contextHash)
|
||||
{
|
||||
if (proof.size() != ecPedersenProofLength + ecSingleBulletproofLength ||
|
||||
pubKeySlice.size() != ecPubKeyLength ||
|
||||
spendingBalance.size() != ecGamalEncryptedTotalLength ||
|
||||
balanceCommitment.size() != ecPedersenCommitmentLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (mpt_verify_convert_back_proof(
|
||||
proof.data(),
|
||||
pubKeySlice.data(),
|
||||
spendingBalance.data(),
|
||||
balanceCommitment.data(),
|
||||
amount,
|
||||
contextHash.data()) != 0)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -99,19 +99,36 @@ getBookBase(Book const& book)
|
||||
{
|
||||
XRPL_ASSERT(isConsistent(book), "xrpl::getBookBase : input is consistent");
|
||||
|
||||
auto const index = book.domain ? indexHash(
|
||||
LedgerNameSpace::BOOK_DIR,
|
||||
book.in.currency,
|
||||
book.out.currency,
|
||||
book.in.account,
|
||||
book.out.account,
|
||||
*(book.domain))
|
||||
: indexHash(
|
||||
LedgerNameSpace::BOOK_DIR,
|
||||
book.in.currency,
|
||||
book.out.currency,
|
||||
book.in.account,
|
||||
book.out.account);
|
||||
auto getIndexHash = [&book]<typename... Args>(Args... args) {
|
||||
if (book.domain)
|
||||
return indexHash(std::forward<Args>(args)..., *book.domain);
|
||||
return indexHash(std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
auto const index = std::visit(
|
||||
[&]<ValidIssueType TIn, ValidIssueType TOut>(TIn const& in, TOut const& out) {
|
||||
if constexpr (std::is_same_v<TIn, Issue> && std::is_same_v<TOut, Issue>)
|
||||
{
|
||||
return getIndexHash(
|
||||
LedgerNameSpace::BOOK_DIR, in.currency, out.currency, in.account, out.account);
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIn, Issue> && std::is_same_v<TOut, MPTIssue>)
|
||||
{
|
||||
return getIndexHash(
|
||||
LedgerNameSpace::BOOK_DIR, in.currency, out.getMptID(), in.account);
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIn, MPTIssue> && std::is_same_v<TOut, Issue>)
|
||||
{
|
||||
return getIndexHash(
|
||||
LedgerNameSpace::BOOK_DIR, in.getMptID(), out.currency, out.account);
|
||||
}
|
||||
else
|
||||
{
|
||||
return getIndexHash(LedgerNameSpace::BOOK_DIR, in.getMptID(), out.getMptID());
|
||||
}
|
||||
},
|
||||
book.in.value(),
|
||||
book.out.value());
|
||||
|
||||
// Return with quality 0.
|
||||
auto k = keylet::quality({ltDIR_NODE, index}, 0);
|
||||
@@ -401,11 +418,37 @@ nft_sells(uint256 const& id) noexcept
|
||||
}
|
||||
|
||||
Keylet
|
||||
amm(Asset const& issue1, Asset const& issue2) noexcept
|
||||
amm(Asset const& asset1, Asset const& asset2) noexcept
|
||||
{
|
||||
auto const& [minI, maxI] = std::minmax(issue1.get<Issue>(), issue2.get<Issue>());
|
||||
return amm(
|
||||
indexHash(LedgerNameSpace::AMM, minI.account, minI.currency, maxI.account, maxI.currency));
|
||||
auto const& [minA, maxA] = std::minmax(asset1, asset2);
|
||||
return std::visit(
|
||||
[]<ValidIssueType TIss1, ValidIssueType TIss2>(TIss1 const& issue1, TIss2 const& issue2) {
|
||||
if constexpr (std::is_same_v<TIss1, Issue> && std::is_same_v<TIss2, Issue>)
|
||||
{
|
||||
return amm(indexHash(
|
||||
LedgerNameSpace::AMM,
|
||||
issue1.account,
|
||||
issue1.currency,
|
||||
issue2.account,
|
||||
issue2.currency));
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIss1, Issue> && std::is_same_v<TIss2, MPTIssue>)
|
||||
{
|
||||
return amm(indexHash(
|
||||
LedgerNameSpace::AMM, issue1.account, issue1.currency, issue2.getMptID()));
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIss1, MPTIssue> && std::is_same_v<TIss2, Issue>)
|
||||
{
|
||||
return amm(indexHash(
|
||||
LedgerNameSpace::AMM, issue1.getMptID(), issue2.account, issue2.currency));
|
||||
}
|
||||
else if constexpr (std::is_same_v<TIss1, MPTIssue> && std::is_same_v<TIss2, MPTIssue>)
|
||||
{
|
||||
return amm(indexHash(LedgerNameSpace::AMM, issue1.getMptID(), issue2.getMptID()));
|
||||
}
|
||||
},
|
||||
minA.value(),
|
||||
maxA.value());
|
||||
}
|
||||
|
||||
Keylet
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/json/json_errors.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -17,6 +16,11 @@ MPTIssue::MPTIssue(MPTID const& issuanceID) : mptID_(issuanceID)
|
||||
{
|
||||
}
|
||||
|
||||
MPTIssue::MPTIssue(std::uint32_t sequence, AccountID const& account)
|
||||
: MPTIssue(xrpl::makeMptID(sequence, account))
|
||||
{
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
MPTIssue::getIssuer() const
|
||||
{
|
||||
@@ -86,4 +90,11 @@ mptIssueFromJson(Json::Value const& v)
|
||||
return MPTIssue{id};
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, MPTIssue const& x)
|
||||
{
|
||||
os << to_string(x);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
19
src/libxrpl/protocol/PathAsset.cpp
Normal file
19
src/libxrpl/protocol/PathAsset.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/PathAsset.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::string
|
||||
to_string(PathAsset const& asset)
|
||||
{
|
||||
return std::visit([&](auto const& issue) { return to_string(issue); }, asset.value());
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, PathAsset const& x)
|
||||
{
|
||||
os << to_string(x);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -67,6 +67,11 @@ Permission::Permission()
|
||||
#pragma pop_macro("PERMISSION")
|
||||
};
|
||||
|
||||
XRPL_ASSERT(
|
||||
txFeatureMap_.size() == delegableTx_.size(),
|
||||
"xrpl::Permission : txFeatureMap_ and delegableTx_ must have same "
|
||||
"size");
|
||||
|
||||
for ([[maybe_unused]] auto const& permission : granularPermissionMap_)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user