mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-11 14:32:22 +00:00
Compare commits
211 Commits
bthomee/pi
...
ripple/was
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cc2169939 | ||
|
|
826f613ad8 | ||
|
|
1259c1d5ca | ||
|
|
dfcad69155 | ||
|
|
d2641d85bd | ||
|
|
75f66bd9fe | ||
|
|
7cd71cb659 | ||
|
|
9917f96166 | ||
|
|
e1cc82587b | ||
|
|
2cc9439fde | ||
|
|
52af9582e2 | ||
|
|
46e88dc732 | ||
|
|
bc24f2e211 | ||
|
|
7a7c993b15 | ||
|
|
9733ca8f91 | ||
|
|
18d5e3e226 | ||
|
|
b30b4e1d65 | ||
|
|
d435893602 | ||
|
|
00b0cf50f6 | ||
|
|
6d1a5be8d2 | ||
|
|
7ef256499c | ||
|
|
1338062be7 | ||
|
|
4fc1778ec8 | ||
|
|
65322d9e78 | ||
|
|
c5598a4284 | ||
|
|
0deb6bcadf | ||
|
|
9b013b559b | ||
|
|
1d4a3c00b8 | ||
|
|
4b34102e8e | ||
|
|
d006433579 | ||
|
|
a7ab8ee923 | ||
|
|
e0073a4402 | ||
|
|
2930ef217f | ||
|
|
9dbb301699 | ||
|
|
531e8b6ebd | ||
|
|
90397e1a52 | ||
|
|
888ca2e6d9 | ||
|
|
b6514b680f | ||
|
|
913e4b919e | ||
|
|
196e6a1b27 | ||
|
|
27468ddbcf | ||
|
|
bce5d91e45 | ||
|
|
654338fa66 | ||
|
|
9c25d18851 | ||
|
|
3a825a41e1 | ||
|
|
a9ebf786c6 | ||
|
|
5afe8cc321 | ||
|
|
bc5ec3c962 | ||
|
|
1775251e90 | ||
|
|
61bcb7621f | ||
|
|
a3f71b1774 | ||
|
|
4df7d1a4bb | ||
|
|
125df7a425 | ||
|
|
b08bcf5d21 | ||
|
|
dc413aef0c | ||
|
|
77dfd56ace | ||
|
|
953b9a3500 | ||
|
|
1d9ec84350 | ||
|
|
0392846a17 | ||
|
|
1b4a564369 | ||
|
|
fd524c4be9 | ||
|
|
495dda7f58 | ||
|
|
9c3c0280b1 | ||
|
|
f73d8a6cf2 | ||
|
|
6728ab52b7 | ||
|
|
77673663ca | ||
|
|
c1381f8ddd | ||
|
|
bd16f7989d | ||
|
|
65f9cf80c0 | ||
|
|
de55a5ebfc | ||
|
|
2ec4a1114e | ||
|
|
ba03a8a9d2 | ||
|
|
7c8279ec83 | ||
|
|
0418ffb26a | ||
|
|
b2627039f6 | ||
|
|
8f97ec3bde | ||
|
|
e85e7b1b1a | ||
|
|
803a344c65 | ||
|
|
4eb34f381a | ||
|
|
72fffb6e51 | ||
|
|
f7ee580f01 | ||
|
|
122d405750 | ||
|
|
c1c1b4ea67 | ||
|
|
977caea0a5 | ||
|
|
d7ed6d6512 | ||
|
|
f1f2e2629f | ||
|
|
917c610f96 | ||
|
|
317e533d81 | ||
|
|
4160677878 | ||
|
|
df98db1452 | ||
|
|
673476ef1b | ||
|
|
8bc6f9cd70 | ||
|
|
ba5debfecd | ||
|
|
f4a27c9b6d | ||
|
|
fd1cb318e3 | ||
|
|
8c3544a58c | ||
|
|
ed5139d4e3 | ||
|
|
42494dd4cf | ||
|
|
ce84cc8b44 | ||
|
|
9a9a7aab01 | ||
|
|
209a1a6ffa | ||
|
|
fc35a9f9c8 | ||
|
|
c5e50aa221 | ||
|
|
074b1f00d5 | ||
|
|
7a9d245950 | ||
|
|
1809fe07f2 | ||
|
|
409c67494a | ||
|
|
c626b6403a | ||
|
|
81cbc91927 | ||
|
|
1c812a6c4d | ||
|
|
0724927799 | ||
|
|
d83ec96848 | ||
|
|
375dd50b35 | ||
|
|
419d53ec4c | ||
|
|
d4d70d5675 | ||
|
|
6ab15f8377 | ||
|
|
91f3d51f3d | ||
|
|
9ed60b45f8 | ||
|
|
d5c53dcfd2 | ||
|
|
e94321fb41 | ||
|
|
bbc28b3b1c | ||
|
|
843e981c8a | ||
|
|
5aab274b7a | ||
|
|
2c30e41191 | ||
|
|
8ea5106b0b | ||
|
|
f57f67a8ae | ||
|
|
a98269f049 | ||
|
|
b66bc47ca9 | ||
|
|
0e9c7458bb | ||
|
|
1d89940653 | ||
|
|
1a1a6806ec | ||
|
|
1977df9c2e | ||
|
|
6c95548df5 | ||
|
|
69ab39d658 | ||
|
|
b9eb66eecc | ||
|
|
881087dd3d | ||
|
|
90e0bbd0fc | ||
|
|
b57df290de | ||
|
|
8a403f1241 | ||
|
|
6d2640871d | ||
|
|
c145598ff9 | ||
|
|
50e5608d86 | ||
|
|
7a7b96107c | ||
|
|
500bb68831 | ||
|
|
53eb0f60bc | ||
|
|
41205ae928 | ||
|
|
c33b0ae463 | ||
|
|
16087c9680 | ||
|
|
56bc6d58f6 | ||
|
|
ef5d335e09 | ||
|
|
25c3060fef | ||
|
|
ce9f0b38a4 | ||
|
|
35f7cbf772 | ||
|
|
0db564d261 | ||
|
|
427b7ea104 | ||
|
|
7bf6878b4b | ||
|
|
0bc1a115ff | ||
|
|
334bcfa5ef | ||
|
|
106dea4559 | ||
|
|
3ffdcf8114 | ||
|
|
4021a7eb28 | ||
|
|
0690fda0f1 | ||
|
|
d0cc48c6d3 | ||
|
|
d66e3c949e | ||
|
|
0c65a386b5 | ||
|
|
29f5430881 | ||
|
|
101f285bcd | ||
|
|
286dc6322b | ||
|
|
c9346cd40d | ||
|
|
1c5683ec78 | ||
|
|
9bee155d59 | ||
|
|
f34b05f4de | ||
|
|
97ce25f4ce | ||
|
|
9e14c14a26 | ||
|
|
c507880d8f | ||
|
|
3f8328bbf8 | ||
|
|
c10a5f9ef6 | ||
|
|
3c141de695 | ||
|
|
da2b9455f2 | ||
|
|
cb622488c0 | ||
|
|
32f971fec6 | ||
|
|
8dea76baa4 | ||
|
|
299fbe04c4 | ||
|
|
57fc1df7d7 | ||
|
|
eaba76f9e6 | ||
|
|
cb702cc238 | ||
|
|
b69b4a0a4a | ||
|
|
50d6072a73 | ||
|
|
d24cd50e61 | ||
|
|
9f5875158c | ||
|
|
c3dc33c861 | ||
|
|
6be8f2124c | ||
|
|
edfed06001 | ||
|
|
1c646dba91 | ||
|
|
6781068058 | ||
|
|
cfe57c1dfe | ||
|
|
c34d09a971 | ||
|
|
ebd90c4742 | ||
|
|
ba52d34828 | ||
|
|
1b6312afb3 | ||
|
|
bf32dc2e72 | ||
|
|
a15d65f7a2 | ||
|
|
2de8488855 | ||
|
|
129aa4bfaa | ||
|
|
b1d70db63b | ||
|
|
f03c3aafe4 | ||
|
|
51a9f106d1 | ||
|
|
bfc048e3fe | ||
|
|
83418644f7 | ||
|
|
dbc9dd5bfc | ||
|
|
45ab15d4b5 |
@@ -93,6 +93,7 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -67,6 +67,7 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1774439233.809",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"wasmi/1.0.9#1fecdab9b90c96698eb35ea99ca4f5cb%1772227278.324",
|
||||
"sqlite3/3.51.0#66aa11eabd0e34954c5c1c061ad44abe%1774467355.988",
|
||||
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
@@ -11,7 +12,7 @@
|
||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
|
||||
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
|
||||
"openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139",
|
||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384",
|
||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
||||
|
||||
@@ -35,6 +35,7 @@ class Xrpl(ConanFile):
|
||||
"openssl/3.6.1",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.9",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -220,6 +221,7 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -7,6 +7,8 @@ ignorePaths:
|
||||
- cmake/**
|
||||
- LICENSE.md
|
||||
- .clang-tidy
|
||||
- src/test/app/wasm_fixtures/**/*.wat
|
||||
- src/test/app/wasm_fixtures/*.c
|
||||
language: en
|
||||
allowCompoundWords: true # TODO (#6334)
|
||||
ignoreRandomStrings: true
|
||||
@@ -44,6 +46,10 @@ suggestWords:
|
||||
words:
|
||||
- abempty
|
||||
- AMMID
|
||||
- AMMMPT
|
||||
- AMMMPToken
|
||||
- AMMMPTokens
|
||||
- AMMXRP
|
||||
- amt
|
||||
- amts
|
||||
- asnode
|
||||
@@ -60,6 +66,7 @@ words:
|
||||
- Britto
|
||||
- Btrfs
|
||||
- canonicality
|
||||
- cdylib
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
@@ -96,6 +103,7 @@ words:
|
||||
- distro
|
||||
- doxyfile
|
||||
- dxrpl
|
||||
- enabled
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
@@ -148,6 +156,8 @@ words:
|
||||
- ltype
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
- Merkle
|
||||
- Metafuncton
|
||||
- misprediction
|
||||
@@ -157,6 +167,7 @@ words:
|
||||
- mptid
|
||||
- mptissuance
|
||||
- mptissuanceid
|
||||
- mptissue
|
||||
- mptoken
|
||||
- mptokenid
|
||||
- mptokenissuance
|
||||
@@ -246,6 +257,7 @@ words:
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
- stnum
|
||||
- stnumber
|
||||
- stobj
|
||||
- stobject
|
||||
- stpath
|
||||
|
||||
@@ -381,6 +381,9 @@ public:
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
friend Number
|
||||
log10(Number const&, int);
|
||||
|
||||
// Thread local rounding control. Default is to_nearest
|
||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||
static rounding_mode
|
||||
@@ -729,6 +732,10 @@ abs(Number x) noexcept
|
||||
Number
|
||||
power(Number const& f, unsigned n);
|
||||
|
||||
// logarithm with base 10
|
||||
Number
|
||||
log10(Number const& value, int iterations = 50);
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the root of the polynomial g(x) = x^d - f
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -36,7 +36,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 +124,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 +196,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 +263,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 +332,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 +361,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 +446,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 +455,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 +509,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 +523,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 +617,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);
|
||||
|
||||
@@ -21,9 +21,10 @@ std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
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
|
||||
@@ -34,9 +35,10 @@ Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
|
||||
ammHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
std::optional<Issue> const& optIssue1,
|
||||
std::optional<Issue> const& optIssue2,
|
||||
std::optional<Asset> const& optAsset1,
|
||||
std::optional<Asset> const& optAsset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get the balance of LP tokens.
|
||||
@@ -44,8 +46,8 @@ ammHolds(
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Currency const& cur1,
|
||||
Currency const& cur2,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j);
|
||||
@@ -67,13 +69,13 @@ 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);
|
||||
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 tecIMPCOMPLETE is returned.
|
||||
*/
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& view, Issue const& asset, Issue const& asset2, beast::Journal j);
|
||||
deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::Journal j);
|
||||
|
||||
/** Initialize Auction and Voting slots and set the trading/discounted fee.
|
||||
*/
|
||||
@@ -82,7 +84,7 @@ initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Issue const& lptIssue,
|
||||
Asset const& lptAsset,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Return true if the Liquidity Provider is the only AMM provider, false
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
{
|
||||
lowest_layer().shutdown(plain_socket::shutdown_both);
|
||||
}
|
||||
catch (boost::system::system_error const& e)
|
||||
catch (boost::system::system_error& e)
|
||||
{
|
||||
ec = e.code();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -187,7 +187,8 @@ enum LedgerEntryType : std::uint16_t {
|
||||
\
|
||||
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
|
||||
@@ -251,6 +251,12 @@ std::uint8_t constexpr vaultMaximumIOUScale = 18;
|
||||
* another vault; counted from 0 */
|
||||
std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||
|
||||
/** Maximum length of a Data field in Escrow object that can be updated by WASM code. */
|
||||
std::size_t constexpr maxWasmDataLength = 4 * 1024; // 4KB
|
||||
|
||||
/** Maximum length of parameters passed from WASM code to host functions. */
|
||||
std::size_t constexpr maxWasmParamLength = 1024; // 1KB
|
||||
|
||||
/** A ledger index. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
|
||||
@@ -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,9 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_MPT,
|
||||
temBAD_WASM,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -208,6 +211,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
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
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},
|
||||
|
||||
@@ -159,6 +159,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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -401,6 +401,8 @@ JSS(min_ledger); // in: LedgerCleaner
|
||||
JSS(minimum_fee); // out: TxQ
|
||||
JSS(minimum_level); // out: TxQ
|
||||
JSS(missingCommand); // error
|
||||
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.
|
||||
|
||||
@@ -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&
|
||||
|
||||
@@ -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,8 @@ using InvariantChecks = std::tuple<
|
||||
ValidPseudoAccounts,
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault>;
|
||||
ValidVault,
|
||||
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,32 @@ 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&);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
#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 +36,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 +47,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 +88,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
|
||||
|
||||
@@ -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>
|
||||
|
||||
558
include/xrpl/tx/wasm/HostFunc.h
Normal file
558
include/xrpl/tx/wasm/HostFunc.h
Normal file
@@ -0,0 +1,558 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/tx/wasm/ParamsHelper.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class HostFunctionError : int32_t {
|
||||
INTERNAL = -1,
|
||||
FIELD_NOT_FOUND = -2,
|
||||
BUFFER_TOO_SMALL = -3,
|
||||
NO_ARRAY = -4,
|
||||
NOT_LEAF_FIELD = -5,
|
||||
LOCATOR_MALFORMED = -6,
|
||||
SLOT_OUT_RANGE = -7,
|
||||
SLOTS_FULL = -8,
|
||||
EMPTY_SLOT = -9,
|
||||
LEDGER_OBJ_NOT_FOUND = -10,
|
||||
DECODING = -11,
|
||||
DATA_FIELD_TOO_LARGE = -12,
|
||||
POINTER_OUT_OF_BOUNDS = -13,
|
||||
NO_MEM_EXPORTED = -14,
|
||||
INVALID_PARAMS = -15,
|
||||
INVALID_ACCOUNT = -16,
|
||||
INVALID_FIELD = -17,
|
||||
INDEX_OUT_OF_BOUNDS = -18,
|
||||
FLOAT_INPUT_MALFORMED = -19,
|
||||
FLOAT_COMPUTATION_ERROR = -20,
|
||||
NO_RUNTIME = -21,
|
||||
OUT_OF_GAS = -22,
|
||||
};
|
||||
|
||||
using FloatPair = std::pair<int64_t, int32_t>;
|
||||
|
||||
inline int32_t
|
||||
HfErrorToInt(HostFunctionError e)
|
||||
{
|
||||
return static_cast<int32_t>(e);
|
||||
}
|
||||
|
||||
namespace wasm_float {
|
||||
|
||||
std::string
|
||||
floatToString(Slice const& data);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromIntImpl(int64_t x, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromUintImpl(uint64_t x, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromSTAmountImpl(STAmount const& x, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromSTNumberImpl(STNumber const& x, int32_t mode);
|
||||
|
||||
Expected<int64_t, HostFunctionError>
|
||||
floatToIntImpl(Slice const& x, int32_t mode);
|
||||
|
||||
Expected<FloatPair, HostFunctionError>
|
||||
floatToMantissaAndExponentImpl(Slice const& x);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatNegateImpl(Slice const& x);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAbsImpl(Slice const& x);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode);
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
floatCompareImpl(Slice const& x, Slice const& y);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAddImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatDivideImpl(Slice const& x, Slice const& y, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatRootImpl(Slice const& x, int32_t n, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatPowerImpl(Slice const& x, int32_t n, int32_t mode);
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatLogImpl(Slice const& x, int32_t mode);
|
||||
|
||||
} // namespace wasm_float
|
||||
|
||||
// Intended to work only through wasm runtime. Don't call them directly, except with unit tests
|
||||
struct HostFunctions
|
||||
{
|
||||
beast::Journal j_;
|
||||
|
||||
HostFunctions(beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) : j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
virtual void
|
||||
setRT(void*)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void*
|
||||
getRT() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
beast::Journal
|
||||
getJournal() const
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
checkSelf() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<uint32_t, HostFunctionError>
|
||||
getBaseFee() const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(uint256 const& amendmentId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(std::string_view const& amendmentName) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ammKeylet(Asset const& issue1, Asset const& issue2) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
checkKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
|
||||
const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
didKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
delegateKeylet(AccountID const& account, AccountID const& authorize) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
mptokenKeylet(MPTID const& mptid, AccountID const& holder) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
nftOfferKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
offerKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
signersKeylet(AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
ticketKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
vaultKeylet(AccountID const& account, std::uint32_t seq) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
getNFTIssuer(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTTaxon(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTFlags(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
getNFTTransferFee(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTSerial(uint256 const& nftId) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAccount(std::string_view const& msg, AccountID const& account) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceFloat(std::string_view const& msg, Slice const& data) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
traceAmount(std::string_view const& msg, STAmount const& amount) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromInt(int64_t x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromUint(uint64_t x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromSTAmount(STAmount const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatFromSTNumber(STNumber const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int64_t, HostFunctionError>
|
||||
floatToInt(Slice const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<FloatPair, HostFunctionError>
|
||||
floatToMantissaAndExponent(Slice const& x) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatNegate(Slice const& x) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatAbs(Slice const& x) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<int32_t, HostFunctionError>
|
||||
floatCompare(Slice const& x, Slice const& y) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatAdd(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatDivide(Slice const& x, Slice const& y, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatRoot(Slice const& x, int32_t n, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatPower(Slice const& x, int32_t n, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual Expected<Bytes, HostFunctionError>
|
||||
floatLog(Slice const& x, int32_t mode) const
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
|
||||
virtual ~HostFunctions() = default;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
305
include/xrpl/tx/wasm/HostFuncImpl.h
Normal file
305
include/xrpl/tx/wasm/HostFuncImpl.h
Normal file
@@ -0,0 +1,305 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/wasm/HostFunc.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Intended to work only through wasm runtime. Don't call them directly, except with unit tests
|
||||
class WasmHostFunctionsImpl : public HostFunctions
|
||||
{
|
||||
ApplyContext& ctx_;
|
||||
|
||||
Keylet leKey_;
|
||||
mutable std::optional<std::shared_ptr<SLE const>> currentLedgerObj_;
|
||||
|
||||
static int constexpr MAX_CACHE = 256;
|
||||
std::array<std::shared_ptr<SLE const>, MAX_CACHE> cache_;
|
||||
|
||||
std::optional<Bytes> data_;
|
||||
|
||||
void* rt_ = nullptr;
|
||||
|
||||
Expected<std::shared_ptr<SLE const>, HostFunctionError>
|
||||
getCurrentLedgerObj() const
|
||||
{
|
||||
if (!currentLedgerObj_)
|
||||
currentLedgerObj_ = ctx_.view().read(leKey_);
|
||||
if (*currentLedgerObj_)
|
||||
return *currentLedgerObj_;
|
||||
return Unexpected(HostFunctionError::LEDGER_OBJ_NOT_FOUND);
|
||||
}
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
normalizeCacheIndex(int32_t cacheIdx) const
|
||||
{
|
||||
--cacheIdx;
|
||||
if (cacheIdx < 0 || cacheIdx >= MAX_CACHE)
|
||||
return Unexpected(HostFunctionError::SLOT_OUT_RANGE);
|
||||
if (!cache_[cacheIdx])
|
||||
return Unexpected(HostFunctionError::EMPTY_SLOT);
|
||||
return cacheIdx;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
log(std::string_view const& msg, F&& dataFn) const
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
auto& j = std::cerr;
|
||||
#else
|
||||
if (!getJournal().active(beast::severities::kTrace))
|
||||
return;
|
||||
auto j = getJournal().trace();
|
||||
#endif
|
||||
j << "WasmTrace[" << to_short_string(leKey_.key) << "]: " << msg << " " << dataFn();
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
j << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
WasmHostFunctionsImpl(ApplyContext& ct, Keylet const& leKey)
|
||||
: HostFunctions(ct.journal), ctx_(ct), leKey_(leKey)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
setRT(void* rt) override
|
||||
{
|
||||
rt_ = rt;
|
||||
}
|
||||
|
||||
virtual void*
|
||||
getRT() const override
|
||||
{
|
||||
return rt_;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
checkSelf() const override
|
||||
{
|
||||
return !currentLedgerObj_ && !data_ &&
|
||||
std::ranges::find_if(cache_, [](auto& p) { return !!p; }) == cache_.end();
|
||||
}
|
||||
|
||||
std::optional<Bytes> const&
|
||||
getData() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getLedgerSqn() const override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getParentLedgerTime() const override;
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
getParentLedgerHash() const override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getBaseFee() const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(uint256 const& amendmentId) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
isAmendmentEnabled(std::string_view const& amendmentName) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxField(SField const& fname) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjField(SField const& fname) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjField(int32_t cacheIdx, SField const& fname) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getTxNestedField(Slice const& locator) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getCurrentLedgerObjNestedField(Slice const& locator) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxArrayLen(SField const& fname) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjArrayLen(SField const& fname) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getTxNestedArrayLen(Slice const& locator) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getCurrentLedgerObjNestedArrayLen(Slice const& locator) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
updateData(Slice const& data) override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey)
|
||||
const override;
|
||||
|
||||
Expected<Hash, HostFunctionError>
|
||||
computeSha512HalfHash(Slice const& data) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
accountKeylet(AccountID const& account) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
ammKeylet(Asset const& issue1, Asset const& issue2) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
checkKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
|
||||
const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
didKeylet(AccountID const& account) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
delegateKeylet(AccountID const& account, AccountID const& authorize) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
depositPreauthKeylet(AccountID const& account, AccountID const& authorize) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
escrowKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
lineKeylet(AccountID const& account1, AccountID const& account2, Currency const& currency)
|
||||
const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
mptIssuanceKeylet(AccountID const& issuer, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
mptokenKeylet(MPTID const& mptid, AccountID const& holder) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
nftOfferKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
offerKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
oracleKeylet(AccountID const& account, std::uint32_t docId) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
paychanKeylet(AccountID const& account, AccountID const& destination, std::uint32_t seq)
|
||||
const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
permissionedDomainKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
signersKeylet(AccountID const& account) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
ticketKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
vaultKeylet(AccountID const& account, std::uint32_t seq) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFT(AccountID const& account, uint256 const& nftId) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
getNFTIssuer(uint256 const& nftId) const override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTTaxon(uint256 const& nftId) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getNFTFlags(uint256 const& nftId) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
getNFTTransferFee(uint256 const& nftId) const override;
|
||||
|
||||
Expected<std::uint32_t, HostFunctionError>
|
||||
getNFTSerial(uint256 const& nftId) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
trace(std::string_view const& msg, Slice const& data, bool asHex) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceNum(std::string_view const& msg, int64_t data) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceAccount(std::string_view const& msg, AccountID const& account) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceFloat(std::string_view const& msg, Slice const& data) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
traceAmount(std::string_view const& msg, STAmount const& amount) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromInt(int64_t x, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromUint(uint64_t x, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromSTAmount(STAmount const& x, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatFromSTNumber(STNumber const& x, int32_t mode) const override;
|
||||
|
||||
Expected<int64_t, HostFunctionError>
|
||||
floatToInt(Slice const& x, int32_t mode) const override;
|
||||
|
||||
Expected<FloatPair, HostFunctionError>
|
||||
floatToMantissaAndExponent(Slice const& x) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatNegate(Slice const& x) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAbs(Slice const& x) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override;
|
||||
|
||||
Expected<int32_t, HostFunctionError>
|
||||
floatCompare(Slice const& x, Slice const& y) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatAdd(Slice const& x, Slice const& y, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatDivide(Slice const& x, Slice const& y, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatRoot(Slice const& x, int32_t n, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatPower(Slice const& x, int32_t n, int32_t mode) const override;
|
||||
|
||||
Expected<Bytes, HostFunctionError>
|
||||
floatLog(Slice const& x, int32_t mode) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
332
include/xrpl/tx/wasm/HostFuncWrapper.h
Normal file
332
include/xrpl/tx/wasm/HostFuncWrapper.h
Normal file
@@ -0,0 +1,332 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/wasm/ParamsHelper.h>
|
||||
|
||||
#include <wasm.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using getLedgerSqn_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerSqn_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getParentLedgerTime_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getParentLedgerTime_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getParentLedgerHash_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getParentLedgerHash_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getBaseFee_proto = int32_t(uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getBaseFee_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using isAmendmentEnabled_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
isAmendmentEnabled_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using cacheLedgerObj_proto = int32_t(uint8_t const*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
cacheLedgerObj_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjField_proto = int32_t(int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjField_proto = int32_t(int32_t, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjNestedField_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjNestedField_wrap(
|
||||
void* env,
|
||||
wasm_val_vec_t const* params,
|
||||
wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjNestedField_proto = int32_t(int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjNestedField_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxArrayLen_proto = int32_t(int32_t);
|
||||
wasm_trap_t*
|
||||
getTxArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjArrayLen_proto = int32_t(int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjArrayLen_proto = int32_t(int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getTxNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getTxNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getCurrentLedgerObjNestedArrayLen_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getCurrentLedgerObjNestedArrayLen_wrap(
|
||||
void* env,
|
||||
wasm_val_vec_t const* params,
|
||||
wasm_val_vec_t* results);
|
||||
|
||||
using getLedgerObjNestedArrayLen_proto = int32_t(int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getLedgerObjNestedArrayLen_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using updateData_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
updateData_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using checkSignature_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
checkSignature_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using computeSha512HalfHash_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
computeSha512HalfHash_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using accountKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
accountKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using ammKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
ammKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using checkKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
checkKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using credentialKeylet_proto = int32_t(
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t*,
|
||||
int32_t);
|
||||
wasm_trap_t*
|
||||
credentialKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using delegateKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
delegateKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using depositPreauthKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
depositPreauthKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using didKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
didKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using escrowKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
escrowKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using lineKeylet_proto = int32_t(
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t*,
|
||||
int32_t);
|
||||
wasm_trap_t*
|
||||
lineKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using mptIssuanceKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
mptIssuanceKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using mptokenKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
mptokenKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using nftOfferKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
nftOfferKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using offerKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
offerKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using oracleKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
oracleKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using paychanKeylet_proto = int32_t(
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t const*,
|
||||
int32_t,
|
||||
uint8_t*,
|
||||
int32_t);
|
||||
wasm_trap_t*
|
||||
paychanKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using permissionedDomainKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
permissionedDomainKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using signersKeylet_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
signersKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using ticketKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
ticketKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using vaultKeylet_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
vaultKeylet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFT_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTIssuer_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTIssuer_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTTaxon_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTTaxon_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTFlags_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTFlags_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTTransferFee_proto = int32_t(uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTTransferFee_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using getNFTSerial_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
getNFTSerial_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using trace_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
trace_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceNum_proto = int32_t(uint8_t const*, int32_t, int64_t);
|
||||
wasm_trap_t*
|
||||
traceNum_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceAccount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceAccount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceFloat_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceFloat_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using traceAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
traceAmount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromInt_proto = int32_t(int64_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromUint_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromUint_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromSTAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromSTAmount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatFromSTNumber_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatFromSTNumber_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatToInt_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatToInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatToMantissaAndExponent_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
floatToMantissaAndExponent_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatNegate_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
floatNegate_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatAbs_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t);
|
||||
wasm_trap_t*
|
||||
floatAbs_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatSet_proto = int32_t(int32_t, int64_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatSet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatCompare_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
|
||||
wasm_trap_t*
|
||||
floatCompare_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatAdd_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatAdd_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatSubtract_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatSubtract_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatMultiply_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatMultiply_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatDivide_proto =
|
||||
int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatDivide_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatRoot_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatRoot_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatPower_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatPower_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
using floatLog_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
|
||||
wasm_trap_t*
|
||||
floatLog_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
|
||||
} // namespace xrpl
|
||||
245
include/xrpl/tx/wasm/ParamsHelper.h
Normal file
245
include/xrpl/tx/wasm/ParamsHelper.h
Normal file
@@ -0,0 +1,245 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <boost/function_types/function_arity.hpp>
|
||||
#include <boost/function_types/parameter_types.hpp>
|
||||
#include <boost/function_types/result_type.hpp>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace bft = boost::function_types;
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
using Bytes = std::vector<std::uint8_t>;
|
||||
using Hash = xrpl::uint256;
|
||||
|
||||
struct wmem
|
||||
{
|
||||
std::uint8_t* p = nullptr;
|
||||
std::size_t s = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct WasmResult
|
||||
{
|
||||
T result;
|
||||
int64_t cost;
|
||||
};
|
||||
typedef WasmResult<int32_t> EscrowResult;
|
||||
|
||||
struct WasmRuntimeWrapper
|
||||
{
|
||||
virtual ~WasmRuntimeWrapper() = default;
|
||||
|
||||
virtual wmem
|
||||
getMem() = 0;
|
||||
|
||||
virtual std::int64_t
|
||||
getGas() = 0;
|
||||
|
||||
virtual std::int64_t
|
||||
setGas(std::int64_t gas) = 0;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum WasmTypes { WT_I32, WT_I64, WT_U8V };
|
||||
|
||||
struct WasmImportFunc
|
||||
{
|
||||
std::string name;
|
||||
std::optional<WasmTypes> result;
|
||||
std::vector<WasmTypes> params;
|
||||
// void* udata = nullptr;
|
||||
// wasm_func_callback_with_env_t
|
||||
void* wrap = nullptr;
|
||||
uint32_t gas = 0;
|
||||
};
|
||||
|
||||
typedef std::pair<void*, WasmImportFunc> WasmUserData;
|
||||
typedef std::vector<WasmUserData> ImportVec;
|
||||
|
||||
#define WASM_IMPORT_FUNC(v, f, ...) \
|
||||
WasmImpFunc<f##_proto>(v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
|
||||
|
||||
#define WASM_IMPORT_FUNC2(v, f, n, ...) \
|
||||
WasmImpFunc<f##_proto>(v, n, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
|
||||
|
||||
template <int N, int C, typename mpl>
|
||||
void
|
||||
WasmImpArgs(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (N < C)
|
||||
{
|
||||
using at = typename boost::mpl::at_c<mpl, N>::type;
|
||||
if constexpr (std::is_pointer_v<at>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int32_t>)
|
||||
e.params.push_back(WT_I32);
|
||||
else if constexpr (std::is_same_v<at, std::int64_t>)
|
||||
e.params.push_back(WT_I64);
|
||||
else
|
||||
static_assert(std::is_pointer_v<at>, "Unsupported argument type");
|
||||
|
||||
return WasmImpArgs<N + 1, C, mpl>(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <typename rt>
|
||||
void
|
||||
WasmImpRet(WasmImportFunc& e)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<rt>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int32_t>)
|
||||
e.result = WT_I32;
|
||||
else if constexpr (std::is_same_v<rt, std::int64_t>)
|
||||
e.result = WT_I64;
|
||||
else if constexpr (std::is_void_v<rt>)
|
||||
e.result.reset();
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \
|
||||
((defined(__clang_major__)) && (__clang_major__ >= 18))
|
||||
else
|
||||
static_assert(false, "Unsupported return type");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFuncHelper(WasmImportFunc& e)
|
||||
{
|
||||
using rt = typename bft::result_type<F>::type;
|
||||
using pt = typename bft::parameter_types<F>::type;
|
||||
// typename boost::mpl::at_c<mpl, N>::type
|
||||
|
||||
WasmImpRet<rt>(e);
|
||||
WasmImpArgs<0, bft::function_arity<F>::value, pt>(e);
|
||||
// WasmImpWrap(e, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void
|
||||
WasmImpFunc(
|
||||
ImportVec& v,
|
||||
std::string_view imp_name,
|
||||
void* f_wrap,
|
||||
void* data = nullptr,
|
||||
uint32_t gas = 0)
|
||||
{
|
||||
WasmImportFunc e;
|
||||
e.name = imp_name;
|
||||
e.wrap = f_wrap;
|
||||
e.gas = gas;
|
||||
WasmImpFuncHelper<F>(e);
|
||||
v.push_back(std::make_pair(data, std::move(e)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct WasmParamVec
|
||||
{
|
||||
std::uint8_t const* d = nullptr;
|
||||
std::int32_t sz = 0;
|
||||
};
|
||||
|
||||
struct WasmParam
|
||||
{
|
||||
WasmTypes type = WT_I32;
|
||||
union
|
||||
{
|
||||
std::int32_t i32;
|
||||
std::int64_t i64 = 0;
|
||||
float f32;
|
||||
double f64;
|
||||
WasmParamVec u8v;
|
||||
} of;
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::int32_t p, Types&&... args)
|
||||
{
|
||||
v.push_back({.type = WT_I32, .of = {.i32 = p}});
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v, std::int64_t p, Types&&... args)
|
||||
{
|
||||
v.push_back({.type = WT_I64, .of = {.i64 = p}});
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
}
|
||||
|
||||
// We are not supporting float/double for now
|
||||
// Leaving this code here so that it is easier to add later if needed
|
||||
// template <class... Types>
|
||||
// inline void
|
||||
// wasmParamsHlp(std::vector<WasmParam>& v, float p, Types&&... args)
|
||||
// {
|
||||
// v.push_back({.type = WT_F32, .of = {.f32 = p}});
|
||||
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
// }
|
||||
|
||||
// template <class... Types>
|
||||
// inline void
|
||||
// wasmParamsHlp(std::vector<WasmParam>& v, double p, Types&&... args)
|
||||
// {
|
||||
// v.push_back({.type = WT_F64, .of = {.f64 = p}});
|
||||
// wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
// }
|
||||
|
||||
inline void
|
||||
wasmParamsHlp(std::vector<WasmParam>& v)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
inline std::vector<WasmParam>
|
||||
wasmParams(Types&&... args)
|
||||
{
|
||||
std::vector<WasmParam> v;
|
||||
v.reserve(sizeof...(args));
|
||||
wasmParamsHlp(v, std::forward<Types>(args)...);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, size_t size = sizeof(T)>
|
||||
inline constexpr T
|
||||
adjustWasmEndianessHlp(T x)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "Only integral types");
|
||||
if constexpr (size > 1)
|
||||
{
|
||||
using U = std::make_unsigned<T>::type;
|
||||
U u = static_cast<U>(x);
|
||||
U const low = (u & 0xFF) << ((size - 1) << 3);
|
||||
u = adjustWasmEndianessHlp<U, size - 1>(u >> 8);
|
||||
return static_cast<T>(low | u);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T, size_t size = sizeof(T)>
|
||||
inline constexpr T
|
||||
adjustWasmEndianess(T x)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
static_assert(std::is_integral<T>::value, "Only integral types");
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
{
|
||||
return adjustWasmEndianessHlp(x);
|
||||
}
|
||||
return x;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
189
include/xrpl/tx/wasm/README.md
Normal file
189
include/xrpl/tx/wasm/README.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# WASM Module for Programmable Escrows
|
||||
|
||||
This module provides WebAssembly (WASM) execution capabilities for programmable
|
||||
escrows on the XRP Ledger. When an escrow is finished, the WASM code runs to
|
||||
determine whether the escrow conditions are met, enabling custom programmable
|
||||
logic for escrow release conditions.
|
||||
|
||||
For the full specification, see
|
||||
[XLS-0102: WASM VM](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html).
|
||||
|
||||
## Architecture
|
||||
|
||||
The module follows a layered architecture:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ WasmEngine (WasmVM.h) │
|
||||
│ runEscrowWasm(), preflightEscrowWasm() │
|
||||
│ Host function registration │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ WasmiEngine (WasmiVM.h) │
|
||||
│ Low-level wasmi interpreter integration │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ HostFuncWrapper │ HostFuncImpl │
|
||||
│ C-style WASM bridges │ C++ implementations │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ HostFunc (Interface) │
|
||||
│ Abstract base class for host functions │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
- **`WasmVM.h` / `detail/WasmVM.cpp`** - High-level facade providing:
|
||||
- `WasmEngine` singleton that wraps the underlying WASM interpreter
|
||||
- `runEscrowWasm()` - Execute WASM code for escrow finish
|
||||
- `preflightEscrowWasm()` - Validate WASM code during preflight
|
||||
- `createWasmImport()` - Register all host functions
|
||||
|
||||
- **`WasmiVM.h` / `detail/WasmiVM.cpp`** - Low-level integration with the
|
||||
[wasmi](https://github.com/wasmi-labs/wasmi) WebAssembly interpreter:
|
||||
- `WasmiEngine` - Manages WASM modules, instances, and execution
|
||||
- Memory management and gas metering
|
||||
- Function invocation and result handling
|
||||
|
||||
- **`HostFunc.h`** - Abstract `HostFunctions` base class defining the interface
|
||||
for all callable host functions. Each method returns
|
||||
`Expected<T, HostFunctionError>`.
|
||||
|
||||
- **`HostFuncImpl.h` / `detail/HostFuncImpl*.cpp`** - Concrete
|
||||
`WasmHostFunctionsImpl` class that implements host functions with access to
|
||||
`ApplyContext` for ledger state queries. Implementation split across files:
|
||||
- `HostFuncImpl.cpp` - Core utilities (updateData, checkSignature, etc.)
|
||||
- `HostFuncImplFloat.cpp` - Float/number arithmetic operations
|
||||
- `HostFuncImplGetter.cpp` - Field access (transaction, ledger objects)
|
||||
- `HostFuncImplKeylet.cpp` - Keylet construction functions
|
||||
- `HostFuncImplLedgerHeader.cpp` - Ledger header info access
|
||||
- `HostFuncImplNFT.cpp` - NFT-related queries
|
||||
- `HostFuncImplTrace.cpp` - Debugging/tracing functions
|
||||
|
||||
- **`HostFuncWrapper.h` / `detail/HostFuncWrapper.cpp`** - C-style wrapper
|
||||
functions that bridge WASM calls to C++ `HostFunctions` methods. Each host
|
||||
function has:
|
||||
- A `_proto` type alias defining the function signature
|
||||
- A `_wrap` function that extracts parameters and calls the implementation
|
||||
|
||||
- **`ParamsHelper.h`** - Utilities for WASM parameter handling:
|
||||
- `WASM_IMPORT_FUNC` / `WASM_IMPORT_FUNC2` macros for registration
|
||||
- `wasmParams()` helper for building parameter vectors
|
||||
- Type conversion between WASM and C++ types
|
||||
|
||||
## Host Functions
|
||||
|
||||
Host functions allow WASM code to interact with the XRP Ledger. They are
|
||||
organized into categories:
|
||||
|
||||
- **Ledger Information** - Access ledger sequence, timestamps, hashes, fees
|
||||
- **Transaction & Ledger Object Access** - Read fields from the transaction
|
||||
and ledger objects (including the current escrow object)
|
||||
- **Keylet Construction** - Build keylets to look up various ledger object types
|
||||
- **Cryptography** - Signature verification and hashing
|
||||
- **Float Arithmetic** - Mathematical operations for amount calculations
|
||||
- **NFT Operations** - Query NFT properties
|
||||
- **Tracing/Debugging** - Log messages for debugging
|
||||
|
||||
For the complete list of available host functions, their WASM names, and gas
|
||||
costs, see the [XLS-0102 specification](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html)
|
||||
or `detail/WasmVM.cpp` where they are registered via `WASM_IMPORT_FUNC2` macros.
|
||||
For method signatures, see `HostFunc.h`.
|
||||
|
||||
## Gas Model
|
||||
|
||||
Each host function has an associated gas cost. The gas cost is specified when
|
||||
registering the function in `detail/WasmVM.cpp`:
|
||||
|
||||
```cpp
|
||||
WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60);
|
||||
// ^^ gas cost
|
||||
```
|
||||
|
||||
WASM execution is metered, and if the gas limit is exceeded, execution fails.
|
||||
|
||||
## Entry Point
|
||||
|
||||
The WASM module must export a function with the name defined by
|
||||
`ESCROW_FUNCTION_NAME` (currently `"finish"`). This function:
|
||||
|
||||
- Takes no parameters (or parameters passed via host function calls)
|
||||
- Returns an `int32_t`:
|
||||
- `1` (or positive): Escrow conditions are met, allow finish
|
||||
- `0` (or negative): Escrow conditions are not met, reject finish
|
||||
|
||||
## Adding a New Host Function
|
||||
|
||||
To add a new host function, follow these steps:
|
||||
|
||||
### 1. Add to HostFunc.h (Base Class)
|
||||
|
||||
Add a virtual method declaration with a default implementation that returns an
|
||||
error:
|
||||
|
||||
```cpp
|
||||
virtual Expected<ReturnType, HostFunctionError>
|
||||
myNewFunction(ParamType1 param1, ParamType2 param2)
|
||||
{
|
||||
return Unexpected(HostFunctionError::INTERNAL);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Add to HostFuncImpl.h (Declaration)
|
||||
|
||||
Add the method override declaration in `WasmHostFunctionsImpl`:
|
||||
|
||||
```cpp
|
||||
Expected<ReturnType, HostFunctionError>
|
||||
myNewFunction(ParamType1 param1, ParamType2 param2) override;
|
||||
```
|
||||
|
||||
### 3. Implement in detail/HostFuncImpl\*.cpp
|
||||
|
||||
Add the implementation in the appropriate file:
|
||||
|
||||
```cpp
|
||||
Expected<ReturnType, HostFunctionError>
|
||||
WasmHostFunctionsImpl::myNewFunction(ParamType1 param1, ParamType2 param2)
|
||||
{
|
||||
// Implementation using ctx (ApplyContext) for ledger access
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Add Wrapper to HostFuncWrapper.h
|
||||
|
||||
Add the prototype and wrapper declaration:
|
||||
|
||||
```cpp
|
||||
using myNewFunction_proto = int32_t(uint8_t const*, int32_t, ...);
|
||||
wasm_trap_t*
|
||||
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
|
||||
```
|
||||
|
||||
### 5. Implement Wrapper in detail/HostFuncWrapper.cpp
|
||||
|
||||
Implement the C-style wrapper that bridges WASM to C++:
|
||||
|
||||
```cpp
|
||||
wasm_trap_t*
|
||||
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
|
||||
{
|
||||
// Extract parameters from params
|
||||
// Call hfs->myNewFunction(...)
|
||||
// Set results and return
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Register in WasmVM.cpp
|
||||
|
||||
Add the function registration in `setCommonHostFunctions()` or
|
||||
`createWasmImport()`:
|
||||
|
||||
```cpp
|
||||
WASM_IMPORT_FUNC2(i, myNewFunction, "my_new_function", hfs, 100);
|
||||
// ^^ WASM name ^^ gas cost
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> New host functions MUST be amendment-gated in `WasmVM.cpp`.
|
||||
> Wrap the registration in an amendment check to ensure the function is only
|
||||
> available after the corresponding amendment is enabled on the network.
|
||||
90
include/xrpl/tx/wasm/WasmVM.h
Normal file
90
include/xrpl/tx/wasm/WasmVM.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/wasm/HostFunc.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
static std::string_view const W_ENV = "env";
|
||||
static std::string_view const W_HOST_LIB = "host_lib";
|
||||
static std::string_view const W_MEM = "memory";
|
||||
static std::string_view const W_STORE = "store";
|
||||
static std::string_view const W_LOAD = "load";
|
||||
static std::string_view const W_SIZE = "size";
|
||||
static std::string_view const W_ALLOC = "allocate";
|
||||
static std::string_view const W_DEALLOC = "deallocate";
|
||||
static std::string_view const W_PROC_EXIT = "proc_exit";
|
||||
|
||||
static std::string_view const ESCROW_FUNCTION_NAME = "finish";
|
||||
|
||||
uint32_t const MAX_PAGES = 128; // 8MB = 64KB*128
|
||||
|
||||
class WasmiEngine;
|
||||
|
||||
class WasmEngine
|
||||
{
|
||||
std::unique_ptr<WasmiEngine> const impl_;
|
||||
|
||||
WasmEngine();
|
||||
|
||||
WasmEngine(WasmEngine const&) = delete;
|
||||
WasmEngine(WasmEngine&&) = delete;
|
||||
WasmEngine&
|
||||
operator=(WasmEngine const&) = delete;
|
||||
WasmEngine&
|
||||
operator=(WasmEngine&&) = delete;
|
||||
|
||||
public:
|
||||
~WasmEngine() = default;
|
||||
|
||||
static WasmEngine&
|
||||
instance();
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
run(Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gasLimit,
|
||||
std::string_view funcName = {},
|
||||
std::vector<WasmParam> const& params = {},
|
||||
ImportVec const& imports = {},
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
NotTEC
|
||||
check(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmParam> const& params = {},
|
||||
ImportVec const& imports = {},
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
// Host functions helper functionality
|
||||
void*
|
||||
newTrap(std::string const& txt = std::string());
|
||||
|
||||
beast::Journal
|
||||
getJournal() const;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ImportVec
|
||||
createWasmImport(HostFunctions& hfs);
|
||||
|
||||
Expected<EscrowResult, TER>
|
||||
runEscrowWasm(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gasLimit,
|
||||
std::string_view funcName = ESCROW_FUNCTION_NAME,
|
||||
std::vector<WasmParam> const& params = {});
|
||||
|
||||
NotTEC
|
||||
preflightEscrowWasm(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
std::string_view funcName = ESCROW_FUNCTION_NAME,
|
||||
std::vector<WasmParam> const& params = {});
|
||||
|
||||
} // namespace xrpl
|
||||
363
include/xrpl/tx/wasm/WasmiVM.h
Normal file
363
include/xrpl/tx/wasm/WasmiVM.h
Normal file
@@ -0,0 +1,363 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/wasm/WasmVM.h>
|
||||
|
||||
#include <wasm.h>
|
||||
#include <wasmi.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class T, void (*Create)(T*, size_t), void (*Destroy)(T*)>
|
||||
class WasmVec
|
||||
{
|
||||
using TD = std::remove_pointer_t<decltype(T::data)>;
|
||||
T vec_;
|
||||
|
||||
public:
|
||||
WasmVec(size_t s = 0) : vec_ WASM_EMPTY_VEC
|
||||
{
|
||||
if (s > 0)
|
||||
Create(&vec_, s); // zeroes memory
|
||||
}
|
||||
|
||||
~WasmVec()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
WasmVec(WasmVec const&) = delete;
|
||||
WasmVec&
|
||||
operator=(WasmVec const&) = delete;
|
||||
|
||||
WasmVec(WasmVec&& other) noexcept : vec_ WASM_EMPTY_VEC
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
WasmVec&
|
||||
operator=(WasmVec&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
clear();
|
||||
vec_ = other.vec_;
|
||||
other.vec_ = WASM_EMPTY_VEC;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
Destroy(&vec_); // call destructor for every elements too
|
||||
vec_ = WASM_EMPTY_VEC;
|
||||
}
|
||||
|
||||
T
|
||||
release()
|
||||
{
|
||||
T result = vec_;
|
||||
vec_ = WASM_EMPTY_VEC;
|
||||
return result;
|
||||
}
|
||||
|
||||
T*
|
||||
get()
|
||||
{
|
||||
return &vec_;
|
||||
}
|
||||
|
||||
T const*
|
||||
get() const
|
||||
{
|
||||
return &vec_;
|
||||
}
|
||||
|
||||
TD&
|
||||
operator[](size_t i)
|
||||
{
|
||||
if (i >= vec_.size)
|
||||
Throw<std::runtime_error>("Out of bound");
|
||||
return vec_.data[i];
|
||||
}
|
||||
|
||||
TD const&
|
||||
operator[](size_t i) const
|
||||
{
|
||||
if (i >= vec_.size)
|
||||
Throw<std::runtime_error>("Out of bound");
|
||||
return vec_.data[i];
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
return vec_.size;
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return vec_.size == 0u;
|
||||
}
|
||||
};
|
||||
|
||||
using WasmValtypeVec =
|
||||
WasmVec<wasm_valtype_vec_t, &wasm_valtype_vec_new_uninitialized, &wasm_valtype_vec_delete>;
|
||||
using WasmValVec = WasmVec<wasm_val_vec_t, &wasm_val_vec_new_uninitialized, &wasm_val_vec_delete>;
|
||||
using WasmExternVec =
|
||||
WasmVec<wasm_extern_vec_t, &wasm_extern_vec_new_uninitialized, &wasm_extern_vec_delete>;
|
||||
using WasmExporttypeVec = WasmVec<
|
||||
wasm_exporttype_vec_t,
|
||||
&wasm_exporttype_vec_new_uninitialized,
|
||||
&wasm_exporttype_vec_delete>;
|
||||
using WasmImporttypeVec = WasmVec<
|
||||
wasm_importtype_vec_t,
|
||||
&wasm_importtype_vec_new_uninitialized,
|
||||
&wasm_importtype_vec_delete>;
|
||||
|
||||
struct WasmiResult
|
||||
{
|
||||
WasmValVec r;
|
||||
bool f{false}; // failure flag
|
||||
|
||||
WasmiResult(unsigned N = 0) : r(N)
|
||||
{
|
||||
}
|
||||
|
||||
~WasmiResult() = default;
|
||||
WasmiResult(WasmiResult&& o) = default;
|
||||
WasmiResult&
|
||||
operator=(WasmiResult&& o) = default;
|
||||
};
|
||||
|
||||
using ModulePtr = std::unique_ptr<wasm_module_t, decltype(&wasm_module_delete)>;
|
||||
using InstancePtr = std::unique_ptr<wasm_instance_t, decltype(&wasm_instance_delete)>;
|
||||
using EnginePtr = std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>;
|
||||
using StorePtr = std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)>;
|
||||
|
||||
using FuncInfo = std::pair<wasm_func_t const*, wasm_functype_t const*>;
|
||||
|
||||
struct InstanceWrapper
|
||||
{
|
||||
wasm_store_t* store_ = nullptr;
|
||||
WasmExternVec exports_;
|
||||
mutable int memIdx_ = -1;
|
||||
InstancePtr instance_;
|
||||
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||
|
||||
private:
|
||||
static InstancePtr
|
||||
init(
|
||||
StorePtr& s,
|
||||
ModulePtr& m,
|
||||
WasmExternVec& expt,
|
||||
WasmExternVec const& imports,
|
||||
beast::Journal j);
|
||||
|
||||
public:
|
||||
InstanceWrapper();
|
||||
|
||||
InstanceWrapper(InstanceWrapper&& o);
|
||||
|
||||
InstanceWrapper&
|
||||
operator=(InstanceWrapper&& o);
|
||||
|
||||
InstanceWrapper(StorePtr& s, ModulePtr& m, WasmExternVec const& imports, beast::Journal j);
|
||||
|
||||
~InstanceWrapper() = default;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
FuncInfo
|
||||
getFunc(std::string_view funcName, WasmExporttypeVec const& exportTypes) const;
|
||||
|
||||
wmem
|
||||
getMem() const;
|
||||
|
||||
std::int64_t
|
||||
getGas() const;
|
||||
|
||||
std::int64_t
|
||||
setGas(std::int64_t) const;
|
||||
};
|
||||
|
||||
struct ModuleWrapper
|
||||
{
|
||||
ModulePtr module_;
|
||||
InstanceWrapper instanceWrap_;
|
||||
WasmExporttypeVec exportTypes_;
|
||||
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||
|
||||
private:
|
||||
static ModulePtr
|
||||
init(StorePtr& s, Bytes const& wasmBin, beast::Journal j);
|
||||
|
||||
public:
|
||||
ModuleWrapper();
|
||||
ModuleWrapper(ModuleWrapper&& o);
|
||||
ModuleWrapper&
|
||||
operator=(ModuleWrapper&& o);
|
||||
ModuleWrapper(
|
||||
StorePtr& s,
|
||||
Bytes const& wasmBin,
|
||||
bool instantiate,
|
||||
ImportVec const& imports,
|
||||
beast::Journal j);
|
||||
~ModuleWrapper() = default;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
FuncInfo
|
||||
getFunc(std::string_view funcName) const;
|
||||
|
||||
wasm_functype_t*
|
||||
getFuncType(std::string_view funcName) const;
|
||||
|
||||
wmem
|
||||
getMem() const;
|
||||
|
||||
InstanceWrapper&
|
||||
getInstance(int i = 0);
|
||||
|
||||
int
|
||||
addInstance(StorePtr& s, WasmExternVec const& imports);
|
||||
|
||||
std::int64_t
|
||||
getGas() const;
|
||||
|
||||
private:
|
||||
WasmExternVec
|
||||
buildImports(StorePtr& s, ImportVec const& imports) const;
|
||||
};
|
||||
|
||||
class WasmiEngine
|
||||
{
|
||||
EnginePtr engine_;
|
||||
StorePtr store_;
|
||||
std::unique_ptr<ModuleWrapper> moduleWrap_;
|
||||
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
|
||||
|
||||
std::mutex m_; // 1 instance mutex
|
||||
|
||||
public:
|
||||
WasmiEngine();
|
||||
~WasmiEngine() = default;
|
||||
|
||||
static EnginePtr
|
||||
init();
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
run(Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gas,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmParam> const& params,
|
||||
ImportVec const& imports,
|
||||
beast::Journal j);
|
||||
|
||||
NotTEC
|
||||
check(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmParam> const& params,
|
||||
ImportVec const& imports,
|
||||
beast::Journal j);
|
||||
|
||||
std::int64_t
|
||||
getGas() const;
|
||||
|
||||
// Host functions helper functionality
|
||||
wasm_trap_t*
|
||||
newTrap(std::string const& msg);
|
||||
|
||||
beast::Journal
|
||||
getJournal() const;
|
||||
|
||||
private:
|
||||
InstanceWrapper&
|
||||
getRT(int m = 0, int i = 0) const;
|
||||
|
||||
wmem
|
||||
getMem() const;
|
||||
|
||||
Expected<WasmResult<int32_t>, TER>
|
||||
runHlp(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
int64_t gas,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmParam> const& params,
|
||||
ImportVec const& imports);
|
||||
|
||||
NotTEC
|
||||
checkHlp(
|
||||
Bytes const& wasmCode,
|
||||
HostFunctions& hfs,
|
||||
std::string_view funcName,
|
||||
std::vector<WasmParam> const& params,
|
||||
ImportVec const& imports);
|
||||
|
||||
int
|
||||
addModule(Bytes const& wasmCode, bool instantiate, ImportVec const& imports, int64_t gas);
|
||||
void
|
||||
clearModules();
|
||||
|
||||
// int addInstance();
|
||||
|
||||
int32_t
|
||||
runFunc(std::string_view const funcName, int32_t p);
|
||||
|
||||
int32_t
|
||||
makeModule(Bytes const& wasmCode, WasmExternVec const& imports = {});
|
||||
|
||||
FuncInfo
|
||||
getFunc(std::string_view funcName) const;
|
||||
|
||||
static std::vector<wasm_val_t>
|
||||
convertParams(std::vector<WasmParam> const& params);
|
||||
|
||||
static int
|
||||
compareParamTypes(wasm_valtype_vec_t const* ftp, std::vector<wasm_val_t> const& p);
|
||||
|
||||
static void
|
||||
add_param(std::vector<wasm_val_t>& in, int32_t p);
|
||||
static void
|
||||
add_param(std::vector<wasm_val_t>& in, int64_t p);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(std::string_view func, Types&&... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(FuncInfo const& f, Types&&... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(FuncInfo const& f, std::vector<wasm_val_t>& in);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int32_t p, Types&&... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int64_t p, Types&&... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(
|
||||
FuncInfo const& f,
|
||||
std::vector<wasm_val_t>& in,
|
||||
uint8_t const* d,
|
||||
int32_t sz,
|
||||
Types&&... args);
|
||||
|
||||
template <int NR, class... Types>
|
||||
inline WasmiResult
|
||||
call(FuncInfo const& f, std::vector<wasm_val_t>& in, Bytes const& p, Types&&... args);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -945,6 +945,82 @@ power(Number const& f, unsigned n)
|
||||
return r;
|
||||
}
|
||||
|
||||
// Series expansion method approximation of ln(x)
|
||||
static Number
|
||||
ln(Number const& x, int iterations = 50)
|
||||
{
|
||||
Number const N0(0);
|
||||
Number const N2(2, 0);
|
||||
Number const N05(5, -1);
|
||||
Number const LN2(693'147'180'559'945'309ll, -18);
|
||||
|
||||
if (x <= 0)
|
||||
{
|
||||
throw std::runtime_error("Not a positive value");
|
||||
}
|
||||
if (x == 1)
|
||||
{
|
||||
return N0;
|
||||
}
|
||||
|
||||
int exponent = 0;
|
||||
Number mantissa = x;
|
||||
|
||||
while (mantissa >= N2)
|
||||
{
|
||||
mantissa /= 2;
|
||||
exponent += 1;
|
||||
}
|
||||
while (mantissa < N05)
|
||||
{
|
||||
mantissa *= 2;
|
||||
exponent -= 1;
|
||||
}
|
||||
|
||||
Number z = (mantissa - 1) / (mantissa + 1);
|
||||
Number const zz = z * z;
|
||||
Number sum;
|
||||
|
||||
for (int i = 1; i <= iterations; ++i)
|
||||
{
|
||||
sum = sum + z / ((2 * i) - 1);
|
||||
z = z * zz;
|
||||
}
|
||||
|
||||
return 2 * sum + exponent * LN2;
|
||||
}
|
||||
|
||||
Number
|
||||
log10(Number const& x, int iterations)
|
||||
{
|
||||
Number const N0(0);
|
||||
Number const LN10(2'302'585'092'994'045'684ll, -18);
|
||||
|
||||
if (x <= 0)
|
||||
{
|
||||
throw std::runtime_error("Not a positive value");
|
||||
}
|
||||
if (x == 1)
|
||||
{
|
||||
return N0;
|
||||
}
|
||||
|
||||
if (x <= Number(10))
|
||||
{
|
||||
auto const r = ln(x, iterations) / LN10;
|
||||
return r;
|
||||
}
|
||||
|
||||
// (1 <= normalX < 10)
|
||||
// x = normalX * 10^norm
|
||||
// ln(x) = ln(normalX * 10^norm) = ln(normalX) + norm * ln(10)
|
||||
int const diffExp = Number::mantissaLog() + x.exponent_;
|
||||
Number const normalX = x / Number(1, diffExp);
|
||||
auto const lnX = ln(normalX, iterations) + diffExp * LN10;
|
||||
auto const lgX = lnX / LN10;
|
||||
return lgX;
|
||||
}
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
namespace detail {
|
||||
|
||||
@@ -376,14 +374,14 @@ ApplyStateTable::erase(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
auto const iter = items_.find(sle->key());
|
||||
if (iter == items_.end())
|
||||
Throw<std::logic_error>("ApplyStateTable::erase: missing key");
|
||||
LogicError("ApplyStateTable::erase: missing key");
|
||||
auto& item = iter->second;
|
||||
if (item.second != sle)
|
||||
Throw<std::logic_error>("ApplyStateTable::erase: unknown SLE");
|
||||
LogicError("ApplyStateTable::erase: unknown SLE");
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("ApplyStateTable::erase: double erase");
|
||||
LogicError("ApplyStateTable::erase: double erase");
|
||||
break;
|
||||
case Action::insert:
|
||||
items_.erase(iter);
|
||||
@@ -407,7 +405,7 @@ ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("ApplyStateTable::rawErase: double erase");
|
||||
LogicError("ApplyStateTable::rawErase: double erase");
|
||||
break;
|
||||
case Action::insert:
|
||||
items_.erase(result.first);
|
||||
@@ -438,11 +436,11 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::cache:
|
||||
Throw<std::logic_error>("ApplyStateTable::insert: already cached");
|
||||
LogicError("ApplyStateTable::insert: already cached");
|
||||
case Action::insert:
|
||||
Throw<std::logic_error>("ApplyStateTable::insert: already inserted");
|
||||
LogicError("ApplyStateTable::insert: already inserted");
|
||||
case Action::modify:
|
||||
Throw<std::logic_error>("ApplyStateTable::insert: already modified");
|
||||
LogicError("ApplyStateTable::insert: already modified");
|
||||
case Action::erase:
|
||||
break;
|
||||
}
|
||||
@@ -468,7 +466,7 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("ApplyStateTable::replace: already erased");
|
||||
LogicError("ApplyStateTable::replace: already erased");
|
||||
case Action::cache:
|
||||
item.first = Action::modify;
|
||||
break;
|
||||
@@ -484,14 +482,14 @@ ApplyStateTable::update(ReadView const& base, std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
auto const iter = items_.find(sle->key());
|
||||
if (iter == items_.end())
|
||||
Throw<std::logic_error>("ApplyStateTable::update: missing key");
|
||||
LogicError("ApplyStateTable::update: missing key");
|
||||
auto& item = iter->second;
|
||||
if (item.second != sle)
|
||||
Throw<std::logic_error>("ApplyStateTable::update: unknown SLE");
|
||||
LogicError("ApplyStateTable::update: unknown SLE");
|
||||
switch (item.first)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("ApplyStateTable::update: erased");
|
||||
LogicError("ApplyStateTable::update: erased");
|
||||
break;
|
||||
case Action::cache:
|
||||
item.first = Action::modify;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -41,8 +40,10 @@ findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start)
|
||||
{
|
||||
node = view.peek(keylet::page(directory, page));
|
||||
if (!node)
|
||||
Throw<std::logic_error>(
|
||||
"Directory chain: root back-pointer broken."); // LCOV_EXCL_LINE
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: root back-pointer broken.");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
auto indexes = node->getFieldV256(sfIndexes);
|
||||
@@ -61,20 +62,21 @@ insertKey(
|
||||
if (preserveOrder)
|
||||
{
|
||||
if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
|
||||
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
|
||||
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
|
||||
|
||||
indexes.push_back(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't be sure if this page is already sorted because it may be a
|
||||
// legacy page we haven't yet touched. Take the time to sort it.
|
||||
// We can't be sure if this page is already sorted because
|
||||
// it may be a legacy page we haven't yet touched. Take
|
||||
// the time to sort it.
|
||||
std::sort(indexes.begin(), indexes.end());
|
||||
|
||||
auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
|
||||
|
||||
if (pos != indexes.end() && key == *pos)
|
||||
Throw<std::logic_error>("dirInsert: double insertion"); // LCOV_EXCL_LINE
|
||||
LogicError("dirInsert: double insertion"); // LCOV_EXCL_LINE
|
||||
|
||||
indexes.insert(pos, key);
|
||||
}
|
||||
@@ -127,7 +129,8 @@ insertPage(
|
||||
node->setFieldH256(sfRootIndex, directory.key);
|
||||
node->setFieldV256(sfIndexes, indexes);
|
||||
|
||||
// Save some space by not specifying the value 0 since it's the default.
|
||||
// Save some space by not specifying the value 0 since
|
||||
// it's the default.
|
||||
if (page != 1)
|
||||
node->setFieldU64(sfIndexPrevious, page - 1);
|
||||
XRPL_ASSERT_PARTS(!nextPage, "xrpl::directory::insertPage", "nextPage has default value");
|
||||
@@ -196,24 +199,28 @@ ApplyView::emptyDirDelete(Keylet const& directory)
|
||||
auto nextPage = node->getFieldU64(sfIndexNext);
|
||||
|
||||
if (nextPage == rootPage && prevPage != rootPage)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
|
||||
|
||||
if (prevPage == rootPage && nextPage != rootPage)
|
||||
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
|
||||
|
||||
// Older versions of the code would, in some cases, allow the last page to
|
||||
// be empty. Remove such pages:
|
||||
// Older versions of the code would, in some cases, allow the last
|
||||
// page to be empty. Remove such pages:
|
||||
if (nextPage == prevPage && nextPage != rootPage)
|
||||
{
|
||||
auto last = peek(keylet::page(directory, nextPage));
|
||||
|
||||
if (!last)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: fwd link broken.");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (!last->getFieldV256(sfIndexes).empty())
|
||||
return false;
|
||||
|
||||
// Update the first page's linked list and mark it as updated.
|
||||
// Update the first page's linked list and
|
||||
// mark it as updated.
|
||||
node->setFieldU64(sfIndexNext, rootPage);
|
||||
node->setFieldU64(sfIndexPrevious, rootPage);
|
||||
update(node);
|
||||
@@ -221,7 +228,8 @@ ApplyView::emptyDirDelete(Keylet const& directory)
|
||||
// And erase the empty last page:
|
||||
erase(last);
|
||||
|
||||
// Make sure our local values reflect the updated information:
|
||||
// Make sure our local values reflect the
|
||||
// updated information:
|
||||
nextPage = rootPage;
|
||||
prevPage = rootPage;
|
||||
}
|
||||
@@ -261,33 +269,46 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
|
||||
return true;
|
||||
}
|
||||
|
||||
// The current page is now empty; check if it can be deleted, and, if so,
|
||||
// whether the entire directory can now be removed.
|
||||
// The current page is now empty; check if it can be
|
||||
// deleted, and, if so, whether the entire directory
|
||||
// can now be removed.
|
||||
auto prevPage = node->getFieldU64(sfIndexPrevious);
|
||||
auto nextPage = node->getFieldU64(sfIndexNext);
|
||||
|
||||
// The first page is the directory's root node and is treated specially: it
|
||||
// can never be deleted even if it is empty, unless we plan on removing the
|
||||
// entire directory.
|
||||
// The first page is the directory's root node and is
|
||||
// treated specially: it can never be deleted even if
|
||||
// it is empty, unless we plan on removing the entire
|
||||
// directory.
|
||||
if (page == rootPage)
|
||||
{
|
||||
if (nextPage == page && prevPage != page)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: fwd link broken");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (prevPage == page && nextPage != page)
|
||||
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: rev link broken");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Older versions of the code would, in some cases, allow the last page
|
||||
// to be empty. Remove such pages if we stumble on them:
|
||||
// Older versions of the code would, in some cases,
|
||||
// allow the last page to be empty. Remove such
|
||||
// pages if we stumble on them:
|
||||
if (nextPage == prevPage && nextPage != page)
|
||||
{
|
||||
auto last = peek(keylet::page(directory, nextPage));
|
||||
if (!last)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: fwd link broken.");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (last->getFieldV256(sfIndexes).empty())
|
||||
{
|
||||
// Update the first page's linked list and mark it as updated.
|
||||
// Update the first page's linked list and
|
||||
// mark it as updated.
|
||||
node->setFieldU64(sfIndexNext, page);
|
||||
node->setFieldU64(sfIndexPrevious, page);
|
||||
update(node);
|
||||
@@ -295,7 +316,8 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
|
||||
// And erase the empty last page:
|
||||
erase(last);
|
||||
|
||||
// Make sure our local values reflect the updated information:
|
||||
// Make sure our local values reflect the
|
||||
// updated information:
|
||||
nextPage = page;
|
||||
prevPage = page;
|
||||
}
|
||||
@@ -313,24 +335,25 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
|
||||
|
||||
// This can never happen for nodes other than the root:
|
||||
if (nextPage == page)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: fwd link broken"); // LCOV_EXCL_LINE
|
||||
|
||||
if (prevPage == page)
|
||||
Throw<std::logic_error>("Directory chain: rev link broken"); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: rev link broken"); // LCOV_EXCL_LINE
|
||||
|
||||
// This node isn't the root, so it can either be in the middle of the list,
|
||||
// or at the end. Unlink it first and then check if that leaves the list
|
||||
// with only a root:
|
||||
// This node isn't the root, so it can either be in the
|
||||
// middle of the list, or at the end. Unlink it first
|
||||
// and then check if that leaves the list with only a
|
||||
// root:
|
||||
auto prev = peek(keylet::page(directory, prevPage));
|
||||
if (!prev)
|
||||
Throw<std::logic_error>("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: fwd link broken."); // LCOV_EXCL_LINE
|
||||
// Fix previous to point to its new next.
|
||||
prev->setFieldU64(sfIndexNext, nextPage);
|
||||
update(prev);
|
||||
|
||||
auto next = peek(keylet::page(directory, nextPage));
|
||||
if (!next)
|
||||
Throw<std::logic_error>("Directory chain: rev link broken."); // LCOV_EXCL_LINE
|
||||
LogicError("Directory chain: rev link broken."); // LCOV_EXCL_LINE
|
||||
// Fix next to point to its new previous.
|
||||
next->setFieldU64(sfIndexPrevious, prevPage);
|
||||
update(next);
|
||||
@@ -338,12 +361,13 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
|
||||
// The page is no longer linked. Delete it.
|
||||
erase(node);
|
||||
|
||||
// Check whether the next page is the last page and, if so, whether it's
|
||||
// empty. If it is, delete it.
|
||||
// Check whether the next page is the last page and, if
|
||||
// so, whether it's empty. If it is, delete it.
|
||||
if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
|
||||
next->getFieldV256(sfIndexes).empty())
|
||||
{
|
||||
// Since next doesn't point to the root, it can't be pointing to prev.
|
||||
// Since next doesn't point to the root, it
|
||||
// can't be pointing to prev.
|
||||
erase(next);
|
||||
|
||||
// The previous page is now the last page:
|
||||
@@ -353,16 +377,18 @@ ApplyView::dirRemove(Keylet const& directory, std::uint64_t page, uint256 const&
|
||||
// And the root points to the last page:
|
||||
auto root = peek(keylet::page(directory, rootPage));
|
||||
if (!root)
|
||||
Throw<std::logic_error>("Directory chain: root link broken."); // LCOV_EXCL_LINE
|
||||
|
||||
{ // LCOV_EXCL_START
|
||||
LogicError("Directory chain: root link broken.");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
root->setFieldU64(sfIndexPrevious, prevPage);
|
||||
update(root);
|
||||
|
||||
nextPage = rootPage;
|
||||
}
|
||||
|
||||
// If we're not keeping the root, then check to see if it's left empty.
|
||||
// If so, delete it as well.
|
||||
// If we're not keeping the root, then check to see if
|
||||
// it's left empty. If so, delete it as well.
|
||||
if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
|
||||
{
|
||||
if (prev->getFieldV256(sfIndexes).empty())
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -462,14 +461,14 @@ void
|
||||
Ledger::rawErase(std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
if (!stateMap_.delItem(sle->key()))
|
||||
Throw<std::logic_error>("Ledger::rawErase: key not found");
|
||||
LogicError("Ledger::rawErase: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
Ledger::rawErase(uint256 const& key)
|
||||
{
|
||||
if (!stateMap_.delItem(key))
|
||||
Throw<std::logic_error>("Ledger::rawErase: key not found");
|
||||
LogicError("Ledger::rawErase: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -479,7 +478,7 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
|
||||
sle->add(ss);
|
||||
if (!stateMap_.addGiveItem(
|
||||
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
|
||||
Throw<std::logic_error>("Ledger::rawInsert: key already exists");
|
||||
LogicError("Ledger::rawInsert: key already exists");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -489,7 +488,7 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
|
||||
sle->add(ss);
|
||||
if (!stateMap_.updateGiveItem(
|
||||
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle->key(), ss.slice())))
|
||||
Throw<std::logic_error>("Ledger::rawReplace: key not found");
|
||||
LogicError("Ledger::rawReplace: key not found");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -505,7 +504,7 @@ Ledger::rawTxInsert(
|
||||
s.addVL(txn->peekData());
|
||||
s.addVL(metaData->peekData());
|
||||
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
|
||||
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
|
||||
LogicError("duplicate_tx: " + to_string(key));
|
||||
}
|
||||
|
||||
uint256
|
||||
@@ -523,7 +522,7 @@ Ledger::rawTxInsertWithHash(
|
||||
auto item = make_shamapitem(key, s.slice());
|
||||
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
|
||||
if (!txMap_.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
|
||||
Throw<std::logic_error>("duplicate_tx: " + to_string(key));
|
||||
LogicError("duplicate_tx: " + to_string(key));
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class OpenView::txs_iter_impl : public txs_type::iter_base
|
||||
@@ -249,7 +247,7 @@ OpenView::rawTxInsert(
|
||||
auto const result = txs_.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData));
|
||||
if (!result.second)
|
||||
Throw<std::logic_error>("rawTxInsert: duplicate TX id: " + to_string(key));
|
||||
LogicError("rawTxInsert: duplicate TX id: " + to_string(key));
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/detail/RawStateTable.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
namespace detail {
|
||||
|
||||
@@ -243,7 +241,7 @@ RawStateTable::erase(std::shared_ptr<SLE> const& sle)
|
||||
switch (item.action)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("RawStateTable::erase: already erased");
|
||||
LogicError("RawStateTable::erase: already erased");
|
||||
break;
|
||||
case Action::insert:
|
||||
items_.erase(result.first);
|
||||
@@ -272,10 +270,10 @@ RawStateTable::insert(std::shared_ptr<SLE> const& sle)
|
||||
item.sle = sle;
|
||||
break;
|
||||
case Action::insert:
|
||||
Throw<std::logic_error>("RawStateTable::insert: already inserted");
|
||||
LogicError("RawStateTable::insert: already inserted");
|
||||
break;
|
||||
case Action::replace:
|
||||
Throw<std::logic_error>("RawStateTable::insert: already exists");
|
||||
LogicError("RawStateTable::insert: already exists");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -293,7 +291,7 @@ RawStateTable::replace(std::shared_ptr<SLE> const& sle)
|
||||
switch (item.action)
|
||||
{
|
||||
case Action::erase:
|
||||
Throw<std::logic_error>("RawStateTable::replace: was erased");
|
||||
LogicError("RawStateTable::replace: was erased");
|
||||
break;
|
||||
case Action::insert:
|
||||
case Action::replace:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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 +32,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 +68,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 +93,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 +123,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 +183,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 +253,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 +265,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 +282,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 +298,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);
|
||||
}();
|
||||
|
||||
@@ -12,13 +12,16 @@ std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal const j)
|
||||
{
|
||||
auto const assetInBalance = accountHolds(view, ammAccountID, issue1, freezeHandling, j);
|
||||
auto const assetOutBalance = accountHolds(view, ammAccountID, issue2, freezeHandling, 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);
|
||||
}
|
||||
|
||||
@@ -26,38 +29,39 @@ Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
|
||||
ammHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
std::optional<Issue> const& optIssue1,
|
||||
std::optional<Issue> const& optIssue2,
|
||||
std::optional<Asset> const& optAsset1,
|
||||
std::optional<Asset> const& optAsset2,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
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)
|
||||
auto const assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
|
||||
auto const asset1 = ammSle[sfAsset];
|
||||
auto const asset2 = ammSle[sfAsset2];
|
||||
if (optAsset1 && optAsset2)
|
||||
{
|
||||
if (invalidAMMAssetPair(
|
||||
*optIssue1, *optIssue2, std::make_optional(std::make_pair(issue1, issue2))))
|
||||
*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 optIssue1 or optIssue2 " << *optIssue1 << " "
|
||||
<< *optIssue2;
|
||||
JLOG(j.debug()) << "ammHolds: Invalid optAsset1 or optAsset2 " << *optAsset1 << " "
|
||||
<< *optAsset2;
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return std::make_optional(std::make_pair(*optIssue1, *optIssue2));
|
||||
return std::make_optional(std::make_pair(*optAsset1, *optAsset2));
|
||||
}
|
||||
auto const singleIssue = [&issue1, &issue2, &j](
|
||||
Issue checkIssue,
|
||||
char const* label) -> std::optional<std::pair<Issue, Issue>> {
|
||||
if (checkIssue == issue1)
|
||||
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(issue1, issue2));
|
||||
return std::make_optional(std::make_pair(asset1, asset2));
|
||||
}
|
||||
if (checkIssue == issue2)
|
||||
if (checkIssue == asset2)
|
||||
{
|
||||
return std::make_optional(std::make_pair(issue2, issue1));
|
||||
return std::make_optional(std::make_pair(asset2, asset1));
|
||||
}
|
||||
// Unreachable unless AMM corrupted.
|
||||
// LCOV_EXCL_START
|
||||
@@ -65,29 +69,35 @@ ammHolds(
|
||||
return std::nullopt;
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
if (optIssue1)
|
||||
if (optAsset1)
|
||||
{
|
||||
return singleIssue(*optIssue1, "optIssue1");
|
||||
return singleAsset(*optAsset1, "optAsset1");
|
||||
}
|
||||
if (optIssue2)
|
||||
if (optAsset2)
|
||||
{
|
||||
// Cannot have Amount2 without Amount.
|
||||
return singleIssue(*optIssue2, "optIssue2"); // LCOV_EXCL_LINE
|
||||
return singleAsset(*optAsset2, "optAsset2"); // LCOV_EXCL_LINE
|
||||
}
|
||||
return std::make_optional(std::make_pair(issue1, issue2));
|
||||
return std::make_optional(std::make_pair(asset1, asset2));
|
||||
}();
|
||||
if (!issues)
|
||||
if (!assets)
|
||||
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]);
|
||||
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,
|
||||
Currency const& cur1,
|
||||
Currency const& cur2,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
@@ -97,7 +107,7 @@ ammLPHolds(
|
||||
// checks if the underlying assets of LPToken are frozen with the
|
||||
// fixFrozenLPTokenTransfer amendment
|
||||
|
||||
auto const currency = ammLPTCurrency(cur1, cur2);
|
||||
auto const currency = ammLPTCurrency(asset1, asset2);
|
||||
STAmount amount;
|
||||
|
||||
auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
|
||||
@@ -123,14 +133,14 @@ ammLPHolds(
|
||||
// Put balance in account terms.
|
||||
amount.negate();
|
||||
}
|
||||
amount.setIssuer(ammAccount);
|
||||
amount.get<Issue>().account = ammAccount;
|
||||
|
||||
JLOG(j.trace()) << "ammLPHolds:"
|
||||
<< " lpAccount=" << to_string(lpAccount)
|
||||
<< " amount=" << amount.getFullText();
|
||||
}
|
||||
|
||||
return view.balanceHook(lpAccount, ammAccount, amount);
|
||||
return view.balanceHookIOU(lpAccount, ammAccount, amount);
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -140,13 +150,7 @@ ammLPHolds(
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j)
|
||||
{
|
||||
return ammLPHolds(
|
||||
view,
|
||||
ammSle[sfAsset].get<Issue>().currency,
|
||||
ammSle[sfAsset2].get<Issue>().currency,
|
||||
ammSle[sfAccount],
|
||||
lpAccount,
|
||||
j);
|
||||
return ammLPHolds(view, ammSle[sfAsset], ammSle[sfAsset2], ammSle[sfAccount], lpAccount, j);
|
||||
}
|
||||
|
||||
std::uint16_t
|
||||
@@ -180,25 +184,35 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
|
||||
}
|
||||
|
||||
STAmount
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const& issue)
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const& asset)
|
||||
{
|
||||
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};
|
||||
// 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
|
||||
@@ -214,36 +228,80 @@ deleteAMMTrustLines(
|
||||
[&](LedgerEntryType nodeType,
|
||||
uint256 const&,
|
||||
std::shared_ptr<SLE>& sleItem) -> std::pair<TER, SkipEntry> {
|
||||
// Skip AMM
|
||||
if (nodeType == LedgerEntryType::ltAMM)
|
||||
// Skip AMM and MPToken
|
||||
if (nodeType == ltAMM || nodeType == ltMPTOKEN)
|
||||
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)
|
||||
if (nodeType == ltRIPPLE_STATE)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMTrustLines: deleting trustline with "
|
||||
"non-zero balance.";
|
||||
return {tecINTERNAL, SkipEntry::No};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
// 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};
|
||||
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, Issue const& asset, Issue const& asset2, beast::Journal j)
|
||||
deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Journal j)
|
||||
{
|
||||
auto ammSle = sb.peek(keylet::amm(asset, asset2));
|
||||
if (!ammSle)
|
||||
@@ -269,6 +327,12 @@ deleteAMMAccount(Sandbox& sb, Issue const& asset, Issue const& asset2, beast::Jo
|
||||
!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))
|
||||
{
|
||||
@@ -297,7 +361,7 @@ initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Issue const& lptIssue,
|
||||
Asset const& lptAsset,
|
||||
std::uint16_t tfee)
|
||||
{
|
||||
auto const& rules = view.rules();
|
||||
@@ -326,7 +390,7 @@ initializeFeeAuctionVote(
|
||||
.count() +
|
||||
TOTAL_TIME_SLOT_SECS;
|
||||
auctionSlot.setFieldU32(sfExpiration, expiration);
|
||||
auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
|
||||
auctionSlot.setFieldAmount(sfPrice, STAmount{lptAsset, 0});
|
||||
// Set the fee
|
||||
if (tfee != 0)
|
||||
{
|
||||
@@ -351,17 +415,23 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
{
|
||||
// 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.
|
||||
// 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 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.
|
||||
// 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;
|
||||
@@ -377,22 +447,28 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
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 (sle->getFieldU16(sfLedgerEntryType) == ltAMM)
|
||||
if (entryType == ltAMM)
|
||||
{
|
||||
if (hasAMM)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
hasAMM = true;
|
||||
continue;
|
||||
}
|
||||
if (sle->getFieldU16(sfLedgerEntryType) != ltRIPPLE_STATE)
|
||||
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.issue() == ammIssue || highLimit.issue() == ammIssue;
|
||||
lowLimit.asset() == ammIssue || highLimit.asset() == ammIssue;
|
||||
|
||||
// Liquidity Provider trustline
|
||||
if (isLPTrustline)
|
||||
@@ -400,9 +476,11 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
// 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
|
||||
@@ -413,6 +491,7 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// AMM account has at most two IOU trustlines
|
||||
else if (++nIOUTrustLines > 2)
|
||||
{
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
@@ -421,7 +500,8 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
|
||||
if (uNodeNext == 0)
|
||||
{
|
||||
if (nLPTokenTrustLines != 1 || nIOUTrustLines == 0 || nIOUTrustLines > 2)
|
||||
if (nLPTokenTrustLines != 1 || (nIOUTrustLines == 0 && nMPT == 0) ||
|
||||
(nIOUTrustLines > 2 || nMPT > 2) || (nIOUTrustLines + nMPT) > 2)
|
||||
return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
return true;
|
||||
}
|
||||
@@ -437,7 +517,7 @@ verifyAndAdjustLPTokenBalance(
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account)
|
||||
{
|
||||
auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account);
|
||||
auto const res = isOnlyLiquidityProvider(sb, lpTokens.get<Issue>(), account);
|
||||
if (!res.has_value())
|
||||
{
|
||||
return Unexpected<TER>(res.error());
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -81,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;
|
||||
|
||||
@@ -154,7 +153,7 @@ getPseudoAccountFields()
|
||||
if (!ar)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
Throw<std::logic_error>(
|
||||
LogicError(
|
||||
"xrpl::getPseudoAccountFields : unable to find account root "
|
||||
"ledger format");
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -306,18 +306,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 +480,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 +778,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)
|
||||
@@ -352,6 +331,8 @@ accountHolds(
|
||||
}
|
||||
}
|
||||
|
||||
if (view.rules().enabled(featureMPTokensV2))
|
||||
return view.balanceHookMPT(account, mptIssue, amount.mpt().value());
|
||||
return amount;
|
||||
}
|
||||
|
||||
@@ -365,19 +346,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 +364,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 +508,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 +537,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 +608,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 +845,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 +858,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 +961,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 +989,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 +1023,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 +1044,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 +1059,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 +1074,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 +1095,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 +1114,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 +1234,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 +1254,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 +1275,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 +1290,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 +1323,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 +1340,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 +1364,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
|
||||
|
||||
@@ -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
|
||||
@@ -90,11 +90,23 @@ getMPTValue(STAmount const& amount)
|
||||
static bool
|
||||
areComparable(STAmount const& v1, STAmount const& v2)
|
||||
{
|
||||
if (v1.holds<Issue>() && v2.holds<Issue>())
|
||||
return v1.native() == v2.native() && v1.get<Issue>().currency == v2.get<Issue>().currency;
|
||||
if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>())
|
||||
return v1.get<MPTIssue>() == v2.get<MPTIssue>();
|
||||
return false;
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss1, ValidIssueType TIss2>(TIss1 const& issue1, TIss2 const& issue2) {
|
||||
if constexpr (is_issue_v<TIss1> && is_issue_v<TIss2>)
|
||||
{
|
||||
return v1.native() == v2.native() && issue1.currency == issue2.currency;
|
||||
}
|
||||
else if constexpr (is_mptissue_v<TIss1> && is_mptissue_v<TIss2>)
|
||||
{
|
||||
return issue1 == issue2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
},
|
||||
v1.asset().value(),
|
||||
v2.asset().value());
|
||||
}
|
||||
|
||||
static_assert(INITIAL_XRP.drops() == STAmount::cMaxNativeN);
|
||||
@@ -513,26 +525,35 @@ canAdd(STAmount const& a, STAmount const& b)
|
||||
}
|
||||
|
||||
// IOU case (precision check)
|
||||
if (a.holds<Issue>() && b.holds<Issue>())
|
||||
{
|
||||
static STAmount const one{IOUAmount{1, 0}, noIssue()};
|
||||
static STAmount const maxLoss{IOUAmount{1, -4}, noIssue()};
|
||||
STAmount const lhs = divide((a - b) + b, a, noIssue()) - one;
|
||||
STAmount const rhs = divide((b - a) + a, b, noIssue()) - one;
|
||||
return ((rhs.negative() ? -rhs : rhs) + (lhs.negative() ? -lhs : lhs)) <= maxLoss;
|
||||
}
|
||||
auto const ret = std::visit(
|
||||
[&]<ValidIssueType TIss1, ValidIssueType TIss2>(
|
||||
TIss1 const&, TIss2 const&) -> std::optional<bool> {
|
||||
if constexpr (is_issue_v<TIss1> && is_issue_v<TIss2>)
|
||||
{
|
||||
static STAmount const one{IOUAmount{1, 0}, noIssue()};
|
||||
static STAmount const maxLoss{IOUAmount{1, -4}, noIssue()};
|
||||
STAmount const lhs = divide((a - b) + b, a, noIssue()) - one;
|
||||
STAmount const rhs = divide((b - a) + a, b, noIssue()) - one;
|
||||
return ((rhs.negative() ? -rhs : rhs) + (lhs.negative() ? -lhs : lhs)) <= maxLoss;
|
||||
}
|
||||
|
||||
// MPT (overflow & underflow check)
|
||||
if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
|
||||
{
|
||||
MPTAmount const A = a.mpt();
|
||||
MPTAmount const B = b.mpt();
|
||||
return !(
|
||||
(B > MPTAmount{0} &&
|
||||
A > MPTAmount{std::numeric_limits<MPTAmount::value_type>::max()} - B) ||
|
||||
(B < MPTAmount{0} &&
|
||||
A < MPTAmount{std::numeric_limits<MPTAmount::value_type>::min()} - B));
|
||||
}
|
||||
// MPT (overflow & underflow check)
|
||||
if constexpr (is_mptissue_v<TIss1> && is_mptissue_v<TIss2>)
|
||||
{
|
||||
MPTAmount const A = a.mpt();
|
||||
MPTAmount const B = b.mpt();
|
||||
return !(
|
||||
(B > MPTAmount{0} &&
|
||||
A > MPTAmount{std::numeric_limits<MPTAmount::value_type>::max()} - B) ||
|
||||
(B < MPTAmount{0} &&
|
||||
A < MPTAmount{std::numeric_limits<MPTAmount::value_type>::min()} - B));
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
a.asset().value(),
|
||||
b.asset().value());
|
||||
if (ret)
|
||||
return *ret;
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("STAmount::canAdd : unexpected STAmount type");
|
||||
return false;
|
||||
@@ -585,27 +606,36 @@ canSubtract(STAmount const& a, STAmount const& b)
|
||||
}
|
||||
|
||||
// IOU case (no underflow)
|
||||
if (a.holds<Issue>() && b.holds<Issue>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
auto const ret = std::visit(
|
||||
[&]<ValidIssueType TIss1, ValidIssueType TIss2>(
|
||||
TIss1 const&, TIss2 const&) -> std::optional<bool> {
|
||||
if constexpr (is_issue_v<TIss1> && is_issue_v<TIss2>)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// MPT case (underflow & overflow check)
|
||||
if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
|
||||
{
|
||||
MPTAmount const A = a.mpt();
|
||||
MPTAmount const B = b.mpt();
|
||||
// MPT case (underflow & overflow check)
|
||||
if constexpr (is_mptissue_v<TIss1> && is_mptissue_v<TIss2>)
|
||||
{
|
||||
MPTAmount const A = a.mpt();
|
||||
MPTAmount const B = b.mpt();
|
||||
|
||||
// Underflow check
|
||||
if (B > MPTAmount{0} && A < B)
|
||||
return false;
|
||||
// Underflow check
|
||||
if (B > MPTAmount{0} && A < B)
|
||||
return false;
|
||||
|
||||
// Overflow check
|
||||
if (B < MPTAmount{0} &&
|
||||
A > MPTAmount{std::numeric_limits<MPTAmount::value_type>::max()} + B)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// Overflow check
|
||||
if (B < MPTAmount{0} &&
|
||||
A > MPTAmount{std::numeric_limits<MPTAmount::value_type>::max()} + B)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
a.asset().value(),
|
||||
b.asset().value());
|
||||
if (ret)
|
||||
return *ret;
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("STAmount::canSubtract : unexpected STAmount type");
|
||||
return false;
|
||||
@@ -751,45 +781,49 @@ STAmount::getJson(JsonOptions) const
|
||||
void
|
||||
STAmount::add(Serializer& s) const
|
||||
{
|
||||
if (native())
|
||||
{
|
||||
XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::add : zero offset");
|
||||
|
||||
if (!mIsNegative)
|
||||
{
|
||||
s.add64(mValue | cPositive);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAsset.visit(
|
||||
[&](MPTIssue const& issue) {
|
||||
auto u8 = static_cast<unsigned char>(cMPToken >> 56);
|
||||
if (!mIsNegative)
|
||||
u8 |= static_cast<unsigned char>(cPositive >> 56);
|
||||
s.add8(u8);
|
||||
s.add64(mValue);
|
||||
}
|
||||
}
|
||||
else if (mAsset.holds<MPTIssue>())
|
||||
{
|
||||
auto u8 = static_cast<unsigned char>(cMPToken >> 56);
|
||||
if (!mIsNegative)
|
||||
u8 |= static_cast<unsigned char>(cPositive >> 56);
|
||||
s.add8(u8);
|
||||
s.add64(mValue);
|
||||
s.addBitString(mAsset.get<MPTIssue>().getMptID());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*this == beast::zero)
|
||||
{
|
||||
s.add64(cIssuedCurrency);
|
||||
}
|
||||
else if (mIsNegative)
|
||||
{ // 512 = not native
|
||||
s.add64(mValue | (static_cast<std::uint64_t>(mOffset + 512 + 97) << (64 - 10)));
|
||||
}
|
||||
else
|
||||
{ // 256 = positive
|
||||
s.add64(mValue | (static_cast<std::uint64_t>(mOffset + 512 + 256 + 97) << (64 - 10)));
|
||||
}
|
||||
s.addBitString(mAsset.get<Issue>().currency);
|
||||
s.addBitString(mAsset.get<Issue>().account);
|
||||
}
|
||||
s.addBitString(issue.getMptID());
|
||||
},
|
||||
[&](Issue const& issue) {
|
||||
if (native())
|
||||
{
|
||||
XRPL_ASSERT(mOffset == 0, "xrpl::STAmount::add : zero offset");
|
||||
|
||||
if (!mIsNegative)
|
||||
{
|
||||
s.add64(mValue | cPositive);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.add64(mValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*this == beast::zero)
|
||||
{
|
||||
s.add64(cIssuedCurrency);
|
||||
}
|
||||
else if (mIsNegative) // 512 = not native
|
||||
{
|
||||
s.add64(mValue | (static_cast<std::uint64_t>(mOffset + 512 + 97) << (64 - 10)));
|
||||
}
|
||||
else // 256 = positive
|
||||
{
|
||||
s.add64(
|
||||
mValue |
|
||||
(static_cast<std::uint64_t>(mOffset + 512 + 256 + 97) << (64 - 10)));
|
||||
}
|
||||
s.addBitString(issue.currency);
|
||||
s.addBitString(issue.account);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1065,7 +1099,7 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
if (isMPT)
|
||||
{
|
||||
// sequence (32 bits) + account (160 bits)
|
||||
uint192 u;
|
||||
MPTID u;
|
||||
if (!u.parseHex(currencyOrMPTID.asString()))
|
||||
Throw<std::runtime_error>("invalid MPTokenIssuanceID");
|
||||
asset = u;
|
||||
@@ -1255,7 +1289,7 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset)
|
||||
int numOffset = num.exponent();
|
||||
int denOffset = den.exponent();
|
||||
|
||||
if (num.native() || num.holds<MPTIssue>())
|
||||
if (num.integral())
|
||||
{
|
||||
while (numVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1265,7 +1299,7 @@ divide(STAmount const& num, STAmount const& den, Asset const& asset)
|
||||
}
|
||||
}
|
||||
|
||||
if (den.native() || den.holds<MPTIssue>())
|
||||
if (den.integral())
|
||||
{
|
||||
while (denVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1330,7 +1364,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
int offset1 = v1.exponent();
|
||||
int offset2 = v2.exponent();
|
||||
|
||||
if (v1.native() || v1.holds<MPTIssue>())
|
||||
if (v1.integral())
|
||||
{
|
||||
while (value1 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1339,7 +1373,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
}
|
||||
}
|
||||
|
||||
if (v2.native() || v2.holds<MPTIssue>())
|
||||
if (v2.integral())
|
||||
{
|
||||
while (value2 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1363,7 +1397,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
// for years, so it is deeply embedded in the behavior of cross-currency
|
||||
// transactions.
|
||||
//
|
||||
// However in 2022 it was noticed that the rounding characteristics were
|
||||
// However, in 2022 it was noticed that the rounding characteristics were
|
||||
// surprising. When the code converts from IOU-like to XRP-like there may
|
||||
// be a fraction of the IOU-like representation that is too small to be
|
||||
// represented in drops. `canonicalizeRound()` currently does some unusual
|
||||
@@ -1380,9 +1414,9 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
// So an alternative rounding approach was introduced. You'll see that
|
||||
// alternative below.
|
||||
static void
|
||||
canonicalizeRound(bool native, std::uint64_t& value, int& offset, bool)
|
||||
canonicalizeRound(bool integral, std::uint64_t& value, int& offset, bool)
|
||||
{
|
||||
if (native)
|
||||
if (integral)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
@@ -1419,9 +1453,9 @@ canonicalizeRound(bool native, std::uint64_t& value, int& offset, bool)
|
||||
// rounding decisions. canonicalizeRoundStrict() tracks all of the bits in
|
||||
// the value being rounded.
|
||||
static void
|
||||
canonicalizeRoundStrict(bool native, std::uint64_t& value, int& offset, bool roundUp)
|
||||
canonicalizeRoundStrict(bool integral, std::uint64_t& value, int& offset, bool roundUp)
|
||||
{
|
||||
if (native)
|
||||
if (integral)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
@@ -1512,9 +1546,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
if (v1 == beast::zero || v2 == beast::zero)
|
||||
return {asset};
|
||||
|
||||
bool const xrp = asset.native();
|
||||
|
||||
if (v1.native() && v2.native() && xrp)
|
||||
if (v1.native() && v2.native() && asset.native())
|
||||
{
|
||||
std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2));
|
||||
std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2));
|
||||
@@ -1545,7 +1577,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa();
|
||||
int offset1 = v1.exponent(), offset2 = v2.exponent();
|
||||
|
||||
if (v1.native() || v1.holds<MPTIssue>())
|
||||
if (v1.integral())
|
||||
{
|
||||
while (value1 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1554,7 +1586,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
}
|
||||
}
|
||||
|
||||
if (v2.native() || v2.holds<MPTIssue>())
|
||||
if (v2.integral())
|
||||
{
|
||||
while (value2 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1570,7 +1602,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
// range. Dividing their product by 10^14 maintains the
|
||||
// precision, by scaling the result to 10^16 to 10^18.
|
||||
//
|
||||
// If the we're rounding up, we want to round up away
|
||||
// If we're rounding up, we want to round up away
|
||||
// from zero, and if we're rounding down, truncation
|
||||
// is implicit.
|
||||
std::uint64_t amount =
|
||||
@@ -1579,7 +1611,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
int offset = offset1 + offset2 + 14;
|
||||
if (resultNegative != roundUp)
|
||||
{
|
||||
CanonicalizeFunc(xrp, amount, offset, roundUp);
|
||||
CanonicalizeFunc(asset.integral(), amount, offset, roundUp);
|
||||
}
|
||||
STAmount result = [&]() {
|
||||
// If appropriate, tell Number to round down. This gives the desired
|
||||
@@ -1590,7 +1622,7 @@ mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool ro
|
||||
|
||||
if (roundUp && !resultNegative && !result)
|
||||
{
|
||||
if (xrp)
|
||||
if (asset.integral())
|
||||
{
|
||||
// return the smallest value above zero
|
||||
amount = 1;
|
||||
@@ -1634,7 +1666,7 @@ divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool
|
||||
std::uint64_t numVal = num.mantissa(), denVal = den.mantissa();
|
||||
int numOffset = num.exponent(), denOffset = den.exponent();
|
||||
|
||||
if (num.native() || num.holds<MPTIssue>())
|
||||
if (num.integral())
|
||||
{
|
||||
while (numVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1643,7 +1675,7 @@ divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool
|
||||
}
|
||||
}
|
||||
|
||||
if (den.native() || den.holds<MPTIssue>())
|
||||
if (den.integral())
|
||||
{
|
||||
while (denVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1668,12 +1700,12 @@ divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool
|
||||
int offset = numOffset - denOffset - 17;
|
||||
|
||||
if (resultNegative != roundUp)
|
||||
canonicalizeRound(asset.native() || asset.holds<MPTIssue>(), amount, offset, roundUp);
|
||||
canonicalizeRound(asset.integral(), amount, offset, roundUp);
|
||||
|
||||
STAmount result = [&]() {
|
||||
// If appropriate, tell Number the rounding mode we are using.
|
||||
// Note that "roundUp == true" actually means "round away from zero".
|
||||
// Otherwise round toward zero.
|
||||
// Otherwise, round toward zero.
|
||||
using enum Number::rounding_mode;
|
||||
MightSaveRound const savedRound(roundUp ^ resultNegative ? upward : downward);
|
||||
return STAmount(asset, amount, offset, resultNegative);
|
||||
@@ -1681,7 +1713,7 @@ divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool
|
||||
|
||||
if (roundUp && !resultNegative && !result)
|
||||
{
|
||||
if (asset.native() || asset.holds<MPTIssue>())
|
||||
if (asset.integral())
|
||||
{
|
||||
// return the smallest value above zero
|
||||
amount = 1;
|
||||
|
||||
@@ -88,22 +88,19 @@ STIssue::getJson(JsonOptions) const
|
||||
void
|
||||
STIssue::add(Serializer& s) const
|
||||
{
|
||||
if (holds<Issue>())
|
||||
{
|
||||
auto const& issue = asset_.get<Issue>();
|
||||
s.addBitString(issue.currency);
|
||||
if (!isXRP(issue.currency))
|
||||
s.addBitString(issue.account);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& issue = asset_.get<MPTIssue>();
|
||||
s.addBitString(issue.getIssuer());
|
||||
s.addBitString(noAccount());
|
||||
std::uint32_t sequence = 0;
|
||||
memcpy(&sequence, issue.getMptID().data(), sizeof(sequence));
|
||||
s.add32(sequence);
|
||||
}
|
||||
asset_.visit(
|
||||
[&](Issue const& issue) {
|
||||
s.addBitString(issue.currency);
|
||||
if (!isXRP(issue.currency))
|
||||
s.addBitString(issue.account);
|
||||
},
|
||||
[&](MPTIssue const& issue) {
|
||||
s.addBitString(issue.getIssuer());
|
||||
s.addBitString(noAccount());
|
||||
std::uint32_t sequence = 0;
|
||||
memcpy(&sequence, issue.getMptID().data(), sizeof(sequence));
|
||||
s.add32(sequence);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -116,7 +113,9 @@ STIssue::isEquivalent(STBase const& t) const
|
||||
bool
|
||||
STIssue::isDefault() const
|
||||
{
|
||||
return holds<Issue>() && asset_.get<Issue>() == xrpIssue();
|
||||
return asset_.visit(
|
||||
[](Issue const& issue) { return issue == xrpIssue(); },
|
||||
[](MPTIssue const&) { return false; });
|
||||
}
|
||||
|
||||
STBase*
|
||||
|
||||
@@ -749,6 +749,12 @@ STObject::setFieldH128(SField const& field, uint128 const& v)
|
||||
setFieldUsingSetValue<STUInt128>(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldH192(SField const& field, uint192 const& v)
|
||||
{
|
||||
setFieldUsingSetValue<STUInt192>(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldH256(SField const& field, uint256 const& v)
|
||||
{
|
||||
|
||||
@@ -736,7 +736,7 @@ parseLeaf(
|
||||
std::string const element_name(json_name + "." + ss.str());
|
||||
|
||||
// each element in this path has some combination of
|
||||
// account, currency, or issuer
|
||||
// account, asset, or issuer
|
||||
|
||||
Json::Value pathEl = value[i][j];
|
||||
|
||||
@@ -746,14 +746,22 @@ parseLeaf(
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value const& account = pathEl["account"];
|
||||
Json::Value const& currency = pathEl["currency"];
|
||||
Json::Value const& issuer = pathEl["issuer"];
|
||||
bool hasCurrency = false;
|
||||
AccountID uAccount, uIssuer;
|
||||
Currency uCurrency;
|
||||
if (pathEl.isMember(jss::currency) && pathEl.isMember(jss::mpt_issuance_id))
|
||||
{
|
||||
error = RPC::make_error(rpcINVALID_PARAMS, "Invalid Asset.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!account && !currency && !issuer)
|
||||
bool const isMPT = pathEl.isMember(jss::mpt_issuance_id);
|
||||
auto const assetName = isMPT ? jss::mpt_issuance_id : jss::currency;
|
||||
Json::Value const& account = pathEl[jss::account];
|
||||
Json::Value const& asset = pathEl[assetName];
|
||||
Json::Value const& issuer = pathEl[jss::issuer];
|
||||
bool hasAsset = false;
|
||||
AccountID uAccount, uIssuer;
|
||||
PathAsset uAsset;
|
||||
|
||||
if (!account && !asset && !issuer)
|
||||
{
|
||||
error = invalid_data(element_name);
|
||||
return ret;
|
||||
@@ -764,7 +772,7 @@ parseLeaf(
|
||||
// human account id
|
||||
if (!account.isString())
|
||||
{
|
||||
error = string_expected(element_name, "account");
|
||||
error = string_expected(element_name, jss::account.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -775,31 +783,51 @@ parseLeaf(
|
||||
auto const a = parseBase58<AccountID>(account.asString());
|
||||
if (!a)
|
||||
{
|
||||
error = invalid_data(element_name, "account");
|
||||
error = invalid_data(element_name, jss::account.c_str());
|
||||
return ret;
|
||||
}
|
||||
uAccount = *a;
|
||||
}
|
||||
}
|
||||
|
||||
if (currency)
|
||||
if (asset)
|
||||
{
|
||||
// human currency
|
||||
if (!currency.isString())
|
||||
// human asset
|
||||
if (!asset.isString())
|
||||
{
|
||||
error = string_expected(element_name, "currency");
|
||||
error = string_expected(element_name, assetName.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
hasCurrency = true;
|
||||
hasAsset = true;
|
||||
|
||||
if (!uCurrency.parseHex(currency.asString()))
|
||||
if (isMPT)
|
||||
{
|
||||
if (!to_currency(uCurrency, currency.asString()))
|
||||
MPTID u;
|
||||
if (!u.parseHex(asset.asString()))
|
||||
{
|
||||
error = invalid_data(element_name, "currency");
|
||||
error = invalid_data(element_name, assetName.c_str());
|
||||
return ret;
|
||||
}
|
||||
if (getMPTIssuer(u) == beast::zero)
|
||||
{
|
||||
error = invalid_data(element_name, jss::account.c_str());
|
||||
return ret;
|
||||
}
|
||||
uAsset = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
Currency currency;
|
||||
if (!currency.parseHex(asset.asString()))
|
||||
{
|
||||
if (!to_currency(currency, asset.asString()))
|
||||
{
|
||||
error = invalid_data(element_name, assetName.c_str());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
uAsset = currency;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,7 +836,7 @@ parseLeaf(
|
||||
// human account id
|
||||
if (!issuer.isString())
|
||||
{
|
||||
error = string_expected(element_name, "issuer");
|
||||
error = string_expected(element_name, jss::issuer.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -817,14 +845,20 @@ parseLeaf(
|
||||
auto const a = parseBase58<AccountID>(issuer.asString());
|
||||
if (!a)
|
||||
{
|
||||
error = invalid_data(element_name, "issuer");
|
||||
error = invalid_data(element_name, jss::issuer.c_str());
|
||||
return ret;
|
||||
}
|
||||
uIssuer = *a;
|
||||
}
|
||||
|
||||
if (isMPT && uIssuer != getMPTIssuer(uAsset.get<MPTID>()))
|
||||
{
|
||||
error = invalid_data(element_name, jss::issuer.c_str());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
p.emplace_back(uAccount, uCurrency, uIssuer, hasCurrency);
|
||||
p.emplace_back(uAccount, uAsset, uIssuer, hasAsset);
|
||||
}
|
||||
|
||||
tail.push_back(p);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user