mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-09 13:32:23 +00:00
Compare commits
447 Commits
bthomee/pi
...
ripple/se/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41ce0893c0 | ||
|
|
74beb5f80d | ||
|
|
a4dfdaf77a | ||
|
|
826f613ad8 | ||
|
|
1259c1d5ca | ||
|
|
dfcad69155 | ||
|
|
620124e58e | ||
|
|
4bae4d781f | ||
|
|
01a5e07830 | ||
|
|
34522bc668 | ||
|
|
d2641d85bd | ||
|
|
75f66bd9fe | ||
|
|
7cd71cb659 | ||
|
|
9917f96166 | ||
|
|
e1cc82587b | ||
|
|
2cc9439fde | ||
|
|
52af9582e2 | ||
|
|
46e88dc732 | ||
|
|
bc24f2e211 | ||
|
|
7a7c993b15 | ||
|
|
9733ca8f91 | ||
|
|
18d5e3e226 | ||
|
|
b30b4e1d65 | ||
|
|
d435893602 | ||
|
|
00b0cf50f6 | ||
|
|
6d1a5be8d2 | ||
|
|
6c4abae3eb | ||
|
|
fe2e5ce649 | ||
|
|
5c9284865d | ||
|
|
6afa51142d | ||
|
|
22054a573d | ||
|
|
7ef256499c | ||
|
|
1338062be7 | ||
|
|
4fc1778ec8 | ||
|
|
65322d9e78 | ||
|
|
0acfa1c4f2 | ||
|
|
309fdfc3f2 | ||
|
|
63bd5fc4ee | ||
|
|
82e7f7eeec | ||
|
|
c5598a4284 | ||
|
|
63d09f9c51 | ||
|
|
0deb6bcadf | ||
|
|
d606f88e84 | ||
|
|
9b013b559b | ||
|
|
1d4a3c00b8 | ||
|
|
4b34102e8e | ||
|
|
d006433579 | ||
|
|
ef3a8e7f77 | ||
|
|
a7ab8ee923 | ||
|
|
e0073a4402 | ||
|
|
2930ef217f | ||
|
|
9dbb301699 | ||
|
|
531e8b6ebd | ||
|
|
90397e1a52 | ||
|
|
888ca2e6d9 | ||
|
|
3db218bf9f | ||
|
|
f8a149c675 | ||
|
|
51458a92e2 | ||
|
|
b6514b680f | ||
|
|
913e4b919e | ||
|
|
196e6a1b27 | ||
|
|
27468ddbcf | ||
|
|
bce5d91e45 | ||
|
|
780380da7e | ||
|
|
654338fa66 | ||
|
|
fdfdf4fceb | ||
|
|
a76bd834b6 | ||
|
|
864d88a3c2 | ||
|
|
9c25d18851 | ||
|
|
3a825a41e1 | ||
|
|
a9ebf786c6 | ||
|
|
5afe8cc321 | ||
|
|
bc5ec3c962 | ||
|
|
7004d216ed | ||
|
|
1775251e90 | ||
|
|
61bcb7621f | ||
|
|
a3f71b1774 | ||
|
|
4df7d1a4bb | ||
|
|
50a74b899d | ||
|
|
00eeab6d44 | ||
|
|
125df7a425 | ||
|
|
b08bcf5d21 | ||
|
|
dc413aef0c | ||
|
|
77dfd56ace | ||
|
|
953b9a3500 | ||
|
|
1d9ec84350 | ||
|
|
0392846a17 | ||
|
|
5148098079 | ||
|
|
1b4a564369 | ||
|
|
fd524c4be9 | ||
|
|
495dda7f58 | ||
|
|
9c3c0280b1 | ||
|
|
f73d8a6cf2 | ||
|
|
4fe508cb92 | ||
|
|
6728ab52b7 | ||
|
|
42b839cf57 | ||
|
|
446ad36cbb | ||
|
|
eb2d44d442 | ||
|
|
77673663ca | ||
|
|
c1381f8ddd | ||
|
|
bd16f7989d | ||
|
|
4c39e63b1a | ||
|
|
fd14054f17 | ||
|
|
65f9cf80c0 | ||
|
|
4318b2ebf7 | ||
|
|
8bbafdd1c4 | ||
|
|
e6ee492822 | ||
|
|
d4510147d1 | ||
|
|
8970dc8b38 | ||
|
|
a9a94fbf1a | ||
|
|
09af3eb6fb | ||
|
|
719ba392db | ||
|
|
cd46b5d999 | ||
|
|
de55a5ebfc | ||
|
|
2ec4a1114e | ||
|
|
f01ac563a9 | ||
|
|
13707dda05 | ||
|
|
ba03a8a9d2 | ||
|
|
7c8279ec83 | ||
|
|
0c6ccb38de | ||
|
|
a1844086d7 | ||
|
|
f625fb993a | ||
|
|
0418ffb26a | ||
|
|
b2627039f6 | ||
|
|
8f97ec3bde | ||
|
|
e85e7b1b1a | ||
|
|
ac173b6827 | ||
|
|
69c61b2235 | ||
|
|
6ae0e860ff | ||
|
|
c077e7f073 | ||
|
|
803a344c65 | ||
|
|
4eb34f381a | ||
|
|
72fffb6e51 | ||
|
|
f7ee580f01 | ||
|
|
122d405750 | ||
|
|
c1c1b4ea67 | ||
|
|
977caea0a5 | ||
|
|
d7ed6d6512 | ||
|
|
f1f2e2629f | ||
|
|
917c610f96 | ||
|
|
317e533d81 | ||
|
|
4160677878 | ||
|
|
430696682d | ||
|
|
4621e4eda3 | ||
|
|
df98db1452 | ||
|
|
981ac7abf4 | ||
|
|
57d2a91ad5 | ||
|
|
673476ef1b | ||
|
|
8bc6f9cd70 | ||
|
|
e214f2556e | ||
|
|
ba5debfecd | ||
|
|
f4a27c9b6d | ||
|
|
fd1cb318e3 | ||
|
|
94b35a234e | ||
|
|
b5d0078927 | ||
|
|
43c80edaf4 | ||
|
|
8c3544a58c | ||
|
|
ed5139d4e3 | ||
|
|
42494dd4cf | ||
|
|
ce84cc8b44 | ||
|
|
9a9a7aab01 | ||
|
|
209a1a6ffa | ||
|
|
9538e9b34c | ||
|
|
384b3608d7 | ||
|
|
fc35a9f9c8 | ||
|
|
c5e50aa221 | ||
|
|
074b1f00d5 | ||
|
|
7a9d245950 | ||
|
|
1809fe07f2 | ||
|
|
409c67494a | ||
|
|
fb97f7b596 | ||
|
|
c626b6403a | ||
|
|
81cbc91927 | ||
|
|
845c503ea6 | ||
|
|
e1513570df | ||
|
|
1c812a6c4d | ||
|
|
0724927799 | ||
|
|
f22e724f03 | ||
|
|
ff39fa59d9 | ||
|
|
d83ec96848 | ||
|
|
f0d0739528 | ||
|
|
375dd50b35 | ||
|
|
419d53ec4c | ||
|
|
d4d70d5675 | ||
|
|
6ab15f8377 | ||
|
|
91f3d51f3d | ||
|
|
9ed60b45f8 | ||
|
|
d5c53dcfd2 | ||
|
|
0fb212e1db | ||
|
|
8015088340 | ||
|
|
103379836a | ||
|
|
e94321fb41 | ||
|
|
bbc28b3b1c | ||
|
|
843e981c8a | ||
|
|
5aab274b7a | ||
|
|
2c30e41191 | ||
|
|
8ea5106b0b | ||
|
|
f57f67a8ae | ||
|
|
61b2fe4f64 | ||
|
|
7ee964f514 | ||
|
|
397bc8781e | ||
|
|
a98269f049 | ||
|
|
8bb8c2e38b | ||
|
|
b66bc47ca9 | ||
|
|
0e9c7458bb | ||
|
|
36ecd3b52b | ||
|
|
1d89940653 | ||
|
|
1a1a6806ec | ||
|
|
1977df9c2e | ||
|
|
6ffbef09c2 | ||
|
|
e05f907788 | ||
|
|
9d1f51b01a | ||
|
|
6c95548df5 | ||
|
|
69ab39d658 | ||
|
|
b9eb66eecc | ||
|
|
e916416642 | ||
|
|
827ecc6e3a | ||
|
|
6a54ed7f14 | ||
|
|
881087dd3d | ||
|
|
90e0bbd0fc | ||
|
|
b57df290de | ||
|
|
8a403f1241 | ||
|
|
1e0741690d | ||
|
|
6d2640871d | ||
|
|
c5d178f152 | ||
|
|
5a17940e2a | ||
|
|
27ac30208d | ||
|
|
c145598ff9 | ||
|
|
50e5608d86 | ||
|
|
abfcc4ef67 | ||
|
|
e40a4df777 | ||
|
|
dba187f8c5 | ||
|
|
7a7b96107c | ||
|
|
8f2f8d53b4 | ||
|
|
49acc61961 | ||
|
|
500bb68831 | ||
|
|
95d78a8600 | ||
|
|
53eb0f60bc | ||
|
|
41205ae928 | ||
|
|
c33b0ae463 | ||
|
|
16087c9680 | ||
|
|
56bc6d58f6 | ||
|
|
ef5d335e09 | ||
|
|
25c3060fef | ||
|
|
ce9f0b38a4 | ||
|
|
35f7cbf772 | ||
|
|
def7758a23 | ||
|
|
58e5b4ad25 | ||
|
|
578413859c | ||
|
|
0db564d261 | ||
|
|
427b7ea104 | ||
|
|
fa8aa49376 | ||
|
|
3195eb16b2 | ||
|
|
7bf6878b4b | ||
|
|
a891b49c67 | ||
|
|
eed280d169 | ||
|
|
0bc1a115ff | ||
|
|
334bcfa5ef | ||
|
|
106dea4559 | ||
|
|
3ffdcf8114 | ||
|
|
4021a7eb28 | ||
|
|
0690fda0f1 | ||
|
|
d0cc48c6d3 | ||
|
|
d66e3c949e | ||
|
|
7e2e10f02c | ||
|
|
aebec5378c | ||
|
|
0c65a386b5 | ||
|
|
22ca691e75 | ||
|
|
fc6ff69752 | ||
|
|
079e251aca | ||
|
|
67e2c1b563 | ||
|
|
29f5430881 | ||
|
|
b6bd268be2 | ||
|
|
209ee25c32 | ||
|
|
101f285bcd | ||
|
|
566b85b3d6 | ||
|
|
af6beb1d7c | ||
|
|
7d22fe804d | ||
|
|
ce19c13059 | ||
|
|
286dc6322b | ||
|
|
c9346cd40d | ||
|
|
9cfb7ac340 | ||
|
|
3a0e9aab4f | ||
|
|
43caa1ef29 | ||
|
|
1c5683ec78 | ||
|
|
9bee155d59 | ||
|
|
f34b05f4de | ||
|
|
97ce25f4ce | ||
|
|
3b6cd22e32 | ||
|
|
c9c35780d2 | ||
|
|
17f401f374 | ||
|
|
c6c54b3282 | ||
|
|
91455b6860 | ||
|
|
9e14c14a26 | ||
|
|
f16f243c22 | ||
|
|
fe601308e7 | ||
|
|
c507880d8f | ||
|
|
3f8328bbf8 | ||
|
|
e41f6a71b7 | ||
|
|
51f1be7f5b | ||
|
|
c10a5f9ef6 | ||
|
|
3c141de695 | ||
|
|
db263b696c | ||
|
|
f57b855d74 | ||
|
|
da2b9455f2 | ||
|
|
86525d8583 | ||
|
|
c41e52f57a | ||
|
|
55772a0d07 | ||
|
|
965a9e89ac | ||
|
|
51ee06429b | ||
|
|
cb622488c0 | ||
|
|
32f971fec6 | ||
|
|
ca85d09f02 | ||
|
|
8dea76baa4 | ||
|
|
299fbe04c4 | ||
|
|
57fc1df7d7 | ||
|
|
8d266d3941 | ||
|
|
a865b4da1c | ||
|
|
e59f5f3b01 | ||
|
|
8729688feb | ||
|
|
c8b06e7de1 | ||
|
|
eaba76f9e6 | ||
|
|
cb702cc238 | ||
|
|
f1f798bb85 | ||
|
|
c3fd52c177 | ||
|
|
b69b4a0a4a | ||
|
|
50d6072a73 | ||
|
|
d24cd50e61 | ||
|
|
85bff20ae5 | ||
|
|
737fab5471 | ||
|
|
e6592e93a9 | ||
|
|
5a6c4e8ae0 | ||
|
|
9f5875158c | ||
|
|
c3dc33c861 | ||
|
|
7420f47658 | ||
|
|
6be8f2124c | ||
|
|
edfed06001 | ||
|
|
1c646dba91 | ||
|
|
6781068058 | ||
|
|
cfe57c1dfe | ||
|
|
c34d09a971 | ||
|
|
ebd90c4742 | ||
|
|
ba52d34828 | ||
|
|
2f869b3cfc | ||
|
|
ffa21c27a7 | ||
|
|
1b6312afb3 | ||
|
|
bf32dc2e72 | ||
|
|
a15d65f7a2 | ||
|
|
2de8488855 | ||
|
|
129aa4bfaa | ||
|
|
b1d70db63b | ||
|
|
f03c3aafe4 | ||
|
|
51a9f106d1 | ||
|
|
bfc048e3fe | ||
|
|
83418644f7 | ||
|
|
dbc9dd5bfc | ||
|
|
5c480cf883 | ||
|
|
45ab15d4b5 | ||
|
|
adc64e7866 | ||
|
|
4d4a1cfe82 | ||
|
|
f2c7da3705 | ||
|
|
3ab0a82cd3 | ||
|
|
a46d772147 | ||
|
|
f3c50318e8 | ||
|
|
e7aa924c0e | ||
|
|
5266f04970 | ||
|
|
db957cf191 | ||
|
|
8ac514363d | ||
|
|
c2ea68cca4 | ||
|
|
3d86881ce7 | ||
|
|
697d1470f4 | ||
|
|
0b5f8f4051 | ||
|
|
0fed78fbcc | ||
|
|
8c38ef726b | ||
|
|
2399d90334 | ||
|
|
6367d68d1e | ||
|
|
155a84c8a3 | ||
|
|
10558c9eff | ||
|
|
dd30d811e6 | ||
|
|
293d8e4ddb | ||
|
|
77875c9133 | ||
|
|
647b47567e | ||
|
|
b0a1ad3b06 | ||
|
|
1d141bf2e8 | ||
|
|
0d0e279ae2 | ||
|
|
5dc0cee28a | ||
|
|
c15947da56 | ||
|
|
9bc04244e7 | ||
|
|
38c7a27010 | ||
|
|
58741d2791 | ||
|
|
8426470506 | ||
|
|
ccc3280b1a | ||
|
|
2847075705 | ||
|
|
3108ca0549 | ||
|
|
3b849ff497 | ||
|
|
66776b6a85 | ||
|
|
c8c241b50d | ||
|
|
44cb588371 | ||
|
|
6f91b8f8d1 | ||
|
|
3d93379132 | ||
|
|
84fd7d0126 | ||
|
|
7f52287aae | ||
|
|
98b8986868 | ||
|
|
250f2842ee | ||
|
|
9eca1a3a0c | ||
|
|
24b7a03224 | ||
|
|
9007097d24 | ||
|
|
bc445ec6a2 | ||
|
|
4fa0ae521e | ||
|
|
7bdf5fa8b8 | ||
|
|
65b0b976d9 | ||
|
|
a0d275feec | ||
|
|
ece3a8d7be | ||
|
|
463acf51b5 | ||
|
|
1cd16fab87 | ||
|
|
add55c4f33 | ||
|
|
51a9c0ff59 | ||
|
|
6e8a5f0f4e | ||
|
|
8a33702f26 | ||
|
|
a072d49802 | ||
|
|
a0aeeb8e07 | ||
|
|
383b225690 | ||
|
|
ace2247800 | ||
|
|
6a6fed5dce | ||
|
|
1f8aece8cd | ||
|
|
6c6f8cd4f9 | ||
|
|
fb1311e013 | ||
|
|
ce31acf030 | ||
|
|
31ad5ac63b | ||
|
|
1ede0bdec4 | ||
|
|
aef32ead2c | ||
|
|
5b43ec7f73 | ||
|
|
1e9ff88a00 | ||
|
|
bb9bb5f5c5 | ||
|
|
c533abd8b6 | ||
|
|
bb9bc764bc | ||
|
|
b4b53a6cb7 | ||
|
|
9c0204906c | ||
|
|
4670b373c1 | ||
|
|
f03b5883bd | ||
|
|
f8b2fe4dd5 | ||
|
|
be4a0c9c2b | ||
|
|
f37d52d8e9 | ||
|
|
177cdaf550 | ||
|
|
1573a443b7 | ||
|
|
911c0466c0 | ||
|
|
b6a95f9970 |
@@ -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(
|
||||
|
||||
@@ -1272,6 +1272,39 @@
|
||||
# Example:
|
||||
# owner_reserve = 2000000 # 2 XRP
|
||||
#
|
||||
# extension_compute_limit = <gas>
|
||||
#
|
||||
# The extension compute limit is the maximum amount of gas that can be
|
||||
# consumed by a single transaction. The gas limit is used to prevent
|
||||
# transactions from consuming too many resources.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
# extension_compute_limit = 1000000 # 1 million gas
|
||||
#
|
||||
# extension_size_limit = <bytes>
|
||||
#
|
||||
# The extension size limit is the maximum size of a WASM extension in
|
||||
# bytes. The size limit is used to prevent extensions from consuming
|
||||
# too many resources.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
# extension_size_limit = 100000 # 100 kb
|
||||
#
|
||||
# gas_price = <bytes>
|
||||
#
|
||||
# The gas price is the conversion between WASM gas and its price in drops.
|
||||
#
|
||||
# If this parameter is unspecified, xrpld will use an internal
|
||||
# default. Don't change this without understanding the consequences.
|
||||
#
|
||||
# Example:
|
||||
# gas_price = 1000000 # 1 drop per gas
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# 9. Misc Settings
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@@ -238,6 +239,9 @@ public:
|
||||
virtual DatabaseCon&
|
||||
getWalletDB() = 0;
|
||||
|
||||
virtual Fees
|
||||
getFees() const = 0;
|
||||
|
||||
// Temporary: Get the underlying Application for functions that haven't
|
||||
// been migrated yet. This should be removed once all code is migrated.
|
||||
virtual Application&
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,18 @@ public:
|
||||
deliver_ = amount;
|
||||
}
|
||||
|
||||
void
|
||||
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||
{
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
void
|
||||
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
/** Get the number of modified entries
|
||||
*/
|
||||
std::size_t
|
||||
@@ -72,6 +84,8 @@ public:
|
||||
|
||||
private:
|
||||
std::optional<STAmount> deliver_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -52,6 +52,8 @@ public:
|
||||
TER ter,
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
std::optional<std::uint32_t> const& gasUsed,
|
||||
std::optional<std::int32_t> const& wasmReturnCode,
|
||||
bool isDryRun,
|
||||
beast::Journal j);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -225,4 +227,15 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
journal);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static uint32_t
|
||||
calculateAdditionalReserve(T const& finishFunction)
|
||||
{
|
||||
if (!finishFunction)
|
||||
return 1;
|
||||
// First 500 bytes included in the normal reserve
|
||||
// Each additional 500 bytes requires an additional reserve
|
||||
return 1 + (finishFunction->size() / 500);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -80,6 +80,7 @@ authorizeMPToken(
|
||||
* requireAuth check is recursive for MPT shares in a vault, descending to
|
||||
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
|
||||
* purely defensive, as we currently do not allow such vaults to be created.
|
||||
* WeakAuth intentionally allows missing MPTokens under MPToken V2.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
requireAuth(
|
||||
@@ -114,6 +115,12 @@ canTransfer(
|
||||
AccountID const& from,
|
||||
AccountID const& to);
|
||||
|
||||
/** Check if Asset can be traded on DEX. return tecNO_PERMISSION
|
||||
* if it doesn't and tesSUCCESS otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTrade(ReadView const& view, Asset const& asset);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Empty holding operations (MPT-specific)
|
||||
@@ -164,4 +171,73 @@ createMPToken(
|
||||
AccountID const& account,
|
||||
std::uint32_t const flags);
|
||||
|
||||
TER
|
||||
checkCreateMPT(
|
||||
xrpl::ApplyView& view,
|
||||
xrpl::MPTIssue const& mptIssue,
|
||||
xrpl::AccountID const& holder,
|
||||
beast::Journal j);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// MPT Overflow related
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// MaximumAmount doesn't exceed 2**63-1
|
||||
std::int64_t
|
||||
maxMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
// OutstandingAmount may overflow and available amount might be negative.
|
||||
// But available amount is always <= |MaximumAmount - OutstandingAmount|.
|
||||
std::int64_t
|
||||
availableMPTAmount(SLE const& sleIssuance);
|
||||
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID);
|
||||
|
||||
/** Checks for two types of OutstandingAmount overflow during a send operation.
|
||||
* 1. **Direct directSendNoFee (Overflow: No):** A true overflow check when
|
||||
* `OutstandingAmount > MaximumAmount`. This threshold is used for direct
|
||||
* directSendNoFee transactions that bypass the payment engine.
|
||||
* 2. **accountSend & Payment Engine (Overflow: Yes):** A temporary overflow
|
||||
* check when `OutstandingAmount > UINT64_MAX`. This higher threshold is used
|
||||
* for `accountSend` and payments processed via the payment engine.
|
||||
*/
|
||||
bool
|
||||
isMPTOverflow(
|
||||
std::int64_t sendAmount,
|
||||
std::uint64_t outstandingAmount,
|
||||
std::int64_t maximumAmount,
|
||||
AllowMPTOverflow allowOverflow);
|
||||
|
||||
/**
|
||||
* Determine funds available for an issuer to sell in an issuer owned offer.
|
||||
* Issuing step, which could be either MPTEndPointStep last step or BookStep's
|
||||
* TakerPays may overflow OutstandingAmount. Redeeming step, in BookStep's
|
||||
* TakerGets redeems the offer's owner funds, essentially balancing out
|
||||
* the overflow, unless the offer's owner is the issuer.
|
||||
*/
|
||||
[[nodiscard]] STAmount
|
||||
issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue);
|
||||
|
||||
/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer.
|
||||
* See ApplyView::issuerSelfDebitHookMPT().
|
||||
*/
|
||||
void
|
||||
issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// MPT DEX
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* Return true if a transaction is allowed for the specified MPT/account. The
|
||||
* function checks MPTokenIssuance and MPToken objects flags to determine if the
|
||||
* transaction is allowed.
|
||||
*/
|
||||
TER
|
||||
checkMPTTxAllowed(ReadView const& v, TxType tx, Asset const& asset, AccountID const& accountID);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -252,4 +252,14 @@ deleteAMMTrustLine(
|
||||
std::optional<AccountID> const& ammAccountID,
|
||||
beast::Journal j);
|
||||
|
||||
/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior
|
||||
* call to view.peek().
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
deleteAMMMPToken(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE> sleMPT,
|
||||
AccountID const& ammAccountID,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,6 +31,9 @@ enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE };
|
||||
|
||||
enum class WaiveTransferFee : bool { No = false, Yes };
|
||||
|
||||
/** Controls whether accountSend is allowed to overflow OutstandingAmount **/
|
||||
enum class AllowMPTOverflow : bool { No = false, Yes };
|
||||
|
||||
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
|
||||
* - StrongAuth - before checking if authorization is required
|
||||
* - WeakAuth
|
||||
@@ -176,6 +179,16 @@ accountFunds(
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal j);
|
||||
|
||||
// Overload with AuthHandling to support IOU and MPT.
|
||||
[[nodiscard]] STAmount
|
||||
accountFunds(
|
||||
ReadView const& view,
|
||||
AccountID const& id,
|
||||
STAmount const& saDefault,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
beast::Journal j);
|
||||
|
||||
/** Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
* @param amount The amount to transfer
|
||||
@@ -257,7 +270,8 @@ accountSend(
|
||||
AccountID const& to,
|
||||
STAmount const& saAmount,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee = WaiveTransferFee::No);
|
||||
WaiveTransferFee waiveFee = WaiveTransferFee::No,
|
||||
AllowMPTOverflow allowOverflow = AllowMPTOverflow::No);
|
||||
|
||||
using MultiplePaymentDestinations = std::vector<std::pair<AccountID, Number>>;
|
||||
/** Like accountSend, except one account is sending multiple payments (with the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
@@ -31,12 +31,12 @@ class Rules;
|
||||
/** Calculate Liquidity Provider Token (LPT) Currency.
|
||||
*/
|
||||
Currency
|
||||
ammLPTCurrency(Currency const& cur1, Currency const& cur2);
|
||||
ammLPTCurrency(Asset const& asset1, Asset const& asset2);
|
||||
|
||||
/** Calculate LPT Issue from AMM asset pair.
|
||||
*/
|
||||
Issue
|
||||
ammLPTIssue(Currency const& cur1, Currency const& cur2, AccountID const& ammAccountID);
|
||||
ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID);
|
||||
|
||||
/** Validate the amount.
|
||||
* If validZero is false and amount is beast::zero then invalid amount.
|
||||
@@ -46,19 +46,19 @@ ammLPTIssue(Currency const& cur1, Currency const& cur2, AccountID const& ammAcco
|
||||
NotTEC
|
||||
invalidAMMAmount(
|
||||
STAmount const& amount,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt,
|
||||
bool validZero = false);
|
||||
|
||||
NotTEC
|
||||
invalidAMMAsset(
|
||||
Issue const& issue,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt);
|
||||
Asset const& asset,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt);
|
||||
|
||||
NotTEC
|
||||
invalidAMMAssetPair(
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
std::optional<std::pair<Issue, Issue>> const& pair = std::nullopt);
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
std::optional<std::pair<Asset, Asset>> const& pair = std::nullopt);
|
||||
|
||||
/** Get time slot of the auction slot.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
@@ -9,11 +10,12 @@
|
||||
namespace xrpl {
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(IOUAmount const& iou, Issue const& iss)
|
||||
toSTAmount(IOUAmount const& iou, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(asset.holds<Issue>(), "xrpl::toSTAmount : is Issue");
|
||||
bool const isNeg = iou.signum() < 0;
|
||||
std::uint64_t const umant = isNeg ? -iou.mantissa() : iou.mantissa();
|
||||
return STAmount(iss, umant, iou.exponent(), isNeg, STAmount::unchecked());
|
||||
return STAmount(asset, umant, iou.exponent(), isNeg, STAmount::unchecked());
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
@@ -31,12 +33,25 @@ toSTAmount(XRPAmount const& xrp)
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(XRPAmount const& xrp, Issue const& iss)
|
||||
toSTAmount(XRPAmount const& xrp, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(isXRP(iss.account) && isXRP(iss.currency), "xrpl::toSTAmount : is XRP");
|
||||
XRPL_ASSERT(isXRP(asset), "xrpl::toSTAmount : is XRP");
|
||||
return toSTAmount(xrp);
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(MPTAmount const& mpt)
|
||||
{
|
||||
return STAmount(mpt, noMPT());
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(MPTAmount const& mpt, Asset const& asset)
|
||||
{
|
||||
XRPL_ASSERT(asset.holds<MPTIssue>(), "xrpl::toSTAmount : is MPT");
|
||||
return STAmount(mpt, asset.get<MPTIssue>());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(STAmount const& amt) = delete;
|
||||
@@ -76,6 +91,21 @@ toAmount<XRPAmount>(STAmount const& amt)
|
||||
return XRPAmount(sMant);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline MPTAmount
|
||||
toAmount<MPTAmount>(STAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amt.holds<MPTIssue>() && amt.mantissa() <= maxMPTokenAmount && amt.exponent() == 0,
|
||||
"xrpl::toAmount<MPTAmount> : maximum mantissa");
|
||||
if (amt.mantissa() > maxMPTokenAmount || amt.exponent() != 0)
|
||||
Throw<std::runtime_error>("toAmount<MPTAmount>: invalid mantissa or exponent");
|
||||
bool const isNeg = amt.negative();
|
||||
std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
|
||||
|
||||
return MPTAmount(sMant);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(IOUAmount const& amt) = delete;
|
||||
@@ -98,23 +128,36 @@ toAmount<XRPAmount>(XRPAmount const& amt)
|
||||
return amt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T
|
||||
toAmount(MPTAmount const& amt) = delete;
|
||||
|
||||
template <>
|
||||
inline MPTAmount
|
||||
toAmount<MPTAmount>(MPTAmount const& amt)
|
||||
{
|
||||
return amt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
toAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
toAmount(Asset const& asset, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
{
|
||||
saveNumberRoundMode const rm(Number::getround());
|
||||
if (isXRP(issue))
|
||||
if (isXRP(asset))
|
||||
Number::setround(mode);
|
||||
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return IOUAmount(n);
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return XRPAmount(static_cast<std::int64_t>(n));
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return MPTAmount(static_cast<std::int64_t>(n));
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(n));
|
||||
return STAmount(issue, n);
|
||||
if (isXRP(asset))
|
||||
return STAmount(asset, static_cast<std::int64_t>(n));
|
||||
return STAmount(asset, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -125,17 +168,23 @@ toAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Numbe
|
||||
|
||||
template <typename T>
|
||||
T
|
||||
toMaxAmount(Issue const& issue)
|
||||
toMaxAmount(Asset const& asset)
|
||||
{
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return IOUAmount(STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return XRPAmount(static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return MPTAmount(maxMPTokenAmount);
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
return STAmount(issue, STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
return asset.visit(
|
||||
[](Issue const& issue) {
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(STAmount::cMaxNativeN));
|
||||
return STAmount(issue, STAmount::cMaxValue, STAmount::cMaxOffset);
|
||||
},
|
||||
[](MPTIssue const& issue) { return STAmount(issue, maxMPTokenAmount); });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,21 +194,23 @@ toMaxAmount(Issue const& issue)
|
||||
}
|
||||
|
||||
inline STAmount
|
||||
toSTAmount(Issue const& issue, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
toSTAmount(Asset const& asset, Number const& n, Number::rounding_mode mode = Number::getround())
|
||||
{
|
||||
return toAmount<STAmount>(issue, n, mode);
|
||||
return toAmount<STAmount>(asset, n, mode);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Issue
|
||||
getIssue(T const& amt)
|
||||
Asset
|
||||
getAsset(T const& amt)
|
||||
{
|
||||
if constexpr (std::is_same_v<IOUAmount, T>)
|
||||
return noIssue();
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return xrpIssue();
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return noMPT();
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
return amt.issue();
|
||||
return amt.asset();
|
||||
else
|
||||
{
|
||||
constexpr bool alwaysFalse = !std::is_same_v<T, T>;
|
||||
@@ -175,6 +226,8 @@ get(STAmount const& a)
|
||||
return a.iou();
|
||||
else if constexpr (std::is_same_v<XRPAmount, T>)
|
||||
return a.xrp();
|
||||
else if constexpr (std::is_same_v<MPTAmount, T>)
|
||||
return a.mpt();
|
||||
else if constexpr (std::is_same_v<STAmount, T>)
|
||||
return a;
|
||||
else
|
||||
|
||||
@@ -2,20 +2,37 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Asset;
|
||||
class STAmount;
|
||||
|
||||
template <typename TIss>
|
||||
concept ValidIssueType = std::is_same_v<TIss, Issue> || std::is_same_v<TIss, MPTIssue>;
|
||||
template <typename T>
|
||||
requires(
|
||||
std::is_same_v<T, XRPAmount> || std::is_same_v<T, IOUAmount> ||
|
||||
std::is_same_v<T, MPTAmount>)
|
||||
struct AmountType
|
||||
{
|
||||
using amount_type = T;
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
concept AssetType = std::is_convertible_v<A, Asset> || std::is_convertible_v<A, Issue> ||
|
||||
std::is_convertible_v<A, MPTIssue> || std::is_convertible_v<A, MPTID>;
|
||||
/* Used to check for an asset with either badCurrency()
|
||||
* or MPT with 0 account.
|
||||
*/
|
||||
struct BadAsset
|
||||
{
|
||||
};
|
||||
|
||||
inline BadAsset const&
|
||||
badAsset()
|
||||
{
|
||||
static BadAsset const a;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Asset is an abstraction of three different issue types: XRP, IOU, MPT.
|
||||
* For historical reasons, two issue types XRP and IOU are wrapped in Issue
|
||||
@@ -26,6 +43,9 @@ class Asset
|
||||
{
|
||||
public:
|
||||
using value_type = std::variant<Issue, MPTIssue>;
|
||||
using token_type = std::variant<Currency, MPTID>;
|
||||
using AmtType =
|
||||
std::variant<AmountType<XRPAmount>, AmountType<IOUAmount>, AmountType<MPTAmount>>;
|
||||
|
||||
private:
|
||||
value_type issue_;
|
||||
@@ -69,36 +89,42 @@ public:
|
||||
constexpr value_type const&
|
||||
value() const;
|
||||
|
||||
constexpr token_type
|
||||
token() const;
|
||||
|
||||
void
|
||||
setJson(Json::Value& jv) const;
|
||||
|
||||
STAmount
|
||||
operator()(Number const&) const;
|
||||
|
||||
bool
|
||||
constexpr AmtType
|
||||
getAmountType() const;
|
||||
|
||||
// Custom, generic visit implementation
|
||||
template <typename... Visitors>
|
||||
constexpr auto
|
||||
visit(Visitors&&... visitors) const -> decltype(auto)
|
||||
{
|
||||
// Simple delegation to the reusable utility, passing the internal
|
||||
// variant data.
|
||||
return detail::visit(issue_, std::forward<Visitors>(visitors)...);
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
native() const
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return issue.native();
|
||||
if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
return false;
|
||||
},
|
||||
issue_);
|
||||
return visit(
|
||||
[&](Issue const& issue) { return issue.native(); },
|
||||
[&](MPTIssue const&) { return false; });
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return issue.native();
|
||||
if constexpr (std::is_same_v<TIss, MPTIssue>)
|
||||
return true;
|
||||
},
|
||||
issue_);
|
||||
return visit(
|
||||
[&](Issue const& issue) { return issue.native(); },
|
||||
[&](MPTIssue const&) { return true; });
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -110,6 +136,10 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Currency const& lhs, Asset const& rhs);
|
||||
|
||||
// rhs is either badCurrency() or MPT issuer is 0
|
||||
friend constexpr bool
|
||||
operator==(BadAsset const& lhs, Asset const& rhs);
|
||||
|
||||
/** Return true if both assets refer to the same currency (regardless of
|
||||
* issuer) or MPT issuance. Otherwise return false.
|
||||
*/
|
||||
@@ -117,6 +147,12 @@ public:
|
||||
equalTokens(Asset const& lhs, Asset const& rhs);
|
||||
};
|
||||
|
||||
template <ValidIssueType TIss>
|
||||
constexpr bool is_issue_v = std::is_same_v<TIss, Issue>;
|
||||
|
||||
template <ValidIssueType TIss>
|
||||
constexpr bool is_mptissue_v = std::is_same_v<TIss, MPTIssue>;
|
||||
|
||||
inline Json::Value
|
||||
to_json(Asset const& asset)
|
||||
{
|
||||
@@ -156,6 +192,29 @@ Asset::value() const
|
||||
return issue_;
|
||||
}
|
||||
|
||||
constexpr Asset::token_type
|
||||
Asset::token() const
|
||||
{
|
||||
return visit(
|
||||
[&](Issue const& issue) -> Asset::token_type { return issue.currency; },
|
||||
[&](MPTIssue const& issue) -> Asset::token_type { return issue.getMptID(); });
|
||||
}
|
||||
|
||||
constexpr Asset::AmtType
|
||||
Asset::getAmountType() const
|
||||
{
|
||||
return visit(
|
||||
[&](Issue const& issue) -> Asset::AmtType {
|
||||
constexpr AmountType<XRPAmount> xrp;
|
||||
constexpr AmountType<IOUAmount> iou;
|
||||
return native() ? AmtType(xrp) : AmtType(iou);
|
||||
},
|
||||
[&](MPTIssue const& issue) -> Asset::AmtType {
|
||||
constexpr AmountType<MPTAmount> mpt;
|
||||
return AmtType(mpt);
|
||||
});
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(Asset const& lhs, Asset const& rhs)
|
||||
{
|
||||
@@ -177,7 +236,7 @@ operator<=>(Asset const& lhs, Asset const& rhs)
|
||||
[]<ValidIssueType TLhs, ValidIssueType TRhs>(TLhs const& lhs_, TRhs const& rhs_) {
|
||||
if constexpr (std::is_same_v<TLhs, TRhs>)
|
||||
return std::weak_ordering(lhs_ <=> rhs_);
|
||||
else if constexpr (std::is_same_v<TLhs, Issue> && std::is_same_v<TRhs, MPTIssue>)
|
||||
else if constexpr (is_issue_v<TLhs> && is_mptissue_v<TRhs>)
|
||||
return std::weak_ordering::greater;
|
||||
else
|
||||
return std::weak_ordering::less;
|
||||
@@ -189,7 +248,17 @@ operator<=>(Asset const& lhs, Asset const& rhs)
|
||||
constexpr bool
|
||||
operator==(Currency const& lhs, Asset const& rhs)
|
||||
{
|
||||
return rhs.holds<Issue>() && rhs.get<Issue>().currency == lhs;
|
||||
return rhs.visit(
|
||||
[&](Issue const& issue) { return issue.currency == lhs; },
|
||||
[](MPTIssue const& issue) { return false; });
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(BadAsset const&, Asset const& rhs)
|
||||
{
|
||||
return rhs.visit(
|
||||
[](Issue const& issue) -> bool { return badCurrency() == issue.currency; },
|
||||
[](MPTIssue const& issue) -> bool { return issue.getIssuer() == xrpAccount(); });
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
@@ -223,4 +292,36 @@ validJSONAsset(Json::Value const& jv);
|
||||
Asset
|
||||
assetFromJson(Json::Value const& jv);
|
||||
|
||||
Json::Value
|
||||
to_json(Asset const& asset);
|
||||
|
||||
inline bool
|
||||
isConsistent(Asset const& asset)
|
||||
{
|
||||
return asset.visit(
|
||||
[](Issue const& issue) { return isConsistent(issue); },
|
||||
[](MPTIssue const&) { return true; });
|
||||
}
|
||||
|
||||
inline bool
|
||||
validAsset(Asset const& asset)
|
||||
{
|
||||
return asset.visit(
|
||||
[](Issue const& issue) { return isConsistent(issue) && issue.currency != badCurrency(); },
|
||||
[](MPTIssue const& issue) { return issue.getIssuer() != xrpAccount(); });
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, Asset const& r)
|
||||
{
|
||||
using beast::hash_append;
|
||||
r.visit(
|
||||
[&](Issue const& issue) { hash_append(h, issue); },
|
||||
[&](MPTIssue const& issue) { hash_append(h, issue); });
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Asset const& x);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
@@ -15,15 +15,15 @@ namespace xrpl {
|
||||
class Book final : public CountedObject<Book>
|
||||
{
|
||||
public:
|
||||
Issue in;
|
||||
Issue out;
|
||||
Asset in;
|
||||
Asset out;
|
||||
std::optional<uint256> domain;
|
||||
|
||||
Book()
|
||||
{
|
||||
}
|
||||
|
||||
Book(Issue const& in_, Issue const& out_, std::optional<uint256> const& domain_)
|
||||
Book(Asset const& in_, Asset const& out_, std::optional<uint256> const& domain_)
|
||||
: in(in_), out(out_), domain(domain_)
|
||||
{
|
||||
}
|
||||
@@ -112,16 +112,67 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::MPTIssue> : private boost::base_from_member<std::hash<xrpl::MPTID>, 0>
|
||||
{
|
||||
private:
|
||||
using id_hash_type = boost::base_from_member<std::hash<xrpl::MPTID>, 0>;
|
||||
|
||||
public:
|
||||
explicit hash() = default;
|
||||
|
||||
using value_type = std::size_t;
|
||||
using argument_type = xrpl::MPTIssue;
|
||||
|
||||
value_type
|
||||
operator()(argument_type const& value) const
|
||||
{
|
||||
value_type const result(id_hash_type::member(value.getMptID()));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Asset>
|
||||
{
|
||||
private:
|
||||
using value_type = std::size_t;
|
||||
using argument_type = xrpl::Asset;
|
||||
|
||||
using issue_hasher = std::hash<xrpl::Issue>;
|
||||
using mptissue_hasher = std::hash<xrpl::MPTIssue>;
|
||||
|
||||
issue_hasher m_issue_hasher;
|
||||
mptissue_hasher m_mptissue_hasher;
|
||||
|
||||
public:
|
||||
explicit hash() = default;
|
||||
|
||||
value_type
|
||||
operator()(argument_type const& asset) const
|
||||
{
|
||||
return asset.visit(
|
||||
[&](xrpl::Issue const& issue) {
|
||||
value_type const result(m_issue_hasher(issue));
|
||||
return result;
|
||||
},
|
||||
[&](xrpl::MPTIssue const& issue) {
|
||||
value_type const result(m_mptissue_hasher(issue));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Book>
|
||||
{
|
||||
private:
|
||||
using issue_hasher = std::hash<xrpl::Issue>;
|
||||
using asset_hasher = std::hash<xrpl::Asset>;
|
||||
using uint256_hasher = xrpl::uint256::hasher;
|
||||
|
||||
issue_hasher m_issue_hasher;
|
||||
asset_hasher m_asset_hasher;
|
||||
uint256_hasher m_uint256_hasher;
|
||||
|
||||
public:
|
||||
@@ -133,8 +184,8 @@ public:
|
||||
value_type
|
||||
operator()(argument_type const& value) const
|
||||
{
|
||||
value_type result(m_issue_hasher(value.in));
|
||||
boost::hash_combine(result, m_issue_hasher(value.out));
|
||||
value_type result(m_asset_hasher(value.in));
|
||||
boost::hash_combine(result, m_asset_hasher(value.out));
|
||||
|
||||
if (value.domain)
|
||||
boost::hash_combine(result, m_uint256_hasher(*value.domain));
|
||||
@@ -159,6 +210,22 @@ struct hash<xrpl::Issue> : std::hash<xrpl::Issue>
|
||||
// using Base::Base; // inherit ctors
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::MPTIssue> : std::hash<xrpl::MPTIssue>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::MPTIssue>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Asset> : std::hash<xrpl::Asset>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::Asset>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<xrpl::Book> : std::hash<xrpl::Book>
|
||||
{
|
||||
|
||||
86
include/xrpl/protocol/Concepts.h
Normal file
86
include/xrpl/protocol/Concepts.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class STAmount;
|
||||
class Asset;
|
||||
class Issue;
|
||||
class MPTIssue;
|
||||
class IOUAmount;
|
||||
class XRPAmount;
|
||||
class MPTAmount;
|
||||
|
||||
template <typename A>
|
||||
concept StepAmount =
|
||||
std::is_same_v<A, XRPAmount> || std::is_same_v<A, IOUAmount> || std::is_same_v<A, MPTAmount>;
|
||||
|
||||
template <typename TIss>
|
||||
concept ValidIssueType = std::is_same_v<TIss, Issue> || std::is_same_v<TIss, MPTIssue>;
|
||||
|
||||
template <typename A>
|
||||
concept AssetType = std::is_convertible_v<A, Asset> || std::is_convertible_v<A, Issue> ||
|
||||
std::is_convertible_v<A, MPTIssue> || std::is_convertible_v<A, MPTID>;
|
||||
|
||||
template <typename T>
|
||||
concept ValidPathAsset = (std::is_same_v<T, Currency> || std::is_same_v<T, MPTID>);
|
||||
|
||||
template <class TTakerPays, class TTakerGets>
|
||||
concept ValidTaker =
|
||||
((std::is_same_v<TTakerPays, IOUAmount> || std::is_same_v<TTakerPays, XRPAmount> ||
|
||||
std::is_same_v<TTakerPays, MPTAmount>) &&
|
||||
(std::is_same_v<TTakerGets, IOUAmount> || std::is_same_v<TTakerGets, XRPAmount> ||
|
||||
std::is_same_v<TTakerGets, MPTAmount>) &&
|
||||
(!std::is_same_v<TTakerPays, XRPAmount> || !std::is_same_v<TTakerGets, XRPAmount>));
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This template combines multiple callable objects (lambdas) into a single
|
||||
// object that std::visit can use for overload resolution.
|
||||
template <typename... Ts>
|
||||
struct CombineVisitors : Ts...
|
||||
{
|
||||
// Bring all operator() overloads from base classes into this scope.
|
||||
// It's the mechanism that makes the CombineVisitors struct function
|
||||
// as a single callable object with multiple overloads.
|
||||
using Ts::operator()...;
|
||||
|
||||
// Perfect forwarding constructor to correctly initialize the base class
|
||||
// lambdas
|
||||
constexpr CombineVisitors(Ts&&... ts) : Ts(std::forward<Ts>(ts))...
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// This function forces function template argument deduction, which is more
|
||||
// robust than class template argument deduction (CTAD) via the deduction guide.
|
||||
template <typename... Ts>
|
||||
constexpr CombineVisitors<std::decay_t<Ts>...>
|
||||
make_combine_visitors(Ts&&... ts)
|
||||
{
|
||||
// std::decay_t<Ts> is used to remove references/constness from the lambda
|
||||
// types before they are passed as template arguments to the CombineVisitors
|
||||
// struct.
|
||||
return CombineVisitors<std::decay_t<Ts>...>{std::forward<Ts>(ts)...};
|
||||
}
|
||||
|
||||
// This function takes ANY variant and ANY number of visitors, and performs the
|
||||
// visit. It is the reusable core logic.
|
||||
template <typename Variant, typename... Visitors>
|
||||
constexpr auto
|
||||
visit(Variant&& v, Visitors&&... visitors) -> decltype(auto)
|
||||
{
|
||||
// Use the function template helper instead of raw CTAD.
|
||||
auto visitor_set = make_combine_visitors(std::forward<Visitors>(visitors)...);
|
||||
|
||||
// Delegate to std::visit, perfectly forwarding the variant and the visitor
|
||||
// set.
|
||||
return std::visit(visitor_set, std::forward<Variant>(v));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -8,6 +8,9 @@ namespace xrpl {
|
||||
// This was the reference fee units used in the old fee calculation.
|
||||
inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10;
|
||||
|
||||
// Number of micro-drops in one drop.
|
||||
constexpr std::uint32_t MICRO_DROPS_PER_DROP{1'000'000};
|
||||
|
||||
/** Reflects the fee settings for a particular ledger.
|
||||
|
||||
The fees are always the same for any transactions applied
|
||||
@@ -24,6 +27,15 @@ struct Fees
|
||||
/** @brief Additional XRP reserve required per owned ledger object. */
|
||||
XRPAmount increment{0};
|
||||
|
||||
/** @brief Compute limit for Feature Extensions (instructions). */
|
||||
std::uint32_t extensionComputeLimit{0};
|
||||
|
||||
/** @brief Size limit for Feature Extensions (bytes). */
|
||||
std::uint32_t extensionSizeLimit{0};
|
||||
|
||||
/** @brief Price of WASM gas (micro-drops). */
|
||||
std::uint32_t gasPrice{0};
|
||||
|
||||
explicit Fees() = default;
|
||||
Fees(Fees const&) = default;
|
||||
Fees&
|
||||
|
||||
@@ -207,6 +207,12 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept
|
||||
Keylet
|
||||
escrow(AccountID const& src, std::uint32_t seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
escrow(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltESCROW, key};
|
||||
}
|
||||
|
||||
/** A PaymentChannel */
|
||||
Keylet
|
||||
payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept;
|
||||
|
||||
@@ -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,10 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_MPT,
|
||||
temBAD_WASM,
|
||||
temTEMP_DISABLED,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -165,6 +169,8 @@ enum TEFcodes : TERUnderlyingType {
|
||||
tefNO_TICKET,
|
||||
tefNFTOKEN_IS_NOT_TRANSFERABLE,
|
||||
tefINVALID_LEDGER_FIX_TYPE,
|
||||
tefNO_WASM,
|
||||
tefWASM_FIELD_NOT_INCLUDED,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -208,6 +214,7 @@ enum TERcodes : TERUnderlyingType {
|
||||
terADDRESS_COLLISION, // Failed to allocate AccountID when trying to
|
||||
// create a pseudo-account
|
||||
terNO_DELEGATE_PERMISSION, // Delegate does not have permission
|
||||
terLOCKED, // MPT is locked
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -346,6 +353,7 @@ enum TECcodes : TERUnderlyingType {
|
||||
// backward compatibility with historical data on non-prod networks, can be
|
||||
// reclaimed after those networks reset.
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
tecWASM_REJECTED = 199,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -84,6 +84,12 @@ public:
|
||||
|
||||
if (obj.isFieldPresent(sfParentBatchID))
|
||||
parentBatchID_ = obj.getFieldH256(sfParentBatchID);
|
||||
|
||||
if (obj.isFieldPresent(sfGasUsed))
|
||||
gasUsed_ = obj.getFieldU32(sfGasUsed);
|
||||
|
||||
if (obj.isFieldPresent(sfWasmReturnCode))
|
||||
wasmReturnCode_ = obj.getFieldI32(sfWasmReturnCode);
|
||||
}
|
||||
|
||||
std::optional<STAmount> const&
|
||||
@@ -104,6 +110,30 @@ public:
|
||||
parentBatchID_ = id;
|
||||
}
|
||||
|
||||
void
|
||||
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||
{
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
std::optional<std::uint32_t> const&
|
||||
getGasUsed() const
|
||||
{
|
||||
return gasUsed_;
|
||||
}
|
||||
|
||||
void
|
||||
setWasmReturnCode(std::optional<std::int32_t> const wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
std::optional<std::int32_t> const&
|
||||
getWasmReturnCode() const
|
||||
{
|
||||
return wasmReturnCode_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint256 transactionID_;
|
||||
std::uint32_t ledgerSeq_;
|
||||
@@ -112,6 +142,8 @@ private:
|
||||
|
||||
std::optional<STAmount> deliveredAmount_;
|
||||
std::optional<uint256> parentBatchID_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
|
||||
STArray nodes_;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(SmartEscrow, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(MPTokensV2, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -161,8 +161,10 @@ LEDGER_ENTRY(ltDIR_NODE, 0x0064, DirectoryNode, directory, ({
|
||||
{sfOwner, soeOPTIONAL}, // for owner directories
|
||||
{sfTakerPaysCurrency, soeOPTIONAL}, // order book directories
|
||||
{sfTakerPaysIssuer, soeOPTIONAL}, // order book directories
|
||||
{sfTakerPaysMPT, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsCurrency, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsIssuer, soeOPTIONAL}, // order book directories
|
||||
{sfTakerGetsMPT, soeOPTIONAL}, // order book directories
|
||||
{sfExchangeRate, soeOPTIONAL}, // order book directories
|
||||
{sfIndexes, soeREQUIRED},
|
||||
{sfRootIndex, soeREQUIRED},
|
||||
@@ -302,6 +304,11 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
|
||||
{sfBaseFeeDrops, soeOPTIONAL},
|
||||
{sfReserveBaseDrops, soeOPTIONAL},
|
||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||
// Smart Escrow fields
|
||||
{sfExtensionComputeLimit, soeOPTIONAL},
|
||||
{sfExtensionSizeLimit, soeOPTIONAL},
|
||||
{sfGasPrice, soeOPTIONAL},
|
||||
|
||||
{sfPreviousTxnID, soeOPTIONAL},
|
||||
{sfPreviousTxnLgrSeq, soeOPTIONAL},
|
||||
}))
|
||||
@@ -332,6 +339,8 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
{sfCondition, soeOPTIONAL},
|
||||
{sfCancelAfter, soeOPTIONAL},
|
||||
{sfFinishAfter, soeOPTIONAL},
|
||||
{sfFinishFunction, soeOPTIONAL},
|
||||
{sfData, soeOPTIONAL},
|
||||
{sfSourceTag, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
|
||||
@@ -113,6 +113,11 @@ TYPED_SFIELD(sfInterestRate, UINT32, 65) // 1/10 basis points (bi
|
||||
TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips)
|
||||
TYPED_SFIELD(sfExtensionComputeLimit, UINT32, 69)
|
||||
TYPED_SFIELD(sfExtensionSizeLimit, UINT32, 70)
|
||||
TYPED_SFIELD(sfGasPrice, UINT32, 71)
|
||||
TYPED_SFIELD(sfComputationAllowance, UINT32, 72)
|
||||
TYPED_SFIELD(sfGasUsed, UINT32, 73)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -159,6 +164,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)
|
||||
@@ -223,8 +230,9 @@ TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::sMD_NeedsAsset
|
||||
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
|
||||
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::sMD_NeedsAsset | SField::sMD_Default)
|
||||
|
||||
// int32
|
||||
// 32-bit signed (common)
|
||||
TYPED_SFIELD(sfLoanScale, INT32, 1)
|
||||
TYPED_SFIELD(sfWasmReturnCode, INT32, 2)
|
||||
|
||||
// currency amount (common)
|
||||
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
||||
@@ -254,7 +262,7 @@ TYPED_SFIELD(sfBaseFeeDrops, AMOUNT, 22)
|
||||
TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23)
|
||||
TYPED_SFIELD(sfReserveIncrementDrops, AMOUNT, 24)
|
||||
|
||||
// currency amount (AMM)
|
||||
// currency amount (more)
|
||||
TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25)
|
||||
TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26)
|
||||
TYPED_SFIELD(sfEPrice, AMOUNT, 27)
|
||||
@@ -296,6 +304,7 @@ TYPED_SFIELD(sfAssetClass, VL, 28)
|
||||
TYPED_SFIELD(sfProvider, VL, 29)
|
||||
TYPED_SFIELD(sfMPTokenMetadata, VL, 30)
|
||||
TYPED_SFIELD(sfCredentialType, VL, 31)
|
||||
TYPED_SFIELD(sfFinishFunction, VL, 32)
|
||||
|
||||
// account (common)
|
||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
Delegation::delegable,
|
||||
uint256{},
|
||||
createAcct,
|
||||
createAcct | mayCreateMPT,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
@@ -50,11 +50,13 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
{sfCancelAfter, soeOPTIONAL},
|
||||
{sfFinishAfter, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfFinishFunction, soeOPTIONAL},
|
||||
{sfData, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This transaction type completes an existing escrow. */
|
||||
@@ -71,6 +73,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||
{sfFulfillment, soeOPTIONAL},
|
||||
{sfCondition, soeOPTIONAL},
|
||||
{sfCredentialIDs, soeOPTIONAL},
|
||||
{sfComputationAllowance, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
|
||||
@@ -129,10 +132,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 +242,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
noPriv,
|
||||
({
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSendMax, soeREQUIRED},
|
||||
{sfSendMax, soeREQUIRED, soeMPTSupported},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfInvoiceID, soeOPTIONAL},
|
||||
@@ -252,11 +255,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 +412,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 +427,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 +443,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 +459,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 +478,8 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
featureAMM,
|
||||
noPriv,
|
||||
({
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAsset2, soeREQUIRED},
|
||||
{sfAsset, soeREQUIRED, soeMPTSupported},
|
||||
{sfAsset2, soeREQUIRED, soeMPTSupported},
|
||||
{sfTradingFee, soeREQUIRED},
|
||||
}))
|
||||
|
||||
@@ -489,8 +492,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 +506,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 */
|
||||
@@ -1110,6 +1113,10 @@ TRANSACTION(ttFEE, 101, SetFee,
|
||||
{sfBaseFeeDrops, soeOPTIONAL},
|
||||
{sfReserveBaseDrops, soeOPTIONAL},
|
||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||
// Smart Escrow fields
|
||||
{sfExtensionComputeLimit, soeOPTIONAL},
|
||||
{sfExtensionSizeLimit, soeOPTIONAL},
|
||||
{sfGasPrice, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This system-generated transaction type is used to update the network's negative UNL
|
||||
|
||||
@@ -248,6 +248,9 @@ JSS(expected_date); // out: any (warnings)
|
||||
JSS(expected_date_UTC); // out: any (warnings)
|
||||
JSS(expected_ledger_size); // out: TxQ
|
||||
JSS(expiration); // out: AccountOffers, AccountChannels, ValidatorList, amm_info
|
||||
JSS(extension_compute); // out: NetworkOPs
|
||||
JSS(extension_size); // out: NetworkOPs
|
||||
JSS(gas_price); // out: NetworkOPs
|
||||
JSS(fail_hard); // in: Sign, Submit
|
||||
JSS(failed); // out: InboundLedger
|
||||
JSS(feature); // in: Feature
|
||||
@@ -401,6 +404,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.
|
||||
|
||||
@@ -174,6 +174,54 @@ public:
|
||||
return this->sle_->isFieldPresent(sfFinishAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfFinishFunction (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getFinishFunction() const
|
||||
{
|
||||
if (hasFinishFunction())
|
||||
return this->sle_->at(sfFinishFunction);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfFinishFunction is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasFinishFunction() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfFinishFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfData (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getData() const
|
||||
{
|
||||
if (hasData())
|
||||
return this->sle_->at(sfData);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfData is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasData() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfSourceTag (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -451,6 +499,28 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfFinishFunction (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowBuilder&
|
||||
setFinishFunction(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfFinishFunction] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfData (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowBuilder&
|
||||
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfData] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfSourceTag (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -213,6 +213,78 @@ public:
|
||||
return this->sle_->isFieldPresent(sfReserveIncrementDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfExtensionComputeLimit (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getExtensionComputeLimit() const
|
||||
{
|
||||
if (hasExtensionComputeLimit())
|
||||
return this->sle_->at(sfExtensionComputeLimit);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfExtensionComputeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasExtensionComputeLimit() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfExtensionComputeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfExtensionSizeLimit (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getExtensionSizeLimit() const
|
||||
{
|
||||
if (hasExtensionSizeLimit())
|
||||
return this->sle_->at(sfExtensionSizeLimit);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfExtensionSizeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasExtensionSizeLimit() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfExtensionSizeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasPrice (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasPrice() const
|
||||
{
|
||||
if (hasGasPrice())
|
||||
return this->sle_->at(sfGasPrice);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasPrice is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasPrice() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfGasPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfPreviousTxnID (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -373,6 +445,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfExtensionComputeLimit (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setExtensionComputeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfExtensionComputeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfExtensionSizeLimit (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setExtensionSizeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfExtensionSizeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasPrice (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setGasPrice(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasPrice] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfPreviousTxnID (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&
|
||||
|
||||
@@ -58,6 +58,32 @@ public:
|
||||
return this->tx_->at(sfDestination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestinationTag (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getDestinationTag() const
|
||||
{
|
||||
if (hasDestinationTag())
|
||||
{
|
||||
return this->tx_->at(sfDestinationTag);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDestinationTag is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDestinationTag() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfDestinationTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
@@ -149,29 +175,55 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestinationTag (soeOPTIONAL)
|
||||
* @brief Get sfFinishFunction (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getDestinationTag() const
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getFinishFunction() const
|
||||
{
|
||||
if (hasDestinationTag())
|
||||
if (hasFinishFunction())
|
||||
{
|
||||
return this->tx_->at(sfDestinationTag);
|
||||
return this->tx_->at(sfFinishFunction);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDestinationTag is present.
|
||||
* @brief Check if sfFinishFunction is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDestinationTag() const
|
||||
hasFinishFunction() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfDestinationTag);
|
||||
return this->tx_->isFieldPresent(sfFinishFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfData (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getData() const
|
||||
{
|
||||
if (hasData())
|
||||
{
|
||||
return this->tx_->at(sfData);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfData is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasData() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfData);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -230,6 +282,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestinationTag (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestinationTag] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
@@ -276,13 +339,24 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestinationTag (soeOPTIONAL)
|
||||
* @brief Set sfFinishFunction (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
setFinishFunction(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestinationTag] = value;
|
||||
object_[sfFinishFunction] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfData (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfData] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,32 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfCredentialIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfComputationAllowance (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getComputationAllowance() const
|
||||
{
|
||||
if (hasComputationAllowance())
|
||||
{
|
||||
return this->tx_->at(sfComputationAllowance);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfComputationAllowance is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasComputationAllowance() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfComputationAllowance);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -247,6 +273,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfComputationAllowance (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowFinishBuilder&
|
||||
setComputationAllowance(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfComputationAllowance] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the EscrowFinish wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -21,7 +21,7 @@ class OfferCreateBuilder;
|
||||
* Type: ttOFFER_CREATE (7)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: uint256{}
|
||||
* Privileges: noPriv
|
||||
* Privileges: mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use OfferCreateBuilder to construct new transactions.
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerPays (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -60,6 +61,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfTakerGets (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -194,6 +196,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerPays (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
OfferCreateBuilder&
|
||||
@@ -205,6 +208,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfTakerGets (soeREQUIRED)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
OfferCreateBuilder&
|
||||
|
||||
@@ -21,7 +21,7 @@ class PaymentBuilder;
|
||||
* Type: ttPAYMENT (0)
|
||||
* Delegable: Delegation::delegable
|
||||
* Amendment: uint256{}
|
||||
* Privileges: createAcct
|
||||
* Privileges: createAcct | mayCreateMPT
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use PaymentBuilder to construct new transactions.
|
||||
|
||||
@@ -254,6 +254,84 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfReserveIncrementDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfExtensionComputeLimit (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getExtensionComputeLimit() const
|
||||
{
|
||||
if (hasExtensionComputeLimit())
|
||||
{
|
||||
return this->tx_->at(sfExtensionComputeLimit);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfExtensionComputeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasExtensionComputeLimit() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfExtensionComputeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfExtensionSizeLimit (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getExtensionSizeLimit() const
|
||||
{
|
||||
if (hasExtensionSizeLimit())
|
||||
{
|
||||
return this->tx_->at(sfExtensionSizeLimit);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfExtensionSizeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasExtensionSizeLimit() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfExtensionSizeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasPrice (soeOPTIONAL)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasPrice() const
|
||||
{
|
||||
if (hasGasPrice())
|
||||
{
|
||||
return this->tx_->at(sfGasPrice);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasPrice is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasPrice() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfGasPrice);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -384,6 +462,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfExtensionComputeLimit (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setExtensionComputeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfExtensionComputeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfExtensionSizeLimit (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setExtensionSizeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfExtensionSizeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasPrice (soeOPTIONAL)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setGasPrice(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasPrice] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the SetFee wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -75,6 +75,20 @@ public:
|
||||
view_->deliver(amount);
|
||||
}
|
||||
|
||||
/** Sets the gas used in the metadata */
|
||||
void
|
||||
setGasUsed(std::uint32_t const gasUsed)
|
||||
{
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
/** Sets the gas used in the metadata */
|
||||
void
|
||||
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
/** Discard changes and start fresh. */
|
||||
void
|
||||
discard();
|
||||
@@ -124,6 +138,8 @@ private:
|
||||
|
||||
// The ID of the batch transaction we are executing under, if seated.
|
||||
std::optional<uint256 const> parentBatchId_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -246,7 +246,7 @@ flow(
|
||||
EitherAmount stepIn(*strand[0]->cachedIn());
|
||||
for (auto i = 0; i < s; ++i)
|
||||
{
|
||||
bool valid;
|
||||
bool valid = false;
|
||||
std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
|
||||
if (!valid)
|
||||
{
|
||||
@@ -379,8 +379,10 @@ limitOut(
|
||||
return XRPAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
|
||||
return IOUAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, MPTAmount>)
|
||||
return MPTAmount{*out};
|
||||
else
|
||||
return STAmount{remainingOut.issue(), out->mantissa(), out->exponent()};
|
||||
return STAmount{remainingOut.asset(), out->mantissa(), out->exponent()};
|
||||
}();
|
||||
// A tiny difference could be due to the round off
|
||||
if (withinRelativeDistance(out, remainingOut, Number(1, -9)))
|
||||
@@ -534,7 +536,7 @@ public:
|
||||
@return Actual amount in and out from the strands, errors, and payment
|
||||
sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
template <StepAmount TInAmt, StepAmount TOutAmt>
|
||||
FlowResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(xrpl::PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ private:
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
Issue const& lptIssue,
|
||||
Asset const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
};
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ public:
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHanding,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
@@ -132,6 +133,7 @@ public:
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHandling,
|
||||
AuthHandling authHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
@@ -141,8 +143,8 @@ public:
|
||||
Sandbox& sb,
|
||||
std::shared_ptr<SLE> const ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
beast::Journal const& journal);
|
||||
|
||||
private:
|
||||
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
ApplyFlags const flags,
|
||||
AccountID const id,
|
||||
beast::Journal const j,
|
||||
Issue const& issue);
|
||||
Asset const& asset);
|
||||
|
||||
// Use the payment flow code to perform offer crossing.
|
||||
std::pair<TER, Amounts>
|
||||
|
||||
@@ -13,12 +13,21 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
|
||||
557
include/xrpl/tx/wasm/HostFunc.h
Normal file
557
include/xrpl/tx/wasm/HostFunc.h
Normal file
@@ -0,0 +1,557 @@
|
||||
#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
|
||||
|
||||
struct HostFunctions
|
||||
{
|
||||
beast::Journal j_;
|
||||
|
||||
HostFunctions(beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) : j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
virtual void
|
||||
setRT(void const*)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
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
|
||||
303
include/xrpl/tx/wasm/HostFuncImpl.h
Normal file
303
include/xrpl/tx/wasm/HostFuncImpl.h
Normal file
@@ -0,0 +1,303 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/wasm/HostFunc.h>
|
||||
|
||||
namespace xrpl {
|
||||
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 const* 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 const* rt) override
|
||||
{
|
||||
rt_ = rt;
|
||||
}
|
||||
|
||||
virtual void const*
|
||||
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
|
||||
328
include/xrpl/tx/wasm/HostFuncWrapper.h
Normal file
328
include/xrpl/tx/wasm/HostFuncWrapper.h
Normal file
@@ -0,0 +1,328 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/wasm/WasmiVM.h>
|
||||
|
||||
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
|
||||
231
include/xrpl/tx/wasm/ParamsHelper.h
Normal file
231
include/xrpl/tx/wasm/ParamsHelper.h
Normal file
@@ -0,0 +1,231 @@
|
||||
#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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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
|
||||
321
include/xrpl/tx/wasm/WasmiVM.h
Normal file
321
include/xrpl/tx/wasm/WasmiVM.h
Normal file
@@ -0,0 +1,321 @@
|
||||
#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*)>
|
||||
struct WasmVec
|
||||
{
|
||||
T vec_;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
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 const&
|
||||
getInstance(int i = 0) const;
|
||||
|
||||
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 const&
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ ApplyStateTable::apply(
|
||||
TER ter,
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
std::optional<std::uint32_t> const& gasUsed,
|
||||
std::optional<std::int32_t> const& wasmReturnCode,
|
||||
bool isDryRun,
|
||||
beast::Journal j)
|
||||
{
|
||||
@@ -103,6 +105,8 @@ ApplyStateTable::apply(
|
||||
|
||||
meta.setDeliveredAmount(deliver);
|
||||
meta.setParentBatchID(parentBatchId);
|
||||
meta.setGasUsed(gasUsed);
|
||||
meta.setWasmReturnCode(wasmReturnCode);
|
||||
|
||||
Mods newMod;
|
||||
for (auto& item : items_)
|
||||
|
||||
@@ -15,7 +15,8 @@ ApplyViewImpl::apply(
|
||||
bool isDryRun,
|
||||
beast::Journal j)
|
||||
{
|
||||
return items_.apply(to, tx, ter, deliver_, parentBatchId, isDryRun, j);
|
||||
return items_.apply(
|
||||
to, tx, ter, deliver_, parentBatchId, gasUsed_, wasmReturnCode_, isDryRun, j);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
|
||||
@@ -169,6 +169,12 @@ Ledger::Ledger(
|
||||
sle->at(sfReserveIncrement) = *f;
|
||||
sle->at(sfReferenceFeeUnits) = FEE_UNITS_DEPRECATED;
|
||||
}
|
||||
if (std::find(amendments.begin(), amendments.end(), featureSmartEscrow) != amendments.end())
|
||||
{
|
||||
sle->at(sfExtensionComputeLimit) = fees.extensionComputeLimit;
|
||||
sle->at(sfExtensionSizeLimit) = fees.extensionSizeLimit;
|
||||
sle->at(sfGasPrice) = fees.gasPrice;
|
||||
}
|
||||
rawInsert(sle);
|
||||
}
|
||||
|
||||
@@ -552,6 +558,7 @@ Ledger::setup()
|
||||
{
|
||||
bool oldFees = false;
|
||||
bool newFees = false;
|
||||
bool extensionFees = false;
|
||||
{
|
||||
auto const baseFee = sle->at(~sfBaseFee);
|
||||
auto const reserveBase = sle->at(~sfReserveBase);
|
||||
@@ -568,6 +575,7 @@ Ledger::setup()
|
||||
auto const baseFeeXRP = sle->at(~sfBaseFeeDrops);
|
||||
auto const reserveBaseXRP = sle->at(~sfReserveBaseDrops);
|
||||
auto const reserveIncrementXRP = sle->at(~sfReserveIncrementDrops);
|
||||
|
||||
auto assign = [&ret](XRPAmount& dest, std::optional<STAmount> const& src) {
|
||||
if (src)
|
||||
{
|
||||
@@ -586,6 +594,22 @@ Ledger::setup()
|
||||
assign(fees_.increment, reserveIncrementXRP);
|
||||
newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP;
|
||||
}
|
||||
{
|
||||
auto const extensionComputeLimit = sle->at(~sfExtensionComputeLimit);
|
||||
auto const extensionSizeLimit = sle->at(~sfExtensionSizeLimit);
|
||||
auto const gasPrice = sle->at(~sfGasPrice);
|
||||
|
||||
auto assign = [](std::uint32_t& dest, std::optional<std::uint32_t> const& src) {
|
||||
if (src)
|
||||
{
|
||||
dest = src.value();
|
||||
}
|
||||
};
|
||||
assign(fees_.extensionComputeLimit, extensionComputeLimit);
|
||||
assign(fees_.extensionSizeLimit, extensionSizeLimit);
|
||||
assign(fees_.gasPrice, gasPrice);
|
||||
extensionFees = extensionComputeLimit || extensionSizeLimit || gasPrice;
|
||||
}
|
||||
if (oldFees && newFees)
|
||||
{
|
||||
// Should be all of one or the other, but not both
|
||||
@@ -596,6 +620,12 @@ Ledger::setup()
|
||||
// Can't populate the new fees before the amendment is enabled
|
||||
ret = false;
|
||||
}
|
||||
if (!rules_.enabled(featureSmartEscrow) && extensionFees)
|
||||
{
|
||||
// Can't populate the extension fees before the amendment is
|
||||
// enabled
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SHAMapMissingNode const&)
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
auto
|
||||
DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency const& c) -> Key
|
||||
DeferredCredits::makeKeyIOU(AccountID const& a1, AccountID const& a2, Currency const& c) -> KeyIOU
|
||||
{
|
||||
if (a1 < a2)
|
||||
{
|
||||
@@ -19,21 +21,23 @@ DeferredCredits::makeKey(AccountID const& a1, AccountID const& a2, Currency cons
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::credit(
|
||||
DeferredCredits::creditIOU(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditSenderBalance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::credit : sender is not receiver");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::credit : positive amount");
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::creditIOU : sender is not receiver");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditIOU : positive amount");
|
||||
XRPL_ASSERT(
|
||||
amount.holds<Issue>(), "xrpl::detail::DeferredCredits::creditIOU : amount is for Issue");
|
||||
|
||||
auto const k = makeKey(sender, receiver, amount.getCurrency());
|
||||
auto i = credits_.find(k);
|
||||
if (i == credits_.end())
|
||||
auto const k = makeKeyIOU(sender, receiver, amount.get<Issue>().currency);
|
||||
auto i = creditsIOU_.find(k);
|
||||
if (i == creditsIOU_.end())
|
||||
{
|
||||
Value v;
|
||||
ValueIOU v;
|
||||
|
||||
if (sender < receiver)
|
||||
{
|
||||
@@ -48,7 +52,7 @@ DeferredCredits::credit(
|
||||
v.lowAcctOrigBalance = -preCreditSenderBalance;
|
||||
}
|
||||
|
||||
credits_[k] = v;
|
||||
creditsIOU_[k] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,6 +69,93 @@ DeferredCredits::credit(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::creditMPT(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amount.holds<MPTIssue>(),
|
||||
"xrpl::detail::DeferredCredits::creditMPT : amount is for MPTIssue");
|
||||
XRPL_ASSERT(!amount.negative(), "xrpl::detail::DeferredCredits::creditMPT : positive amount");
|
||||
XRPL_ASSERT(
|
||||
sender != receiver, "xrpl::detail::DeferredCredits::creditMPT : sender is not receiver");
|
||||
|
||||
auto const mptAmtVal = amount.mpt().value();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
bool const isSenderIssuer = sender == issuer;
|
||||
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
if (i == creditsMPT_.end())
|
||||
{
|
||||
IssuerValueMPT v;
|
||||
if (isSenderIssuer)
|
||||
{
|
||||
v.credit = mptAmtVal;
|
||||
v.holders[receiver].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.holders[sender].debit = mptAmtVal;
|
||||
v.holders[sender].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
v.origBalance = preCreditBalanceIssuer;
|
||||
creditsMPT_.emplace(mptID, std::move(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
// only record the balance the first time, do not record it here
|
||||
auto& v = i->second;
|
||||
if (isSenderIssuer)
|
||||
{
|
||||
v.credit += mptAmtVal;
|
||||
if (!v.holders.contains(receiver))
|
||||
{
|
||||
v.holders[receiver].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!v.holders.contains(sender))
|
||||
{
|
||||
v.holders[sender].debit = mptAmtVal;
|
||||
v.holders[sender].origBalance = preCreditBalanceHolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
v.holders[sender].debit += mptAmtVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::issuerSelfDebitMPT(
|
||||
MPTIssue const& issue,
|
||||
std::uint64_t amount,
|
||||
std::int64_t origBalance)
|
||||
{
|
||||
auto const& mptID = issue.getMptID();
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
|
||||
if (i == creditsMPT_.end())
|
||||
{
|
||||
IssuerValueMPT v;
|
||||
v.origBalance = origBalance;
|
||||
v.selfDebit = amount;
|
||||
creditsMPT_.emplace(mptID, std::move(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
i->second.selfDebit += amount;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next)
|
||||
{
|
||||
@@ -88,16 +179,16 @@ DeferredCredits::ownerCount(AccountID const& id) const
|
||||
|
||||
// Get the adjustments for the balance between main and other.
|
||||
auto
|
||||
DeferredCredits::adjustments(
|
||||
DeferredCredits::adjustmentsIOU(
|
||||
AccountID const& main,
|
||||
AccountID const& other,
|
||||
Currency const& currency) const -> std::optional<Adjustment>
|
||||
Currency const& currency) const -> std::optional<AdjustmentIOU>
|
||||
{
|
||||
std::optional<Adjustment> result;
|
||||
std::optional<AdjustmentIOU> result;
|
||||
|
||||
Key const k = makeKey(main, other, currency);
|
||||
auto i = credits_.find(k);
|
||||
if (i == credits_.end())
|
||||
KeyIOU const k = makeKeyIOU(main, other, currency);
|
||||
auto i = creditsIOU_.find(k);
|
||||
if (i == creditsIOU_.end())
|
||||
return result;
|
||||
|
||||
auto const& v = i->second;
|
||||
@@ -112,12 +203,21 @@ DeferredCredits::adjustments(
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
DeferredCredits::adjustmentsMPT(xrpl::MPTID const& mptID) const -> std::optional<AdjustmentMPT>
|
||||
{
|
||||
auto i = creditsMPT_.find(mptID);
|
||||
if (i == creditsMPT_.end())
|
||||
return std::nullopt;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void
|
||||
DeferredCredits::apply(DeferredCredits& to)
|
||||
{
|
||||
for (auto const& i : credits_)
|
||||
for (auto const& i : creditsIOU_)
|
||||
{
|
||||
auto r = to.credits_.emplace(i);
|
||||
auto r = to.creditsIOU_.emplace(i);
|
||||
if (!r.second)
|
||||
{
|
||||
auto& toVal = r.first->second;
|
||||
@@ -128,6 +228,30 @@ DeferredCredits::apply(DeferredCredits& to)
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& i : creditsMPT_)
|
||||
{
|
||||
auto r = to.creditsMPT_.emplace(i);
|
||||
if (!r.second)
|
||||
{
|
||||
auto& toVal = r.first->second;
|
||||
auto const& fromVal = i.second;
|
||||
toVal.credit += fromVal.credit;
|
||||
toVal.selfDebit += fromVal.selfDebit;
|
||||
for (auto& [k, v] : fromVal.holders)
|
||||
{
|
||||
if (toVal.holders.find(k) == toVal.holders.end())
|
||||
{
|
||||
toVal.holders[k] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
toVal.holders[k].debit += v.debit;
|
||||
}
|
||||
}
|
||||
// Do not update the orig balance, it's already correct
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& i : ownerCounts_)
|
||||
{
|
||||
auto r = to.ownerCounts_.emplace(i);
|
||||
@@ -143,11 +267,13 @@ DeferredCredits::apply(DeferredCredits& to)
|
||||
} // namespace detail
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHook(
|
||||
PaymentSandbox::balanceHookIOU(
|
||||
AccountID const& account,
|
||||
AccountID const& issuer,
|
||||
STAmount const& amount) const
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "balanceHookIOU: amount is for Issue");
|
||||
|
||||
/*
|
||||
There are two algorithms here. The pre-switchover algorithm takes the
|
||||
current amount and subtracts the recorded credits. The post-switchover
|
||||
@@ -159,14 +285,14 @@ PaymentSandbox::balanceHook(
|
||||
magnitudes, (B+C)-C may not equal B.
|
||||
*/
|
||||
|
||||
auto const currency = amount.getCurrency();
|
||||
auto const& currency = amount.get<Issue>().currency;
|
||||
|
||||
auto delta = amount.zeroed();
|
||||
auto lastBal = amount;
|
||||
auto minBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustments(account, issuer, currency))
|
||||
if (auto adj = curSB->tab_.adjustmentsIOU(account, issuer, currency))
|
||||
{
|
||||
delta += adj->debits;
|
||||
lastBal = adj->origBalance;
|
||||
@@ -180,13 +306,13 @@ PaymentSandbox::balanceHook(
|
||||
// to compute usable balance just slightly above what the ledger
|
||||
// calculates (but always less than the actual balance).
|
||||
auto adjustedAmt = std::min({amount, lastBal - delta, minBal});
|
||||
adjustedAmt.setIssuer(amount.getIssuer());
|
||||
adjustedAmt.get<Issue>().account = amount.getIssuer();
|
||||
|
||||
if (isXRP(issuer) && adjustedAmt < beast::zero)
|
||||
{
|
||||
// A calculated negative XRP balance is not an error case. Consider a
|
||||
// payment snippet that credits a large XRP amount and then debits the
|
||||
// same amount. The credit can't be used but we subtract the debit and
|
||||
// same amount. The credit can't be used, but we subtract the debit and
|
||||
// calculate a negative value. It's not an error case.
|
||||
adjustedAmt.clear();
|
||||
}
|
||||
@@ -194,6 +320,64 @@ PaymentSandbox::balanceHook(
|
||||
return adjustedAmt;
|
||||
}
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHookMPT(AccountID const& account, MPTIssue const& issue, std::int64_t amount)
|
||||
const
|
||||
{
|
||||
auto const& issuer = issue.getIssuer();
|
||||
bool const accountIsHolder = account != issuer;
|
||||
|
||||
std::int64_t delta = 0;
|
||||
std::int64_t lastBal = amount;
|
||||
std::int64_t minBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustmentsMPT(issue))
|
||||
{
|
||||
if (accountIsHolder)
|
||||
{
|
||||
if (auto const i = adj->holders.find(account); i != adj->holders.end())
|
||||
{
|
||||
delta += i->second.debit;
|
||||
lastBal = i->second.origBalance;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delta += adj->credit;
|
||||
lastBal = adj->origBalance;
|
||||
}
|
||||
minBal = std::min(lastBal, minBal);
|
||||
}
|
||||
}
|
||||
|
||||
// The adjusted amount should never be larger than the balance.
|
||||
|
||||
auto const adjustedAmt = std::min({amount, lastBal - delta, minBal});
|
||||
|
||||
return adjustedAmt > 0 ? STAmount{issue, adjustedAmt} : STAmount{issue};
|
||||
}
|
||||
|
||||
STAmount
|
||||
PaymentSandbox::balanceHookSelfIssueMPT(xrpl::MPTIssue const& issue, std::int64_t amount) const
|
||||
{
|
||||
std::int64_t selfDebited = 0;
|
||||
std::int64_t lastBal = amount;
|
||||
for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_)
|
||||
{
|
||||
if (auto adj = curSB->tab_.adjustmentsMPT(issue))
|
||||
{
|
||||
selfDebited += adj->selfDebit;
|
||||
lastBal = adj->origBalance;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastBal > selfDebited)
|
||||
return STAmount{issue, lastBal - selfDebited};
|
||||
|
||||
return STAmount{issue};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) const
|
||||
{
|
||||
@@ -207,13 +391,39 @@ PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) co
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::creditHook(
|
||||
PaymentSandbox::creditHookIOU(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
STAmount const& preCreditBalance)
|
||||
{
|
||||
tab_.credit(from, to, amount, preCreditBalance);
|
||||
XRPL_ASSERT(amount.holds<Issue>(), "creditHookIOU: amount is for Issue");
|
||||
|
||||
tab_.creditIOU(from, to, amount, preCreditBalance);
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::creditHookMPT(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
std::uint64_t preCreditBalanceHolder,
|
||||
std::int64_t preCreditBalanceIssuer)
|
||||
{
|
||||
XRPL_ASSERT(amount.holds<MPTIssue>(), "creditHookMPT: amount is for MPTIssue");
|
||||
|
||||
tab_.creditMPT(from, to, amount, preCreditBalanceHolder, preCreditBalanceIssuer);
|
||||
}
|
||||
|
||||
void
|
||||
PaymentSandbox::issuerSelfDebitHookMPT(
|
||||
MPTIssue const& issue,
|
||||
std::uint64_t amount,
|
||||
std::int64_t origBalance)
|
||||
{
|
||||
XRPL_ASSERT(amount > 0, "PaymentSandbox::issuerSelfDebitHookMPT: amount must be > 0");
|
||||
|
||||
tab_.issuerSelfDebitMPT(issue, amount, origBalance);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -346,7 +556,7 @@ PaymentSandbox::balanceChanges(ReadView const& view) const
|
||||
}
|
||||
// The following are now set, put them in the map
|
||||
auto delta = newBalance - oldBalance;
|
||||
auto const cur = newBalance.getCurrency();
|
||||
auto const cur = newBalance.get<Issue>().currency;
|
||||
result[std::make_tuple(lowID, highID, cur)] = delta;
|
||||
auto r = result.emplace(std::make_tuple(lowID, lowID, cur), delta);
|
||||
if (r.second)
|
||||
|
||||
@@ -84,11 +84,10 @@ bool
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
Issue const& asset,
|
||||
Issue const& asset2)
|
||||
Asset const& asset,
|
||||
Asset const& asset2)
|
||||
{
|
||||
return isFrozen(view, account, asset.currency, asset.account) ||
|
||||
isFrozen(view, account, asset2.currency, asset2.account);
|
||||
return isFrozen(view, account, asset) || isFrozen(view, account, asset2);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -334,22 +333,19 @@ withdrawToDestExceedsLimit(
|
||||
if (from == to || to == issuer || isXRP(issuer))
|
||||
return tesSUCCESS;
|
||||
|
||||
return std::visit(
|
||||
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
|
||||
if constexpr (std::is_same_v<TIss, Issue>)
|
||||
return amount.asset().visit(
|
||||
[&](Issue const& issue) -> TER {
|
||||
auto const& currency = issue.currency;
|
||||
auto const owed = creditBalance(view, to, issuer, currency);
|
||||
if (owed <= beast::zero)
|
||||
{
|
||||
auto const& currency = issue.currency;
|
||||
auto const owed = creditBalance(view, to, issuer, currency);
|
||||
if (owed <= beast::zero)
|
||||
{
|
||||
auto const limit = creditLimit(view, to, issuer, currency);
|
||||
if (-owed >= limit || amount > (limit + owed))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
auto const limit = creditLimit(view, to, issuer, currency);
|
||||
if (-owed >= limit || amount > (limit + owed))
|
||||
return tecNO_LINE;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
},
|
||||
amount.asset().value());
|
||||
[](MPTIssue const&) -> TER { return tesSUCCESS; });
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -80,7 +80,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
|
||||
auto const fullBalance = sle->getFieldAmount(sfBalance);
|
||||
|
||||
auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
|
||||
auto const balance = view.balanceHookIOU(id, xrpAccount(), fullBalance);
|
||||
|
||||
STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user