mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-16 04:18:33 +00:00
Compare commits
687 Commits
ximinez/va
...
alphanet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7ec38c843 | ||
|
|
39341a5b7a | ||
|
|
c842da7ffa | ||
|
|
a9a27fee8f | ||
|
|
6538794e81 | ||
|
|
aece614450 | ||
|
|
8c1af90d8a | ||
|
|
2c0fde6943 | ||
|
|
4c24f5aed6 | ||
|
|
642fd96ca7 | ||
|
|
c822dcc075 | ||
|
|
af5a877a4d | ||
|
|
dce61d0d8c | ||
|
|
7f1be3103b | ||
|
|
0d3750f39d | ||
|
|
b20a95ad78 | ||
|
|
ea63e465f0 | ||
|
|
5057dba76f | ||
|
|
78fc349f09 | ||
|
|
03ff361885 | ||
|
|
bcb8fb7d4f | ||
|
|
0ae6a81246 | ||
|
|
6b24a5c32e | ||
|
|
48ca26c2a4 | ||
|
|
bb60431ebf | ||
|
|
2a11a0f342 | ||
|
|
008643d01c | ||
|
|
6badf9b5aa | ||
|
|
f3e2670dfa | ||
|
|
adaae9496a | ||
|
|
b045b86222 | ||
|
|
e34711194c | ||
|
|
e634c6ddc7 | ||
|
|
b30e9fc369 | ||
|
|
6ad5aae761 | ||
|
|
5af42fc475 | ||
|
|
3e7af2ab7e | ||
|
|
9403736199 | ||
|
|
028783661d | ||
|
|
21890d9daf | ||
|
|
060957ed39 | ||
|
|
1381483c7a | ||
|
|
3e4bdf2782 | ||
|
|
e3c8996e44 | ||
|
|
d5bfe94f15 | ||
|
|
2ad4def35f | ||
|
|
f7f50caa6e | ||
|
|
3e54e7d00b | ||
|
|
827b50f169 | ||
|
|
58a59c37ed | ||
|
|
986065c16f | ||
|
|
37cb4cdbe3 | ||
|
|
2ddf6ee148 | ||
|
|
8ce4f71427 | ||
|
|
6e1eb88e6e | ||
|
|
49cdc105de | ||
|
|
636d2d4851 | ||
|
|
7d7275847d | ||
|
|
f0fd6ad85e | ||
|
|
5d8fd9824e | ||
|
|
346ea40f69 | ||
|
|
8809bdf3f0 | ||
|
|
deaf596494 | ||
|
|
7863ac8cf6 | ||
|
|
b3b38e4416 | ||
|
|
ccd5dc5e06 | ||
|
|
b2453b626e | ||
|
|
de6e5d3a94 | ||
|
|
fac20a06f3 | ||
|
|
b72ff98159 | ||
|
|
2ba6f4cc68 | ||
|
|
9a7c5ea593 | ||
|
|
7281e0606a | ||
|
|
71f5555873 | ||
|
|
3967ed6d54 | ||
|
|
e0151229b6 | ||
|
|
b6a899583b | ||
|
|
dc3bd9cf00 | ||
|
|
1e8b136bfb | ||
|
|
3c47af779c | ||
|
|
c28d389e0e | ||
|
|
d83a84510e | ||
|
|
f8fba079fe | ||
|
|
42502e4263 | ||
|
|
36c165f74d | ||
|
|
f7ea645bf4 | ||
|
|
50527485d3 | ||
|
|
421af6db79 | ||
|
|
fee4bfc22e | ||
|
|
5e3d20b3ed | ||
|
|
9e2aaf6f60 | ||
|
|
ec042fefee | ||
|
|
c5ae59c4a0 | ||
|
|
45e4b8899d | ||
|
|
c5dc408596 | ||
|
|
473fe44a85 | ||
|
|
0fdaf69e2c | ||
|
|
9d41b1bd1c | ||
|
|
f137d71510 | ||
|
|
8bc6e81c5f | ||
|
|
764cbe7c29 | ||
|
|
520650081b | ||
|
|
a097ccebae | ||
|
|
fe4ccdf750 | ||
|
|
046d4dd4af | ||
|
|
d27beef500 | ||
|
|
85512541ad | ||
|
|
3ab5288ef2 | ||
|
|
d0dbf9163c | ||
|
|
a1478fac39 | ||
|
|
cc76708563 | ||
|
|
422e5245e4 | ||
|
|
e3ba569187 | ||
|
|
959a186a0f | ||
|
|
3b1c9e4320 | ||
|
|
a3a2c85c41 | ||
|
|
b1ac891210 | ||
|
|
da57183e0c | ||
|
|
f370289733 | ||
|
|
d1dc7a6ccf | ||
|
|
5639863715 | ||
|
|
1be4868875 | ||
|
|
368ff1afce | ||
|
|
3adf2d40b5 | ||
|
|
a6983f8bf3 | ||
|
|
4113b105a5 | ||
|
|
7442ff2dec | ||
|
|
666e77b22c | ||
|
|
8c12de6c56 | ||
|
|
b21fd86f6e | ||
|
|
f5f47f1cf5 | ||
|
|
ca39bff3c8 | ||
|
|
dd0edc19a0 | ||
|
|
820ca5b332 | ||
|
|
1b226c8b2e | ||
|
|
ca6121c5b3 | ||
|
|
c49789086a | ||
|
|
5337d028a2 | ||
|
|
43d842926a | ||
|
|
2adffaef72 | ||
|
|
bd87edfc75 | ||
|
|
d34aa37b3c | ||
|
|
a0074f83d3 | ||
|
|
028ccea7a1 | ||
|
|
df85d43d8a | ||
|
|
809d92f23a | ||
|
|
5ae09518b8 | ||
|
|
3ab800bf47 | ||
|
|
fe1436fb3e | ||
|
|
abfa572370 | ||
|
|
d8e44687bf | ||
|
|
8a3363752f | ||
|
|
226ec160bb | ||
|
|
ef438aec26 | ||
|
|
6cf743dad9 | ||
|
|
52d2008797 | ||
|
|
4d685d4bad | ||
|
|
1e5e8ef018 | ||
|
|
1ac1d40d06 | ||
|
|
fb10515cff | ||
|
|
829f5f2a5a | ||
|
|
3c85a0180f | ||
|
|
096fa967da | ||
|
|
82ede27f10 | ||
|
|
866afeee0c | ||
|
|
75fac65e27 | ||
|
|
ecae6b6346 | ||
|
|
6f4fe178dd | ||
|
|
083986ab82 | ||
|
|
c145201538 | ||
|
|
4fbbd6ff59 | ||
|
|
39c95fee3b | ||
|
|
db6fe77959 | ||
|
|
be58ec0cd5 | ||
|
|
4073838f66 | ||
|
|
e227e2ce8b | ||
|
|
383a2f8819 | ||
|
|
e64703b151 | ||
|
|
c91640e3ac | ||
|
|
0ceee3a013 | ||
|
|
974b17f427 | ||
|
|
91de196312 | ||
|
|
ac71553abe | ||
|
|
9a8ec56d42 | ||
|
|
cb6dad0bc4 | ||
|
|
d54ca4cd3b | ||
|
|
5a0269aca7 | ||
|
|
0774ca32a9 | ||
|
|
8feb75011e | ||
|
|
088c5b3b03 | ||
|
|
1c4abe8c5e | ||
|
|
91a5747bf9 | ||
|
|
bc892f0bd3 | ||
|
|
8950530b04 | ||
|
|
5cbebf3c58 | ||
|
|
c634c75aee | ||
|
|
9801c23bad | ||
|
|
f6472347e9 | ||
|
|
a41889df31 | ||
|
|
1ced7dc8f3 | ||
|
|
df1458a136 | ||
|
|
cdd5ebf4c1 | ||
|
|
1a9a11fc31 | ||
|
|
a9430d4778 | ||
|
|
fed5093488 | ||
|
|
6c74fc1a7d | ||
|
|
939f8b1a49 | ||
|
|
1f8c462c42 | ||
|
|
47df026dcc | ||
|
|
2e48bb4b7f | ||
|
|
9e427ae3ba | ||
|
|
8022fc33cf | ||
|
|
9767d86de4 | ||
|
|
ca2d999618 | ||
|
|
4a01fe5aa6 | ||
|
|
f60a9f1c64 | ||
|
|
5ee903befc | ||
|
|
68f0dcae53 | ||
|
|
d582ae7990 | ||
|
|
0dbe51c740 | ||
|
|
63fff4b518 | ||
|
|
d3550fac60 | ||
|
|
10ef15fff1 | ||
|
|
2307cfdc5a | ||
|
|
3b328cfc45 | ||
|
|
e20b2430ba | ||
|
|
99072fbddf | ||
|
|
059cefa35a | ||
|
|
a2205190a6 | ||
|
|
4c657bc92e | ||
|
|
ac481a0bd7 | ||
|
|
7eacd9d735 | ||
|
|
1f9675d22f | ||
|
|
029fa435ee | ||
|
|
c92ca426bf | ||
|
|
ff896b226a | ||
|
|
3c53c94e7a | ||
|
|
d85bf722ea | ||
|
|
b664989cfb | ||
|
|
e77934302a | ||
|
|
ef7aeca6bf | ||
|
|
eec1d29b92 | ||
|
|
971ba2281e | ||
|
|
90357eeae1 | ||
|
|
597202a6f0 | ||
|
|
1600b3e7f3 | ||
|
|
d411448739 | ||
|
|
ecee732187 | ||
|
|
ce2586c039 | ||
|
|
36fb84926e | ||
|
|
56decaf852 | ||
|
|
d562129e7c | ||
|
|
1d9fe9dbaf | ||
|
|
e6eb6ccf6c | ||
|
|
5351d4f089 | ||
|
|
8cc2169939 | ||
|
|
74beb5f80d | ||
|
|
a4dfdaf77a | ||
|
|
826f613ad8 | ||
|
|
1259c1d5ca | ||
|
|
4bae4d781f | ||
|
|
01a5e07830 | ||
|
|
34522bc668 | ||
|
|
d2641d85bd | ||
|
|
75f66bd9fe | ||
|
|
7cd71cb659 | ||
|
|
9917f96166 | ||
|
|
e1cc82587b | ||
|
|
2cc9439fde | ||
|
|
52af9582e2 | ||
|
|
46e88dc732 | ||
|
|
bc24f2e211 | ||
|
|
7a7c993b15 | ||
|
|
9733ca8f91 | ||
|
|
18d5e3e226 | ||
|
|
b30b4e1d65 | ||
|
|
d435893602 | ||
|
|
00b0cf50f6 | ||
|
|
42cbecb9e4 | ||
|
|
fe2e5ce649 | ||
|
|
6afa51142d | ||
|
|
22054a573d | ||
|
|
7ef256499c | ||
|
|
1338062be7 | ||
|
|
4fc1778ec8 | ||
|
|
65322d9e78 | ||
|
|
0acfa1c4f2 | ||
|
|
309fdfc3f2 | ||
|
|
63bd5fc4ee | ||
|
|
82e7f7eeec | ||
|
|
c5598a4284 | ||
|
|
63d09f9c51 | ||
|
|
0deb6bcadf | ||
|
|
d606f88e84 | ||
|
|
9b013b559b | ||
|
|
1d4a3c00b8 | ||
|
|
4b34102e8e | ||
|
|
d006433579 | ||
|
|
a7ab8ee923 | ||
|
|
e0073a4402 | ||
|
|
2930ef217f | ||
|
|
9dbb301699 | ||
|
|
531e8b6ebd | ||
|
|
90397e1a52 | ||
|
|
888ca2e6d9 | ||
|
|
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 | ||
|
|
446ad36cbb | ||
|
|
eb2d44d442 | ||
|
|
77673663ca | ||
|
|
c1381f8ddd | ||
|
|
bd16f7989d | ||
|
|
fd14054f17 | ||
|
|
65f9cf80c0 | ||
|
|
4318b2ebf7 | ||
|
|
e6ee492822 | ||
|
|
d4510147d1 | ||
|
|
a9a94fbf1a | ||
|
|
719ba392db | ||
|
|
cd46b5d999 | ||
|
|
de55a5ebfc | ||
|
|
2ec4a1114e | ||
|
|
f01ac563a9 | ||
|
|
13707dda05 | ||
|
|
ba03a8a9d2 | ||
|
|
7c8279ec83 | ||
|
|
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 | ||
|
|
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 | ||
|
|
ff39fa59d9 | ||
|
|
d83ec96848 | ||
|
|
f0d0739528 | ||
|
|
375dd50b35 | ||
|
|
419d53ec4c | ||
|
|
d4d70d5675 | ||
|
|
6ab15f8377 | ||
|
|
91f3d51f3d | ||
|
|
9ed60b45f8 | ||
|
|
d5c53dcfd2 | ||
|
|
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 |
24
.codecov.yml
24
.codecov.yml
@@ -1,10 +1,32 @@
|
||||
codecov:
|
||||
require_ci_to_pass: true
|
||||
# The C++ and Rust uploads land minutes apart; without this gate Codecov
|
||||
# publishes a near-zero total from whichever one arrives first.
|
||||
notify:
|
||||
after_n_builds: 2
|
||||
wait_for_ci: true
|
||||
|
||||
comment:
|
||||
behavior: default
|
||||
layout: reach,diff,flags,tree,reach
|
||||
show_carryforward_flags: false
|
||||
show_carryforward_flags: true
|
||||
after_n_builds: 2
|
||||
|
||||
# C++ and Rust coverage upload from independent workflows under the `cpp` and
|
||||
# `rust` flags; carryforward keeps one language's total when only the other reran.
|
||||
flag_management:
|
||||
default_rules:
|
||||
carryforward: true
|
||||
individual_flags:
|
||||
- name: cpp
|
||||
carryforward: true
|
||||
paths:
|
||||
- include/
|
||||
- src/
|
||||
- name: rust
|
||||
carryforward: true
|
||||
paths:
|
||||
- crates/
|
||||
|
||||
coverage:
|
||||
range: "70..85"
|
||||
|
||||
@@ -7,6 +7,9 @@ ignorePaths:
|
||||
- cmake/**
|
||||
- LICENSE.md
|
||||
- .clang-tidy
|
||||
- src/test/app/wasm_fixtures/**/*.wat
|
||||
- src/test/app/wasm_fixtures/*.c
|
||||
- nix/check-tools/*.txt # generated, and full of Nix store hashes
|
||||
language: en
|
||||
allowCompoundWords: true # TODO (#6334)
|
||||
ignoreRandomStrings: true
|
||||
@@ -42,6 +45,7 @@ suggestWords:
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
words:
|
||||
- cusip
|
||||
- abempty
|
||||
- AMMID
|
||||
- AMMMPT
|
||||
@@ -67,7 +71,11 @@ words:
|
||||
- Btrfs
|
||||
- Buildx
|
||||
- canonicality
|
||||
- cdylib
|
||||
- canonicalised
|
||||
- canonicality
|
||||
- cctools
|
||||
- CGNAT
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
@@ -109,9 +117,10 @@ words:
|
||||
- disablerepo
|
||||
- distro
|
||||
- doxyfile
|
||||
- dsymutil
|
||||
- dxrpl
|
||||
- elgamal
|
||||
- enabled
|
||||
- emittance
|
||||
- enablerepo
|
||||
- endmacro
|
||||
- envrc
|
||||
@@ -167,17 +176,18 @@ words:
|
||||
- LOCALGOOD
|
||||
- logwstream
|
||||
- Lombrozo
|
||||
- lresolv
|
||||
- lseq
|
||||
- lsmf
|
||||
- ltype
|
||||
- mathbunnyru
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
- Merkle
|
||||
- misprediction
|
||||
- mispricing
|
||||
- missingok
|
||||
- MPTAMM
|
||||
- mptbalance
|
||||
- MPTDEX
|
||||
- mptflags
|
||||
@@ -220,6 +230,7 @@ words:
|
||||
- Nyffenegger
|
||||
- onlatest
|
||||
- ostr
|
||||
- otool
|
||||
- oxalica
|
||||
- pargs
|
||||
- partitioner
|
||||
@@ -249,11 +260,15 @@ words:
|
||||
- Raphson
|
||||
- rcflags
|
||||
- replayer
|
||||
- rerandomize
|
||||
- repodata
|
||||
- repomd
|
||||
- rerandomization
|
||||
- rerandomize
|
||||
- rerandomized
|
||||
- rerandomizes
|
||||
- rerere
|
||||
- retargeted
|
||||
- retargets
|
||||
- retriable
|
||||
- RIPD
|
||||
- ripdtop
|
||||
@@ -271,8 +286,8 @@ words:
|
||||
- rustup
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- scons
|
||||
- Schnorr
|
||||
- scons
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
@@ -289,6 +304,7 @@ words:
|
||||
- sles
|
||||
- soci
|
||||
- socidb
|
||||
- Sonatype
|
||||
- sponsee
|
||||
- sponsees
|
||||
- SRPMS
|
||||
@@ -296,7 +312,9 @@ words:
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
- stjson
|
||||
- stnum
|
||||
- stnumber
|
||||
- stobj
|
||||
- stobject
|
||||
- stpath
|
||||
@@ -308,6 +326,7 @@ words:
|
||||
- summands
|
||||
- superpeer
|
||||
- superpeers
|
||||
- Swatinem
|
||||
- takergets
|
||||
- takerpays
|
||||
- ters
|
||||
@@ -322,8 +341,8 @@ words:
|
||||
- txn
|
||||
- txns
|
||||
- txs
|
||||
- ubsan
|
||||
- UBSAN
|
||||
- ubsan
|
||||
- ufdio
|
||||
- umant
|
||||
- unacquired
|
||||
@@ -335,7 +354,9 @@ words:
|
||||
- unfindable
|
||||
- unflatten
|
||||
- unfund
|
||||
- ungated
|
||||
- unimpair
|
||||
- unmetered
|
||||
- unroutable
|
||||
- unscalable
|
||||
- unserviced
|
||||
@@ -361,8 +382,11 @@ words:
|
||||
- writeme
|
||||
- wsrch
|
||||
- wthread
|
||||
- Xahau
|
||||
- xbridge
|
||||
- xchain
|
||||
- xcrun
|
||||
- xfloat
|
||||
- ximinez
|
||||
- XMACRO
|
||||
- xored
|
||||
@@ -372,4 +396,4 @@ words:
|
||||
- xrplf
|
||||
- xxhash
|
||||
- xxhasher
|
||||
- CGNAT
|
||||
- zstdio
|
||||
|
||||
3
.envrc
3
.envrc
@@ -1,5 +1,8 @@
|
||||
watch_file nix/*.nix
|
||||
|
||||
# Pinned Rust toolchain, read by nix/packages.nix via fromRustupToolchainFile.
|
||||
watch_file rust-toolchain.toml
|
||||
|
||||
# The dev shell derivation includes all of conan/ (see nix/devshell.nix), so any
|
||||
# change in there has to invalidate direnv's cached environment.
|
||||
watch_dir conan
|
||||
|
||||
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
39
.github/actions/cargo-cache/action.yml
vendored
Normal file
39
.github/actions/cargo-cache/action.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Use cargo artifacts cache
|
||||
description: >
|
||||
Cache the cargo build artifacts with rust-cache. Never caches ~/.cargo/bin:
|
||||
when saving the cache, rust-cache deletes all binaries that were already
|
||||
present there, which on persistent self-hosted runners wipes the tools
|
||||
installed by prepare-runner. Harmless on ephemeral runners, but kept
|
||||
consistent everywhere.
|
||||
|
||||
inputs:
|
||||
workspaces:
|
||||
description: "Workspaces to cache, as 'workspace -> target' lines."
|
||||
required: false
|
||||
default: crates
|
||||
key:
|
||||
description: "Additional part of the cache key."
|
||||
required: false
|
||||
default: ""
|
||||
cache-directories:
|
||||
description: "Additional non-workspace directories to cache."
|
||||
required: false
|
||||
default: ""
|
||||
save-if:
|
||||
description: >
|
||||
Condition for saving the cache after the job. Defaults to save only from develop branch
|
||||
required: false
|
||||
default: ${{ github.ref == 'refs/heads/develop' }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Use cargo artifacts cache
|
||||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
cache-bin: "false"
|
||||
cache-directories: ${{ inputs.cache-directories }}
|
||||
key: ${{ inputs.key }}
|
||||
save-if: ${{ inputs.save-if }}
|
||||
workspaces: ${{ inputs.workspaces }}
|
||||
44
.github/actions/generate-version/action.yml
vendored
44
.github/actions/generate-version/action.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: Generate build version number
|
||||
description: "Generate build version number."
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "The generated build version number."
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# When a tag is pushed, the version is used as-is.
|
||||
- name: Generate version for tag event
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
|
||||
|
||||
# When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted
|
||||
# from the BuildInfo.cpp file and the shortened commit hash appended to it.
|
||||
# We use a plus sign instead of a hyphen because Conan recipe versions do
|
||||
# not support two hyphens.
|
||||
- name: Generate version for non-tag event
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Extracting version from BuildInfo.cpp.'
|
||||
VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')"
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo 'Unable to extract version from BuildInfo.cpp.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'Appending shortened commit hash to version.'
|
||||
SHA='${{ github.sha }}'
|
||||
VERSION="${VERSION}+${SHA:0:7}"
|
||||
|
||||
echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
|
||||
|
||||
- name: Output version
|
||||
id: version
|
||||
shell: bash
|
||||
run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}"
|
||||
44
.github/actions/release-info/action.yml
vendored
Normal file
44
.github/actions/release-info/action.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Release info
|
||||
description: "Derive the version, release channel and package release number for this build."
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "The build version number."
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
channel:
|
||||
description: "The release channel this build belongs to."
|
||||
value: ${{ steps.release_info.outputs.channel }}
|
||||
pkg_release:
|
||||
description: "The package release number: 1 for a tag, <run number>.<commit date>git<short commit hash> otherwise."
|
||||
value: ${{ steps.release_info.outputs.pkg_release }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# A tag names its own version. Anything else takes it from BuildInfo.cpp and
|
||||
# appends the commit hash as build metadata, joined with a plus sign because a
|
||||
# Conan version cannot contain two hyphens.
|
||||
- name: Determine version
|
||||
id: version
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
version="${REF_NAME}"
|
||||
else
|
||||
version="$(awk -F'"' '/versionString =/ { print $2 }' src/libxrpl/protocol/BuildInfo.cpp)"
|
||||
if [[ -z "${version}" ]]; then
|
||||
echo "Unable to read versionString from BuildInfo.cpp." >&2
|
||||
exit 1
|
||||
fi
|
||||
version="${version}+${SHA:0:7}"
|
||||
fi
|
||||
|
||||
echo "version=${version}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Determine release channel and package release
|
||||
id: release_info
|
||||
uses: XRPLF/actions/release-info@7cc0e4a8d9d0b838f92c48d312856b190341bbba
|
||||
70
.github/actions/setup-nix-env/action.yml
vendored
Normal file
70
.github/actions/setup-nix-env/action.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Setup Nix environment
|
||||
description: "Build the flake's CI environment and put its tools on PATH."
|
||||
|
||||
# The environment from nix/ci-env.nix, the same one the Linux CI images bake in
|
||||
# (see nix/docker). Exported onto PATH rather than entered with `nix develop`:
|
||||
# the composite actions below run plain `bash` and would escape a dev shell.
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
||||
steps:
|
||||
- name: Build the CI environment
|
||||
id: build
|
||||
shell: bash
|
||||
env:
|
||||
# --out-link doubles as a GC root for the length of the job.
|
||||
OUT_LINK: ${{ runner.temp }}/xrpld-ci-env
|
||||
run: |
|
||||
# --extra-experimental-features: flakes may not be on in the runner's nix.conf.
|
||||
nix --extra-experimental-features "nix-command flakes" \
|
||||
build .#default --out-link "${OUT_LINK}" --print-build-logs
|
||||
echo "path=$(readlink -f "${OUT_LINK}")" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Export the environment
|
||||
shell: bash
|
||||
env:
|
||||
ENV_PATH: ${{ steps.build.outputs.path }}
|
||||
run: |
|
||||
echo "${ENV_PATH}/bin" >>"${GITHUB_PATH}"
|
||||
|
||||
# Already KEY=VALUE per line. See `darwinEnv` in nix/ci-env.nix.
|
||||
ENV_FILE="${ENV_PATH}/share/xrpld-ci-env/env"
|
||||
if [ -f "${ENV_FILE}" ]; then
|
||||
cat "${ENV_FILE}" >>"${GITHUB_ENV}"
|
||||
fi
|
||||
|
||||
# XrplSanity.cmake otherwise rejects a Nix compiler as one that leaked.
|
||||
echo "XRPL_DEVSHELL=ci-env" >>"${GITHUB_ENV}"
|
||||
|
||||
# Unlike the Linux nix images, macOS needs no SSL_CERT_FILE: it has its
|
||||
# own trust store, and pinning would break TLS to hosts relying on it.
|
||||
|
||||
# In RUNNER_TEMP, which the runner empties per job, like the `.conan2`
|
||||
# prepare-runner hands the system toolchain - but under its own name:
|
||||
# that Conan is a different version, and the two would migrate each
|
||||
# other's cache.
|
||||
echo "CONAN_HOME=${RUNNER_TEMP}/.conan2-nix" >>"${GITHUB_ENV}"
|
||||
|
||||
# Config, profiles and remote, exactly as the dev shell sets them up on
|
||||
# entry; the `setup-conan` action is skipped for this toolchain.
|
||||
- name: Setup Conan
|
||||
shell: bash
|
||||
run: ./conan/init.sh
|
||||
|
||||
# `Check tools` runs later but swallows failures; a bad export would just
|
||||
# build with the system toolchain.
|
||||
- name: Verify the toolchain resolves into the Nix store
|
||||
shell: bash
|
||||
run: |
|
||||
for tool in clang clang++ cmake ninja conan; do
|
||||
path="$(command -v "${tool}" || true)"
|
||||
echo "${tool} -> ${path:-<not found>}"
|
||||
case "${path}" in
|
||||
/nix/store/*) ;;
|
||||
*)
|
||||
echo "::error::${tool} does not resolve into the Nix store"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
19
.github/dependabot.yml
vendored
19
.github/dependabot.yml
vendored
@@ -4,7 +4,8 @@ updates:
|
||||
directories:
|
||||
- /
|
||||
- .github/actions/build-deps/
|
||||
- .github/actions/generate-version/
|
||||
- .github/actions/cargo-cache/
|
||||
- .github/actions/release-info/
|
||||
- .github/actions/set-compiler-env/
|
||||
- .github/actions/setup-conan/
|
||||
schedule:
|
||||
@@ -19,3 +20,19 @@ updates:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: cargo
|
||||
directory: /crates
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "04:00"
|
||||
timezone: Etc/GMT
|
||||
commit-message:
|
||||
prefix: "chore: [DEPENDABOT] "
|
||||
target-branch: develop
|
||||
open-pull-requests-limit: 10
|
||||
groups:
|
||||
rust-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -18,7 +18,7 @@ If too broad, please consider splitting into multiple PRs.
|
||||
If there is a relevant task or issue, please link it here.
|
||||
-->
|
||||
|
||||
### Context of Change
|
||||
## Context of Change
|
||||
|
||||
<!--
|
||||
Please include the context of a change.
|
||||
@@ -29,7 +29,7 @@ If a refactor, how is this better than the previous implementation?
|
||||
If there is a spec or design document for this feature, please link it here.
|
||||
-->
|
||||
|
||||
### API Impact
|
||||
## API Impact
|
||||
|
||||
<!--
|
||||
Please check [x] relevant options, delete irrelevant ones.
|
||||
|
||||
@@ -19,6 +19,7 @@ libxrpl.ledger > xrpl.json
|
||||
libxrpl.ledger > xrpl.ledger
|
||||
libxrpl.ledger > xrpl.nodestore
|
||||
libxrpl.ledger > xrpl.protocol
|
||||
libxrpl.ledger > xrpl.server
|
||||
libxrpl.ledger > xrpl.shamap
|
||||
libxrpl.net > xrpl.basics
|
||||
libxrpl.net > xrpl.net
|
||||
@@ -206,6 +207,7 @@ xrpl.core > xrpl.json
|
||||
xrpl.core > xrpl.protocol
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
xrpl.ledger > xrpl.core
|
||||
xrpl.ledger > xrpl.json
|
||||
xrpl.ledger > xrpl.nodestore
|
||||
xrpl.ledger > xrpl.protocol
|
||||
|
||||
2
.github/scripts/rename/binary.sh
vendored
2
.github/scripts/rename/binary.sh
vendored
@@ -49,7 +49,7 @@ ${SED_COMMAND} -i -E 's@ripple/xrpld@XRPLF/rippled@g' BUILD.md
|
||||
${SED_COMMAND} -i -E 's@XRPLF/xrpld@XRPLF/rippled@g' BUILD.md
|
||||
${SED_COMMAND} -i -E 's@xrpld \(`xrpld`\)@xrpld@g' BUILD.md
|
||||
${SED_COMMAND} -i -E 's@XRPLF/xrpld@XRPLF/rippled@g' CONTRIBUTING.md
|
||||
${SED_COMMAND} -i -E 's@XRPLF/xrpld@XRPLF/rippled@g' docs/build/install.md
|
||||
${SED_COMMAND} -i -E 's@XRPLF/xrpld@XRPLF/rippled@g' docs/install.md
|
||||
|
||||
popd
|
||||
echo "Processing complete."
|
||||
|
||||
4
.github/scripts/rename/docs.sh
vendored
4
.github/scripts/rename/docs.sh
vendored
@@ -77,8 +77,8 @@ ${SED_COMMAND} -i 's/Ripple integrators/XRPL developers/' README.md
|
||||
${SED_COMMAND} -i 's/sanitizer-configuration-for-rippled/sanitizer-configuration-for-xrpld/' docs/build/sanitizers.md
|
||||
${SED_COMMAND} -i 's/rippled/xrpld/g' .github/scripts/levelization/README.md
|
||||
${SED_COMMAND} -i 's/rippled/xrpld/g' .github/scripts/strategy-matrix/generate.py
|
||||
${SED_COMMAND} -i 's@/rippled@/xrpld@g' docs/build/install.md
|
||||
${SED_COMMAND} -i 's@github.com/XRPLF/xrpld@github.com/XRPLF/rippled@g' docs/build/install.md
|
||||
${SED_COMMAND} -i 's@/rippled@/xrpld@g' docs/install.md
|
||||
${SED_COMMAND} -i 's@github.com/XRPLF/xrpld@github.com/XRPLF/rippled@g' docs/install.md
|
||||
${SED_COMMAND} -i 's/rippled/xrpld/g' docs/Doxyfile
|
||||
${SED_COMMAND} -i 's/ripple_basics/basics/' include/xrpl/basics/CountedObject.h
|
||||
${SED_COMMAND} -i 's/<ripple/<xrpl/' include/xrpl/protocol/AccountID.h
|
||||
|
||||
123
.github/scripts/strategy-matrix/generate.py
vendored
123
.github/scripts/strategy-matrix/generate.py
vendored
@@ -7,7 +7,13 @@ from pathlib import Path
|
||||
|
||||
THIS_DIR = Path(__file__).parent.resolve()
|
||||
|
||||
_BASE_CMAKE_ARGS = ["-Dtests=ON", "-Dwerr=ON", "-Dxrpld=ON", "-Dwextra=ON"]
|
||||
_BASE_CMAKE_ARGS = [
|
||||
"-Dtests=ON",
|
||||
"-Dwerr=ON",
|
||||
"-Dxrpld=ON",
|
||||
"-Dwextra=ON",
|
||||
"-Drust=ON",
|
||||
]
|
||||
|
||||
# Maps sanitizer names (as used in cmake) to short config-name suffixes.
|
||||
_SANITIZER_SUFFIX: dict[str, str] = {
|
||||
@@ -17,6 +23,19 @@ _SANITIZER_SUFFIX: dict[str, str] = {
|
||||
}
|
||||
|
||||
|
||||
def config_name(
|
||||
distro: str,
|
||||
compiler: str,
|
||||
build_type: str,
|
||||
arch: str,
|
||||
suffix: str = "",
|
||||
sanitizer: str = "",
|
||||
) -> str:
|
||||
"""Name a config. Its artifacts are named after it, so packaging reuses this."""
|
||||
parts = [s for s in [suffix, _SANITIZER_SUFFIX.get(sanitizer, "")] if s]
|
||||
return "-".join([f"{distro}-{compiler}-{build_type.lower()}-{arch}", *parts])
|
||||
|
||||
|
||||
def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
"""Get the full list of CMake arguments for a config."""
|
||||
args = _BASE_CMAKE_ARGS.copy()
|
||||
@@ -31,17 +50,27 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
|
||||
|
||||
# Every config must declare 'minimal'. Minimal configs form the reduced matrix
|
||||
# built for pull requests by default; the full matrix adds the rest. Packaging
|
||||
# configs declare it too, but packaging is gated in the workflow, not by it.
|
||||
# built for pull requests by default; the full matrix adds the rest.
|
||||
#
|
||||
# Configs may also opt into 'benchmark' to smoke-run the benchmarks. Note that
|
||||
# the flag applies to every entry a config expands into, so only set it on
|
||||
# configs that expand to a single combination.
|
||||
# Configs may also opt into 'benchmark' to smoke-run the benchmarks, or carry a
|
||||
# 'package' map to be packaged as well. Note that either applies to every entry
|
||||
# a config expands into, so only set them on configs that expand to a single
|
||||
# combination.
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class PackageConfig:
|
||||
"""The 'package' map of a config whose binaries are also packaged."""
|
||||
|
||||
type: str # "deb" or "rpm"; has to match what the image provides
|
||||
# The packaging container image: a vanilla distro image, not the nix image
|
||||
# the config itself builds in.
|
||||
image: str
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class LinuxConfig:
|
||||
"""One entry in linux.json's 'configs' or 'package_configs' arrays."""
|
||||
"""One entry in a linux.json 'configs' array."""
|
||||
|
||||
compiler: list[str]
|
||||
build_type: list[str]
|
||||
@@ -51,7 +80,13 @@ class LinuxConfig:
|
||||
sanitizers: list[str] = dataclasses.field(default_factory=list)
|
||||
suffix: str = ""
|
||||
extra_cmake_args: str = ""
|
||||
image: str = "" # only used by package_configs entries
|
||||
package: PackageConfig | None = None # set to also package this config
|
||||
# Flip every amendment to Supported::Yes before building (perf/test only).
|
||||
force_supported: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if isinstance(self.package, dict):
|
||||
self.package = PackageConfig(**self.package)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -60,22 +95,16 @@ class LinuxFile:
|
||||
|
||||
image_tag: str
|
||||
configs: dict[str, list[LinuxConfig]] # distro → configs
|
||||
package_configs: dict[str, list[LinuxConfig]] # distro → packaging configs
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: Path) -> "LinuxFile":
|
||||
data = json.loads(path.read_text())
|
||||
|
||||
def parse(section: dict) -> dict[str, list[LinuxConfig]]:
|
||||
return {
|
||||
distro: [LinuxConfig(**c) for c in cfgs]
|
||||
for distro, cfgs in section.items()
|
||||
}
|
||||
|
||||
return cls(
|
||||
image_tag=data["image_tag"],
|
||||
configs=parse(data["configs"]),
|
||||
package_configs=parse(data.get("package_configs", {})),
|
||||
configs={
|
||||
distro: [LinuxConfig(**c) for c in cfgs]
|
||||
for distro, cfgs in data["configs"].items()
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -88,6 +117,9 @@ class PlatformConfig:
|
||||
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
|
||||
benchmark: bool = False # if true, smoke-run the benchmarks after testing
|
||||
extra_cmake_args: str = ""
|
||||
# "" is the runner's system compiler, "nix" the flake's CI environment.
|
||||
# macOS only: Linux always builds in a Nix image, Windows has no Nix.
|
||||
toolchain: str = ""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if isinstance(self.build_type, str):
|
||||
@@ -137,6 +169,8 @@ class MatrixEntry:
|
||||
sanitizers: str
|
||||
image: str = "" # container image; empty for macOS/Windows (runs natively)
|
||||
compiler: str = "" # compiler name ("gcc" or "clang"); empty for macOS/Windows
|
||||
toolchain: str = "" # "nix" for the flake's CI environment; see PlatformConfig
|
||||
force_supported: bool = False # flip amendments to Supported::Yes before build
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -146,7 +180,7 @@ class PackagingEntry:
|
||||
xrpld_artifact_name: str
|
||||
validator_keys_artifact_name: str
|
||||
image: str
|
||||
distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps
|
||||
package_type: str # "deb" or "rpm"; drives the format-specific steps
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -187,13 +221,9 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
effective_sanitizers,
|
||||
effective_archs.items(),
|
||||
):
|
||||
name = f"{distro}-{compiler}-{build_type.lower()}-{arch}"
|
||||
suffix_parts = [
|
||||
s for s in [cfg.suffix, _SANITIZER_SUFFIX.get(sanitizer, "")] if s
|
||||
]
|
||||
if suffix_parts:
|
||||
name += "-" + "-".join(suffix_parts)
|
||||
|
||||
name = config_name(
|
||||
distro, compiler, build_type, arch, cfg.suffix, sanitizer
|
||||
)
|
||||
entries.append(
|
||||
MatrixEntry(
|
||||
config_name=name,
|
||||
@@ -206,6 +236,7 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
architecture=arch_info,
|
||||
sanitizers=sanitizer,
|
||||
compiler=compiler,
|
||||
force_supported=cfg.force_supported,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -213,27 +244,33 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
|
||||
|
||||
def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
||||
"""Generate the packaging matrix from a LinuxFile's package_configs section.
|
||||
"""Generate the packaging matrix from the configs that carry a 'package' map.
|
||||
|
||||
Packaging uses vanilla distro images (debian:bookworm, ubi9, …) instead of
|
||||
the nix-based build images, because deb/rpm tooling (debhelper, rpm-build)
|
||||
is taken from the distro's archive rather than from nixpkgs. Each config
|
||||
entry carries its own 'image'.
|
||||
Packaging consumes the binaries that config's build job uploaded, so the
|
||||
artifact names come from the same config name, and a packaged config is one
|
||||
that passes -Dvalidator_keys=ON.
|
||||
|
||||
The artifact names must match what the build job uploads: one artifact per
|
||||
binary, each named after the build config.
|
||||
Packaging itself runs in vanilla distro images (debian:trixie, almalinux:10)
|
||||
instead of the nix-based build images, because deb/rpm tooling (debhelper,
|
||||
rpm-build) is taken from the distro's archive rather than from nixpkgs.
|
||||
"""
|
||||
entries = []
|
||||
for distro, configs in linux.package_configs.items():
|
||||
for distro, configs in linux.configs.items():
|
||||
for cfg in configs:
|
||||
for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type):
|
||||
config_name = f"{distro}-{compiler}-{build_type.lower()}-amd64"
|
||||
if cfg.package is None:
|
||||
continue
|
||||
for compiler, build_type, arch in itertools.product(
|
||||
cfg.compiler, cfg.build_type, cfg.arch
|
||||
):
|
||||
# The packaging workflow hardcodes an amd64 runner.
|
||||
assert arch == "amd64", f"cannot package {distro} on {arch}"
|
||||
name = config_name(distro, compiler, build_type, arch, cfg.suffix)
|
||||
entries.append(
|
||||
PackagingEntry(
|
||||
xrpld_artifact_name=f"xrpld-{config_name}",
|
||||
validator_keys_artifact_name=f"validator-keys-{config_name}",
|
||||
image=cfg.image,
|
||||
distro=distro,
|
||||
xrpld_artifact_name=f"xrpld-{name}",
|
||||
validator_keys_artifact_name=f"validator-keys-{name}",
|
||||
image=cfg.package.image,
|
||||
package_type=cfg.package.type,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -253,9 +290,12 @@ def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]
|
||||
if minimal and not cfg.minimal:
|
||||
continue
|
||||
for build_type in cfg.build_type:
|
||||
name = f"{platform_name}-{arch}-{build_type.lower()}"
|
||||
if cfg.toolchain:
|
||||
name += f"-{cfg.toolchain}"
|
||||
entries.append(
|
||||
MatrixEntry(
|
||||
config_name=f"{platform_name}-{arch}-{build_type.lower()}",
|
||||
config_name=name,
|
||||
cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args),
|
||||
cmake_target="install" if is_windows else "all",
|
||||
build_only=cfg.build_only,
|
||||
@@ -263,6 +303,7 @@ def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]
|
||||
build_type=build_type,
|
||||
architecture=Architecture(platform=pf.platform, runner=pf.runner),
|
||||
sanitizers="",
|
||||
toolchain=cfg.toolchain,
|
||||
)
|
||||
)
|
||||
return entries
|
||||
|
||||
53
.github/scripts/strategy-matrix/linux.json
vendored
53
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-fecfc0c",
|
||||
"image_tag": "sha-060957e",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
@@ -17,7 +17,6 @@
|
||||
"minimal": true,
|
||||
"benchmark": true
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
@@ -30,7 +29,6 @@
|
||||
"arch": ["arm64"],
|
||||
"minimal": false
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["gcc", "clang"],
|
||||
"build_type": ["Debug", "Release"],
|
||||
@@ -38,7 +36,6 @@
|
||||
"minimal": false,
|
||||
"sanitizers": ["address", "undefinedbehavior"]
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["clang"],
|
||||
"build_type": ["Debug"],
|
||||
@@ -62,47 +59,45 @@
|
||||
"minimal": false,
|
||||
"suffix": "unity",
|
||||
"extra_cmake_args": "-Dunity=ON"
|
||||
},
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "supported",
|
||||
"force_supported": true,
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"debian": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON"
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"rhel": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON"
|
||||
}
|
||||
]
|
||||
},
|
||||
"package_configs": {
|
||||
"debian": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
|
||||
}
|
||||
],
|
||||
|
||||
"rhel": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "rpm",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-49cdc10"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
13
.github/scripts/strategy-matrix/macos.json
vendored
13
.github/scripts/strategy-matrix/macos.json
vendored
@@ -12,6 +12,19 @@
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"build_only": true,
|
||||
"minimal": false
|
||||
},
|
||||
{
|
||||
"build_type": "Release",
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"toolchain": "nix",
|
||||
"minimal": false
|
||||
},
|
||||
{
|
||||
"build_type": "Debug",
|
||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"toolchain": "nix",
|
||||
"build_only": true,
|
||||
"minimal": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
6
.github/workflows/build-nix-images.yml
vendored
6
.github/workflows/build-nix-images.yml
vendored
@@ -5,14 +5,13 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-nix-images.yml"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
- "rust-toolchain.toml"
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "bin/check-tools.sh"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
pull_request:
|
||||
@@ -24,6 +23,7 @@ on:
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
base_image: debian:bookworm
|
||||
- name: rhel
|
||||
base_image: registry.access.redhat.com/ubi9/ubi:latest
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@9e7e4e80af9e684c116b38369add8eea64451f32
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
with:
|
||||
image_name: xrpld/nix-${{ matrix.distro.name }}
|
||||
dockerfile: nix/docker/Dockerfile
|
||||
|
||||
19
.github/workflows/build-packaging-images.yml
vendored
19
.github/workflows/build-packaging-images.yml
vendored
@@ -6,13 +6,13 @@ on:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "package/Dockerfile"
|
||||
- "package/install-packaging-tools.sh"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "package/Dockerfile"
|
||||
- "package/install-packaging-tools.sh"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -33,14 +33,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Newest of each distro: these images only wrap pre-built binaries, so
|
||||
# they set no floor for consumers. build_pkg.py pins the RPM dist tag.
|
||||
distro:
|
||||
- name: debian
|
||||
base_image: debian:bookworm
|
||||
base_image: debian:trixie
|
||||
# AlmaLinux rather than UBI, which does not ship rpm-sign.
|
||||
- name: rhel
|
||||
base_image: registry.access.redhat.com/ubi9/ubi:latest
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@9e7e4e80af9e684c116b38369add8eea64451f32
|
||||
base_image: almalinux:10
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
with:
|
||||
image_name: xrpld/packaging-${{ matrix.distro.name }}
|
||||
dockerfile: package/Dockerfile
|
||||
dockerfile: package/docker/Dockerfile
|
||||
base_image: ${{ matrix.distro.base_image }}
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
|
||||
2
.github/workflows/build-pre-commit-image.yml
vendored
2
.github/workflows/build-pre-commit-image.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@9e7e4e80af9e684c116b38369add8eea64451f32
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
with:
|
||||
image_name: xrpld/pre-commit
|
||||
dockerfile: bin/pre-commit/Dockerfile
|
||||
|
||||
103
.github/workflows/build-supported-image.yml
vendored
Normal file
103
.github/workflows/build-supported-image.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# Package the "all amendments Supported::Yes" build into a runtime Docker image
|
||||
# and push it to GHCR, as a drop-in for the rippleci/xrpld image xrpl.js uses
|
||||
# for standalone testing -- except every amendment is built Supported::Yes.
|
||||
#
|
||||
# This does NOT build or package anything: the Trigger workflow already builds
|
||||
# the supported binary and the supported .deb (the force_supported build config
|
||||
# and the matching supported package config in linux.json). This workflow waits
|
||||
# for a successful Trigger run on develop, downloads that run's supported .deb
|
||||
# artifact, installs it into a slim base (docker/supported.Dockerfile, which
|
||||
# replicates rippleci's layout), and pushes ghcr.io/xrplf/xrpld/supported.
|
||||
#
|
||||
# Perf/test artifact only -- never run it on a production validator.
|
||||
name: Build supported Docker image
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Trigger"]
|
||||
types: [completed]
|
||||
branches: [develop]
|
||||
# Manual runs: point at a specific completed Trigger run via its run id.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
trigger_run_id:
|
||||
description: "Run id of the Trigger workflow whose supported .deb to package."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
IMAGE_NAME: ghcr.io/xrplf/xrpld/supported
|
||||
# The supported .deb artifact uploaded by reusable-package.yml:
|
||||
# <artifact_name>-pkg, where artifact_name carries the -supported suffix.
|
||||
DEB_ARTIFACT: xrpld-ubuntu-gcc-release-amd64-supported-pkg
|
||||
SOURCE_RUN_ID: ${{ github.event.workflow_run.id || github.event.inputs.trigger_run_id }}
|
||||
jobs:
|
||||
image:
|
||||
# Only for successful Trigger runs (workflow_run), and only on the canonical
|
||||
# repo where GITHUB_TOKEN can push to ghcr.io/xrplf/*.
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success') }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Download the supported .deb from the Trigger run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ${{ env.DEB_ARTIFACT }}
|
||||
path: dl
|
||||
run-id: ${{ env.SOURCE_RUN_ID }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Assemble build context
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p docker-context
|
||||
deb="$(find dl -name '*.deb' | head -n1)"
|
||||
[ -n "${deb}" ] || {
|
||||
echo "::error::no supported .deb found in run ${SOURCE_RUN_ID}"
|
||||
exit 1
|
||||
}
|
||||
mv "${deb}" docker-context/xrpld.deb
|
||||
echo "Packaging $(basename "${deb}") into ${IMAGE_NAME}"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
with:
|
||||
images: ${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=sha,prefix=sha-,format=short
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
with:
|
||||
context: docker-context
|
||||
file: docker/supported.Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
80
.github/workflows/cargo-audit.yml
vendored
Normal file
80
.github/workflows/cargo-audit.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: Cargo audit
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 06:32 UTC every Monday.
|
||||
- cron: "32 6 * * 1"
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "crates/**/Cargo.toml"
|
||||
- "crates/Cargo.lock"
|
||||
- ".github/workflows/cargo-audit.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "crates/**/Cargo.toml"
|
||||
- "crates/Cargo.lock"
|
||||
- ".github/workflows/cargo-audit.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: crates
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Run cargo audit
|
||||
id: audit
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -o pipefail
|
||||
cargo audit | tee /tmp/cargo-audit.txt
|
||||
|
||||
- name: Prepare issue body
|
||||
if: ${{ steps.audit.outcome != 'success' && github.event_name == 'schedule' }}
|
||||
run: |
|
||||
{
|
||||
echo "## \`cargo audit\` found advisories"
|
||||
echo
|
||||
echo '```'
|
||||
cat /tmp/cargo-audit.txt
|
||||
echo '```'
|
||||
echo
|
||||
echo "---"
|
||||
echo "*This issue was automatically created by the cargo-audit workflow.*"
|
||||
} >/tmp/cargo-audit-issue.md
|
||||
|
||||
- name: Create issue
|
||||
if: ${{ steps.audit.outcome != 'success' && github.event_name == 'schedule' }}
|
||||
uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712
|
||||
with:
|
||||
title: "cargo audit found vulnerabilities"
|
||||
body_file: /tmp/cargo-audit-issue.md
|
||||
labels: "Bug,Security"
|
||||
|
||||
- name: Fail if advisories were found
|
||||
if: ${{ steps.audit.outcome != 'success' }}
|
||||
run: |
|
||||
echo "cargo audit found advisories!"
|
||||
cat /tmp/cargo-audit.txt
|
||||
exit 1
|
||||
2
.github/workflows/check-tools.yml
vendored
2
.github/workflows/check-tools.yml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c00c22ada3bd6bcda48fcb0d62fbbab49fec8a0f
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
17
.github/workflows/on-pr.yml
vendored
17
.github/workflows/on-pr.yml
vendored
@@ -77,24 +77,28 @@ jobs:
|
||||
|
||||
# Keep the paths below in sync with those in `on-trigger.yml`.
|
||||
.github/actions/build-deps/**
|
||||
.github/actions/generate-version/**
|
||||
.github/actions/release-info/**
|
||||
.github/actions/setup-conan/**
|
||||
.github/actions/setup-nix-env/**
|
||||
.github/scripts/strategy-matrix/**
|
||||
.github/workflows/reusable-build-test-config.yml
|
||||
.github/workflows/reusable-build-test.yml
|
||||
.github/workflows/reusable-check-autogen.yml
|
||||
.github/workflows/reusable-clang-tidy.yml
|
||||
.github/workflows/reusable-package.yml
|
||||
.github/workflows/reusable-rust.yml
|
||||
.github/workflows/reusable-strategy-matrix.yml
|
||||
.github/workflows/reusable-test.yml
|
||||
.github/workflows/reusable-upload-recipe.yml
|
||||
.clang-tidy
|
||||
.codecov.yml
|
||||
bin/check-nix-store-refs.sh
|
||||
bin/check-tools.sh
|
||||
bin/default-loader-path.sh
|
||||
cfg/**
|
||||
cmake/**
|
||||
conan/**
|
||||
crates/**
|
||||
external/**
|
||||
include/**
|
||||
src/**
|
||||
@@ -102,6 +106,9 @@ jobs:
|
||||
CMakeLists.txt
|
||||
conanfile.py
|
||||
conan.lock
|
||||
flake.lock
|
||||
flake.nix
|
||||
nix/**
|
||||
LICENSE.md
|
||||
package/**
|
||||
README.md
|
||||
@@ -168,6 +175,13 @@ jobs:
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
rust:
|
||||
needs: should-run
|
||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
||||
uses: ./.github/workflows/reusable-rust.yml
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
package:
|
||||
needs: [should-run, build-test]
|
||||
# Packaging consumes the debian/rhel release binaries, which are only built
|
||||
@@ -211,6 +225,7 @@ jobs:
|
||||
- check-rename
|
||||
- clang-tidy
|
||||
- build-test
|
||||
- rust
|
||||
- package
|
||||
- upload-recipe
|
||||
- notify-clio
|
||||
|
||||
18
.github/workflows/on-tag.yml
vendored
18
.github/workflows/on-tag.yml
vendored
@@ -1,5 +1,9 @@
|
||||
# This workflow uploads the libxrpl recipe to the Conan remote and builds
|
||||
# release packages when a versioned tag is pushed.
|
||||
# When a versioned tag is pushed, this workflow:
|
||||
#
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds and tests the release binaries
|
||||
# - builds the DEB and RPM packages
|
||||
# - publishes those packages to the XRPLF package repositories
|
||||
name: Tag
|
||||
|
||||
on:
|
||||
@@ -24,7 +28,7 @@ jobs:
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
build-test:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
if: ${{ github.repository_owner == 'XRPLF' }}
|
||||
uses: ./.github/workflows/reusable-build-test.yml
|
||||
strategy:
|
||||
fail-fast: true
|
||||
@@ -37,6 +41,12 @@ jobs:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
package:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
if: ${{ github.repository_owner == 'XRPLF' }}
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
publish: true
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
|
||||
22
.github/workflows/on-trigger.yml
vendored
22
.github/workflows/on-trigger.yml
vendored
@@ -15,24 +15,28 @@ on:
|
||||
|
||||
# Keep the paths below in sync with those in `on-pr.yml`.
|
||||
- ".github/actions/build-deps/**"
|
||||
- ".github/actions/generate-version/**"
|
||||
- ".github/actions/release-info/**"
|
||||
- ".github/actions/setup-conan/**"
|
||||
- ".github/actions/setup-nix-env/**"
|
||||
- ".github/scripts/strategy-matrix/**"
|
||||
- ".github/workflows/reusable-build-test-config.yml"
|
||||
- ".github/workflows/reusable-build-test.yml"
|
||||
- ".github/workflows/reusable-check-autogen.yml"
|
||||
- ".github/workflows/reusable-clang-tidy.yml"
|
||||
- ".github/workflows/reusable-package.yml"
|
||||
- ".github/workflows/reusable-rust.yml"
|
||||
- ".github/workflows/reusable-strategy-matrix.yml"
|
||||
- ".github/workflows/reusable-test.yml"
|
||||
- ".github/workflows/reusable-upload-recipe.yml"
|
||||
- ".clang-tidy"
|
||||
- ".codecov.yml"
|
||||
- "bin/check-nix-store-refs.sh"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "cfg/**"
|
||||
- "cmake/**"
|
||||
- "conan/**"
|
||||
- "crates/**"
|
||||
- "external/**"
|
||||
- "include/**"
|
||||
- "src/**"
|
||||
@@ -40,6 +44,9 @@ on:
|
||||
- "CMakeLists.txt"
|
||||
- "conanfile.py"
|
||||
- "conan.lock"
|
||||
- "flake.lock"
|
||||
- "flake.nix"
|
||||
- "nix/**"
|
||||
- "LICENSE.md"
|
||||
- "package/**"
|
||||
- "README.md"
|
||||
@@ -96,6 +103,11 @@ jobs:
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
rust:
|
||||
uses: ./.github/workflows/reusable-rust.yml
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
upload-recipe:
|
||||
needs: build-test
|
||||
# Only run when pushing to the develop branch.
|
||||
@@ -108,3 +120,11 @@ jobs:
|
||||
package:
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
# Packages are built on every trigger; only develop pushes in XRPLF/rippled
|
||||
# publish them, matching upload-recipe above.
|
||||
publish: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
signing_key: ${{ secrets.NEXUS_PACKAGES_PRIVATE_KEY }}
|
||||
|
||||
4
.github/workflows/pre-commit.yml
vendored
4
.github/workflows/pre-commit.yml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
jobs:
|
||||
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
|
||||
run-hooks:
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@3ba08d6ddf114092891d48491fc2e26c3ba15552
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@279ec358f4a1be4088be3e024b07916fa97c75b6
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-f56b79f" }'
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-473fe44" }'
|
||||
|
||||
6
.github/workflows/publish-docs.yml
vendored
6
.github/workflows/publish-docs.yml
vendored
@@ -41,13 +41,13 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fecfc0c
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c00c22ada3bd6bcda48fcb0d62fbbab49fec8a0f
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
@@ -91,4 +91,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
||||
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
||||
|
||||
77
.github/workflows/reusable-build-test-config.yml
vendored
77
.github/workflows/reusable-build-test-config.yml
vendored
@@ -69,6 +69,17 @@ on:
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
toolchain:
|
||||
description: 'Where the toolchain comes from ("nix" to build the flake CI environment on the runner, empty for the system one). macOS only: Linux always builds in a Nix image, and Nix has no Windows support.'
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
force_supported:
|
||||
description: "Flip every amendment to Supported::Yes before building. For perf/test builds only; never for release artifacts."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
secrets:
|
||||
CODECOV_TOKEN:
|
||||
description: "The Codecov token to use for uploading coverage reports."
|
||||
@@ -111,6 +122,9 @@ jobs:
|
||||
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||
VALIDATOR_KEYS_ENABLED: ${{ contains(inputs.cmake_args, '-Dvalidator_keys=ON') }}
|
||||
SANITIZERS_ENABLED: ${{ inputs.sanitizers != '' }}
|
||||
# The binaries reusable-package.yml consumes. A private repository skips
|
||||
# them except on a tag push, which is what produces its release packages.
|
||||
PACKAGING_ARTIFACTS_ENABLED: ${{ github.event.repository.visibility == 'public' || startsWith(github.ref, 'refs/tags/') }}
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
@@ -119,11 +133,30 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Force all amendments to Supported::Yes
|
||||
if: ${{ inputs.force_supported && runner.os == 'Linux' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
MACRO="include/xrpl/protocol/detail/features.macro"
|
||||
echo "Flipping Supported::No -> Supported::Yes in ${MACRO}:"
|
||||
grep -n 'Supported::No,' "${MACRO}" || echo " (none found)"
|
||||
sed -i 's/Supported::No,/Supported::Yes,/g' "${MACRO}"
|
||||
if grep -q 'Supported::No,' "${MACRO}"; then
|
||||
echo "::error::Supported::No entries remain after sed"
|
||||
exit 1
|
||||
fi
|
||||
git diff -- "${MACRO}" || true
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c00c22ada3bd6bcda48fcb0d62fbbab49fec8a0f
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: ${{ inputs.ccache_enabled }}
|
||||
|
||||
# Before any step that uses a build tool, composite actions included.
|
||||
- name: Setup Nix environment
|
||||
if: ${{ inputs.toolchain == 'nix' }}
|
||||
uses: ./.github/actions/setup-nix-env
|
||||
|
||||
- name: Set ccache log file
|
||||
if: ${{ inputs.ccache_enabled && runner.debug == '1' }}
|
||||
run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}"
|
||||
@@ -148,7 +181,21 @@ jobs:
|
||||
with:
|
||||
compiler: ${{ inputs.compiler }}
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
with:
|
||||
cache-directories: ${{ env.BUILD_DIR }}/corrosion
|
||||
key: ${{ inputs.config_name }}
|
||||
# two workspaces here because build artifacts are located in 2 places:
|
||||
# - crates/target when cargo is called directly
|
||||
# - build/cargo when cargo is called by cmake
|
||||
workspaces: |
|
||||
crates
|
||||
crates -> ${{ runner.os == 'Windows' && format('../{0}/x64/{1}/cargo', env.BUILD_DIR, inputs.build_type) || format('../{0}/cargo', env.BUILD_DIR) }}
|
||||
|
||||
# `setup-nix-env` already did this for the Nix toolchain.
|
||||
- name: Setup Conan
|
||||
if: ${{ inputs.toolchain != 'nix' }}
|
||||
env:
|
||||
SANITIZERS: ${{ inputs.sanitizers }}
|
||||
uses: ./.github/actions/setup-conan
|
||||
@@ -212,6 +259,24 @@ jobs:
|
||||
--target "${CMAKE_TARGET}" \
|
||||
2>&1 | tee "${GITHUB_WORKSPACE}/build.log"
|
||||
|
||||
# Nothing may reference the store, so whole trees are checked - the Conan
|
||||
# cache included, since what it holds is what gets uploaded and reused.
|
||||
- name: Check the build output for Nix store references (Nix toolchain)
|
||||
if: ${{ inputs.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${BUILD_DIR}"
|
||||
|
||||
- name: Check the Conan cache for Nix store references (Nix toolchain)
|
||||
if: ${{ inputs.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
|
||||
# Only what PatchNixBinary.cmake retargets: the toolchain in the Linux
|
||||
# images always references the store. Same condition it uses.
|
||||
- name: Check for Nix store references (Linux)
|
||||
if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }}
|
||||
run: |
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpld"
|
||||
./bin/check-nix-store-refs.sh "${BUILD_DIR}/xrpl_tests"
|
||||
|
||||
- name: Show ccache statistics
|
||||
if: ${{ inputs.ccache_enabled }}
|
||||
run: |
|
||||
@@ -222,7 +287,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
|
||||
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
@@ -236,7 +301,7 @@ jobs:
|
||||
run: ./validator-keys --unittest
|
||||
|
||||
- name: Upload the validator-keys binary
|
||||
if: ${{ github.event.repository.visibility == 'public' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
|
||||
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: validator-keys-${{ inputs.config_name }}
|
||||
@@ -323,6 +388,11 @@ jobs:
|
||||
|
||||
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee "${GITHUB_WORKSPACE}/unittest.log"
|
||||
|
||||
- name: Run Rust tests
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: crates
|
||||
run: cargo nextest run --workspace --all-features --locked --no-tests=warn
|
||||
|
||||
# Smoke-run every benchmark module with a single repetition to confirm the
|
||||
# benchmarks still build and execute. This is a correctness check, not a
|
||||
# performance measurement, so there is nothing to gain from repeating it
|
||||
@@ -394,6 +464,7 @@ jobs:
|
||||
disable_telem: true
|
||||
fail_ci_if_error: true
|
||||
files: ${{ env.BUILD_DIR }}/coverage.xml
|
||||
flags: cpp
|
||||
plugins: noop
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
|
||||
2
.github/workflows/reusable-build-test.yml
vendored
2
.github/workflows/reusable-build-test.yml
vendored
@@ -51,5 +51,7 @@ jobs:
|
||||
config_name: ${{ matrix.config_name }}
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
compiler: ${{ matrix.compiler || '' }}
|
||||
toolchain: ${{ matrix.toolchain || '' }}
|
||||
force_supported: ${{ matrix.force_supported || false }}
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
18
.github/workflows/reusable-clang-tidy.yml
vendored
18
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -27,14 +27,14 @@ jobs:
|
||||
determine-files:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@d041ac9f1fa9f07a4ba335eb4c1c82233fb3fef6
|
||||
uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@70145243b905dc3e040a61d39c00e178cfb96f71
|
||||
|
||||
run-clang-tidy:
|
||||
name: Run clang tidy
|
||||
needs: [determine-files]
|
||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-fecfc0c"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c00c22ada3bd6bcda48fcb0d62fbbab49fec8a0f
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
@@ -59,6 +59,12 @@ jobs:
|
||||
with:
|
||||
compiler: ${{ env.COMPILER }}
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
with:
|
||||
cache-directories: ${{ env.BUILD_DIR }}/corrosion
|
||||
workspaces: crates -> ../${{ env.BUILD_DIR }}/cargo
|
||||
|
||||
- name: Setup Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
|
||||
@@ -80,13 +86,13 @@ jobs:
|
||||
-Dwerr=ON \
|
||||
-Dxrpld=ON \
|
||||
-Dverify_headers=ON \
|
||||
-Drust=ON \
|
||||
..
|
||||
|
||||
# clang-tidy needs headers generated from proto files
|
||||
- name: Build libxrpl.libpb
|
||||
- name: Build clang-tidy prerequisites
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
run: |
|
||||
ninja -j ${{ steps.nproc.outputs.nproc }} xrpl.libpb
|
||||
ninja -j ${{ steps.nproc.outputs.nproc }} tidy_prerequisites
|
||||
|
||||
- name: Run clang tidy
|
||||
id: run_clang_tidy
|
||||
|
||||
250
.github/workflows/reusable-package.yml
vendored
250
.github/workflows/reusable-package.yml
vendored
@@ -1,17 +1,40 @@
|
||||
# Build Linux packages (DEB and RPM) from pre-built binary artifacts (xrpld and
|
||||
# validator-keys). Discovers which configurations to package from linux.json
|
||||
# (configs in "package_configs") and fans out one job per distro. Only
|
||||
# linux/amd64 is supported; the runner is hardcoded in the job below.
|
||||
# Build, verify and publish Linux packages from the pre-built xrpld and
|
||||
# validator-keys artifacts, in three stages:
|
||||
#
|
||||
# - 'package' builds and signs one format per config that carries a "package"
|
||||
# map in linux.json; that map names the container image and the format
|
||||
# - 'test-install' installs what was built on a range of distros and runs the
|
||||
# binaries there, so a package that cannot be installed never reaches Nexus
|
||||
# - 'publish' uploads with the image's publish_pkg.py, doing a --dry-run
|
||||
# unless 'publish: true'
|
||||
#
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the jobs below.
|
||||
name: Package
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pkg_release:
|
||||
description: "Package release number. Increment when repackaging the same executable."
|
||||
publish:
|
||||
description: "Whether to publish the packages after building them."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
nexus_url:
|
||||
description: "The base URL of the Nexus instance hosting the deb and rpm repositories."
|
||||
required: false
|
||||
type: string
|
||||
default: "1"
|
||||
default: https://packages.xrplf.org
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
description: "The username of a Nexus account with write access to the repositories."
|
||||
required: false
|
||||
remote_password:
|
||||
description: "The password or token for that Nexus account."
|
||||
required: false
|
||||
signing_key:
|
||||
description: "Armoured PGP private key used to sign the RPMs. Required when publishing."
|
||||
required: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -19,6 +42,7 @@ defaults:
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
PACKAGE_DIR: packages
|
||||
|
||||
jobs:
|
||||
generate-matrix:
|
||||
@@ -41,7 +65,7 @@ jobs:
|
||||
|
||||
package:
|
||||
needs: [generate-matrix]
|
||||
if: ${{ github.event.repository.visibility == 'public' }}
|
||||
if: ${{ github.event.repository.visibility == 'public' || startsWith(github.ref, 'refs/tags/') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
@@ -50,12 +74,17 @@ jobs:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
- name: Download pre-built xrpld binary
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
@@ -71,17 +100,210 @@ jobs:
|
||||
- name: Make binaries executable
|
||||
run: chmod +x "${BUILD_DIR}/xrpld" "${BUILD_DIR}/validator-keys"
|
||||
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
- name: Build package
|
||||
env:
|
||||
PKG_RELEASE: ${{ inputs.pkg_release }}
|
||||
run: ./package/build_pkg.sh
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
PKG_RELEASE: ${{ steps.release_info.outputs.pkg_release }}
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: |
|
||||
./package/build_pkg.py \
|
||||
--package-type "${PACKAGE_TYPE}" \
|
||||
--build-dir "${BUILD_DIR}" \
|
||||
--pkg-release "${PKG_RELEASE}" \
|
||||
--channel "${CHANNEL}"
|
||||
|
||||
# Before the upload, so the artifact, the tested package and the published
|
||||
# package are the same bytes.
|
||||
- name: Sign RPM
|
||||
if: ${{ inputs.publish && matrix.package_type == 'rpm' }}
|
||||
env:
|
||||
PKG_SIGNING_KEY: ${{ secrets.signing_key }}
|
||||
run: ./package/sign_rpm.py --package-dir "${BUILD_DIR}"
|
||||
|
||||
# Split from the debug symbols, which are an order of magnitude larger, so
|
||||
# that test-install downloads only what it installs.
|
||||
- name: Upload package artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld_*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload debug symbol artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg-debug
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-debuginfo-*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
# Every distro family the packages target, oldest release first, so both ends
|
||||
# of the dependency range they declare are exercised.
|
||||
test-install:
|
||||
needs: [package]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- package_type: deb
|
||||
image: debian:11
|
||||
- package_type: deb
|
||||
image: debian:12
|
||||
- package_type: deb
|
||||
image: debian:13
|
||||
- package_type: deb
|
||||
image: ubuntu:20.04
|
||||
- package_type: deb
|
||||
image: ubuntu:22.04
|
||||
- package_type: deb
|
||||
image: ubuntu:24.04
|
||||
- package_type: deb
|
||||
image: ubuntu:26.04
|
||||
|
||||
- package_type: rpm
|
||||
image: almalinux:9
|
||||
- package_type: rpm
|
||||
image: almalinux:10
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:9
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:10
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi9/ubi
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi10/ubi
|
||||
name: "install ${{ matrix.package_type }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# Both formats land in one directory; the step below picks its own by
|
||||
# extension, so this stays independent of the artifact names.
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Find the package
|
||||
id: find
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
run: |
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "*.${PACKAGE_TYPE}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no .${PACKAGE_TYPE} found in ${PACKAGE_DIR}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "package=${package}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Debian 11 went end-of-life on 2026-08-31
|
||||
# (https://www.debian.org/News/2026/20260831) and its packages are
|
||||
# already partly gone from deb.debian.org, so switch to the
|
||||
# snapshot.debian.org entries the image ships commented out in its
|
||||
# sources.list: they are pinned to the snapshot the image was built
|
||||
# from, so they serve every version it needs and never go away.
|
||||
# Snapshots keep their original, long-passed Valid-Until, hence the
|
||||
# disabled check; the retries absorb snapshot.debian.org's throttling.
|
||||
- name: Switch Debian 11 to snapshot.debian.org
|
||||
if: ${{ matrix.image == 'debian:11' }}
|
||||
run: |
|
||||
sed -i 's|^deb |# deb |; s|^# deb http://snapshot|deb http://snapshot|' /etc/apt/sources.list
|
||||
printf '%s\n' \
|
||||
'Acquire::Check-Valid-Until "false";' \
|
||||
'Acquire::Retries "3";' \
|
||||
>/etc/apt/apt.conf.d/99snapshot
|
||||
|
||||
- name: Install the DEB
|
||||
if: ${{ matrix.package_type == 'deb' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: |
|
||||
# Stock Debian and Ubuntu images carry no package lists, so apt has
|
||||
# nothing to resolve the systemd dependency from until it fetches them.
|
||||
apt-get update -qq
|
||||
apt-get install -y "./${PACKAGE}"
|
||||
|
||||
- name: Install the RPM
|
||||
if: ${{ matrix.package_type == 'rpm' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: dnf install -y "./${PACKAGE}"
|
||||
|
||||
- name: Run xrpld
|
||||
run: xrpld --version
|
||||
|
||||
- name: Run validator-keys
|
||||
run: validator-keys --version
|
||||
|
||||
- name: Run rippled, the legacy compatibility symlink
|
||||
run: rippled --version
|
||||
|
||||
- name: Check the service account
|
||||
run: id xrpld
|
||||
|
||||
- name: Check the state directory
|
||||
run: test -d /var/lib/xrpld
|
||||
|
||||
- name: Check the log directory
|
||||
run: test -d /var/log/xrpld
|
||||
|
||||
publish:
|
||||
needs: [generate-matrix, package, test-install]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
# Both artifacts, so the debug symbols are published alongside the package.
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: ${{ matrix.xrpld_artifact_name }}-pkg*
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
- name: Publish package
|
||||
env:
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
DRY_RUN_OPTION: ${{ !inputs.publish && '--dry-run' || '' }}
|
||||
NEXUS_URL: ${{ inputs.nexus_url }}
|
||||
NEXUS_USERNAME: ${{ inputs.publish && secrets.remote_username || '' }}
|
||||
NEXUS_PASSWORD: ${{ inputs.publish && secrets.remote_password || '' }}
|
||||
run: |
|
||||
publish_pkg.py \
|
||||
--channel "${CHANNEL}" \
|
||||
--package-dir "${PACKAGE_DIR}" \
|
||||
--nexus-url "${NEXUS_URL}" \
|
||||
${DRY_RUN_OPTION}
|
||||
|
||||
84
.github/workflows/reusable-rust.yml
vendored
Normal file
84
.github/workflows/reusable-rust.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# Clippy, coverage and documentation for the Rust crates in crates/. Each runs
|
||||
# as an independent job on a GitHub-hosted runner, but inside the same container
|
||||
# image used to build the crates in the C++/Corrosion path, so the toolchain
|
||||
# (and therefore the lints and the cargo cache) matches what production builds
|
||||
# use. Coverage is the exception: it needs the nightly rustc that honours
|
||||
# #[coverage(off)], which the image carries alongside the pinned stable.
|
||||
#
|
||||
# Rust unit tests are deliberately NOT run here. They run as part of the C++
|
||||
# build (reusable-build-test-config.yml), which already compiles the crates on a
|
||||
# self-hosted runner, so there is no need to provision a toolchain again.
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CODECOV_TOKEN:
|
||||
description: "The Codecov token to use for uploading coverage reports."
|
||||
required: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: crates
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
|
||||
- name: Run clippy
|
||||
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Use the nightly Rust toolchain
|
||||
run: rust-nightly path >>"${GITHUB_PATH}"
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
|
||||
- name: Generate coverage report
|
||||
run: cargo llvm-cov nextest --workspace --all-features --locked --no-tests=warn --lcov --output-path lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
fail_ci_if_error: true
|
||||
files: crates/lcov.info
|
||||
flags: rust
|
||||
plugins: noop
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
|
||||
- name: Build documentation
|
||||
env:
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
run: cargo doc --workspace --no-deps --all-features --locked
|
||||
19
.github/workflows/reusable-upload-recipe.yml
vendored
19
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -40,7 +40,7 @@ defaults:
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-fecfc0c
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
@@ -49,9 +49,14 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Generate build version number
|
||||
id: version
|
||||
uses: ./.github/actions/generate-version
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
- name: Set up Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
@@ -64,8 +69,8 @@ jobs:
|
||||
|
||||
- name: Upload Conan recipe (version)
|
||||
run: |
|
||||
conan export . --version=${{ steps.version.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
|
||||
conan export . --version=${{ steps.release_info.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.release_info.outputs.version }}
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when merging into the
|
||||
# 'develop' branch, see on-trigger.yml.
|
||||
@@ -92,4 +97,4 @@ jobs:
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
|
||||
|
||||
outputs:
|
||||
ref: xrpl/${{ steps.version.outputs.version }}
|
||||
ref: xrpl/${{ steps.release_info.outputs.version }}
|
||||
|
||||
13
.github/workflows/upload-conan-deps.yml
vendored
13
.github/workflows/upload-conan-deps.yml
vendored
@@ -68,10 +68,15 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@c00c22ada3bd6bcda48fcb0d62fbbab49fec8a0f
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
# Before any step that uses a build tool, composite actions included.
|
||||
- name: Setup Nix environment
|
||||
if: ${{ matrix.toolchain == 'nix' }}
|
||||
uses: ./.github/actions/setup-nix-env
|
||||
|
||||
- name: Print build environment
|
||||
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
|
||||
|
||||
@@ -87,7 +92,9 @@ jobs:
|
||||
with:
|
||||
compiler: ${{ matrix.compiler }}
|
||||
|
||||
# `setup-nix-env` already did this for the Nix toolchain.
|
||||
- name: Setup Conan
|
||||
if: ${{ matrix.toolchain != 'nix' }}
|
||||
env:
|
||||
SANITIZERS: ${{ matrix.sanitizers }}
|
||||
uses: ./.github/actions/setup-conan
|
||||
@@ -106,6 +113,10 @@ jobs:
|
||||
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
|
||||
- name: Check the Conan cache for Nix store references (Nix toolchain)
|
||||
if: ${{ matrix.toolchain == 'nix' }}
|
||||
run: ./bin/check-nix-store-refs.sh "${CONAN_HOME}"
|
||||
|
||||
- name: Log into Conan remote
|
||||
if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
|
||||
run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_REMOTE_PASSWORD }}"
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -72,11 +72,16 @@ DerivedData
|
||||
/.zed/
|
||||
|
||||
# AI tools.
|
||||
# Shared/committable AI agent config (AGENTS.md, CLAUDE.md, GEMINI.md, .claude/settings.json,
|
||||
# tool-specific rules files, etc.) should be checked in — see CONTRIBUTING.md. Only the
|
||||
# personal/local variants below are ignored.
|
||||
/.agent
|
||||
/.agents
|
||||
/.augment
|
||||
/.claude
|
||||
/CLAUDE.md
|
||||
/.claude/settings.local.json
|
||||
AGENTS.override.md
|
||||
CLAUDE.local.md
|
||||
GEMINI.local.md
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
@@ -89,3 +94,6 @@ target/
|
||||
|
||||
# clangd cache
|
||||
/.cache
|
||||
|
||||
# Rust build directory
|
||||
crates/target
|
||||
|
||||
@@ -62,6 +62,20 @@ repos:
|
||||
types_or: [c++, c, proto]
|
||||
exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --manifest-path crates/Cargo.toml --all
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false # rustfmt formats the whole workspace
|
||||
- id: check-coverage-attrs
|
||||
name: check Rust coverage attributes
|
||||
entry: ./bin/pre-commit/check_rust_coverage_attrs.py
|
||||
language: python
|
||||
files: ^crates/.*\.rs$
|
||||
|
||||
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
|
||||
rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7
|
||||
hooks:
|
||||
@@ -73,11 +87,27 @@ repos:
|
||||
- id: prettier
|
||||
args: [--end-of-line=auto]
|
||||
|
||||
# Scoped to package/: the rest of the repo's Python has pre-existing findings,
|
||||
# so widening these is its own change.
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 7c55798a78262d14b2074abf623d8a992ebb70d4 # frozen: v0.16.2
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
files: ^package/.*\.py$
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: 41e691678310dfd3833f7ab4e180ddb014310356 # frozen: v2.3.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
args: [--strict]
|
||||
files: ^package/.*\.py$
|
||||
|
||||
- repo: https://github.com/scop/pre-commit-shfmt
|
||||
rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1
|
||||
hooks:
|
||||
|
||||
42
AGENTS.md
Normal file
42
AGENTS.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents (Claude Code, and other AGENTS.md-compatible tools) when working with code in this repository.
|
||||
|
||||
## Build
|
||||
|
||||
Required on Linux/macOS: use the Nix devshell, which sets up the compiler, Conan, ccache, and (optionally) Rust automatically.
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
```
|
||||
|
||||
For alternate devshell variants (specific compiler, no-compiler, coverage), see [docs/build/nix.md](./docs/build/nix.md). For the manual build steps, CMake options, and protocol codegen commands, see [BUILD.md](./BUILD.md) (`## Steps`, `## Options`, `## Code generation`).
|
||||
|
||||
Rust crate tests (independent of the CMake build): `cargo test --manifest-path crates/Cargo.toml --workspace` (CI uses `cargo nextest`).
|
||||
|
||||
## Testing
|
||||
|
||||
Unit tests are a custom framework built into the `xrpld` binary itself (not Boost.Test/GTest/Catch); see [CONTRIBUTING.md](./CONTRIBUTING.md#unit-tests) for the basic invocation. Notes not covered there:
|
||||
|
||||
- A suite's `--unittest` name is built from the arguments to its `BEAST_DEFINE_TESTSUITE`/`BEAST_DEFINE_TESTSUITE_PRIO` macro (usually at the bottom of the test file), in reverse order and joined with `.`: `BEAST_DEFINE_TESTSUITE(Credentials, app, xrpl)` → `xrpl.app.Credentials`.
|
||||
- `--unittest-arg` does nothing — don't use it.
|
||||
- Tests that run offline in under a minute should be automatic `--unittest` suites; anything else is a manual/integration test.
|
||||
- New tests should be written using `gtest` under `src/tests/` unless that isn't possible, in which case fall back to the legacy Beast framework under `src/test/`. `tests/` (top-level) holds integration tests exercised against `libxrpl`/`xrpld`.
|
||||
|
||||
## Lint/Format
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md#pre-commit-hooks) for `pre-commit` setup and [CONTRIBUTING.md](./CONTRIBUTING.md#clang-tidy) for `clang-tidy` (opt-in, needs local `clang-tidy` and generated headers).
|
||||
|
||||
## Code Style
|
||||
|
||||
New file placement and header levelization: see [CONTRIBUTING.md](./CONTRIBUTING.md#before-making-a-pull-request). Braces, whitespace, member order, and other conventions: see [docs/CodingStyle.md](./docs/CodingStyle.md). `XRPL_ASSERT`/`UNREACHABLE` contracts: see [CONTRIBUTING.md](./CONTRIBUTING.md#contracts-and-instrumentation). Commit messages: see [CONTRIBUTING.md](./CONTRIBUTING.md#good-commit-messages).
|
||||
|
||||
## Architecture
|
||||
|
||||
Paths below reflect the current layout; update this section if modularization moves a subsystem to a different directory.
|
||||
|
||||
- `include/xrpl/` + `src/libxrpl/` — the core protocol library: ledger, shamap, consensus, crypto, json, resource, nodestore, rdb, peerfinder, and `tx/` (transaction application: `Transactor.cpp`, `applySteps.cpp`, invariants, payment paths). `tx/transactors/` has one file per transaction type, grouped by subsystem: `escrow/`, `vault/`, `lending/`, `sponsor/`, `nft/`, `token/` (MPT), `payment_channel/`, `permissioned_domain/`, `dex/`, `oracle/`, `did/`, `credentials/`, `bridge/`, `check/`, `delegate/`, `account/`, `system/`. Any change to transaction-processing behavior must be gated behind an Amendment.
|
||||
- `src/xrpld/` — the server application built on top of `libxrpl`: `app`, `core`, `overlay` (P2P networking), `peerfinder`, `perflog`, `rpc`, `shamap`. `main` builds an `ApplicationImp` implementing `Application`; most components hold a reference to it (`app_`), giving broad cross-component access — expect to trace call chains through `Application&`.
|
||||
- `src/test/` — unit tests mirroring the subsystems above, plus `jtx/` (the transaction-building test DSL — e.g. `jtx/escrow.h`, `jtx/vault.h`, `jtx/sponsor.h`, `jtx/permissioned_dex.h`) and `unit_test/` (the custom test framework itself, derived from Beast).
|
||||
- `src/tests/` — unit tests for `libxrpl` written in `gtest`, gradually replacing the `src/test` equivalents.
|
||||
- `crates/` — a Rust workspace (only built with `-Dxrpld -Drust=ON`) bridged into C++ via `cxxbridge`/the `cxx` crate; currently just a `hello_world` interop scaffold. Requires the Rust toolchain pinned in `rust-toolchain.toml` (the Nix devshell provides it automatically).
|
||||
@@ -22,17 +22,56 @@ API version 2 is available in `xrpld` version 2.0.0 and later. See [API-VERSION-
|
||||
|
||||
This version is supported by all `xrpld` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
## Unreleased
|
||||
## XRP Ledger server version 3.4.0
|
||||
|
||||
This section contains changes targeting a future version.
|
||||
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
||||
|
||||
### Additions
|
||||
### Additions in 3.4.0
|
||||
|
||||
- `book_offers`, `account_offers`: With the `OfferQualifiers` amendment, an offer entry may now include `all_or_none: true` (the offer carries the `lsfAllOrNone` flag) and/or `min_quantity` (the offer's `MinQuantity` amount). These mark execution-qualified ("contingent") offers that cannot be taken to arbitrary depth; clients should exclude them from quoted/takeable depth. The `OfferCreate` transaction gains the `tfAllOrNone` and `tfPostOnly` flags and an optional `MinQuantity` field, and a marketable `tfPostOnly` offer is rejected with the new `tecWOULD_CROSS` result.
|
||||
|
||||
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
|
||||
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
|
||||
- `ledger`: `nftoken_id`, `nftoken_ids`, and `offer_id` are now included in transaction metadata when transactions are expanded (`expand`, or admin-only `full`), matching the `tx`, `account_tx`, and `subscribe` (`transactions` stream) responses. ([#5706](https://github.com/XRPLF/rippled/pull/5706))
|
||||
|
||||
### Bugfixes in 3.4.0
|
||||
|
||||
- `sign`, `sign_for`, `submit`: `signature_target` now returns `invalidParams` unless it names `CounterpartySignature` or `SponsorSignature`. It previously accepted any inner object field, such as `Book` or `NFToken`, and signed into it.
|
||||
- `sign`, `sign_for`, `submit`, `submit_multisigned`: With `fixCleanup3_4_0` enabled, a signature in `CounterpartySignature` or `SponsorSignature` covers a different prefix than the transaction's own signature, so a signature can no longer be moved from one of those roles into another. Clients that build these signatures themselves must use the new prefixes: `CPT` and `CPM` (single- and multi-signing) for `CounterpartySignature`, and `SPN` and `SPM` for `SponsorSignature`.
|
||||
- `get_aggregate_price`: Duplicate entries in the `oracles` request array are now ignored. [#6586](https://github.com/XRPLF/rippled/pull/6586)
|
||||
- `vault_info`: Errors now identify what the request got wrong instead of reporting every failure as the unregistered token `malformedRequest`, and the `error`, `error_code` and `error_message` fields now agree with each other. An invalid `vault_id` or `seq` returns `invalidParams`, an invalid `owner` returns `actMalformed`, and a request that mixes `vault_id` with `owner`/`seq` or supplies neither returns `invalidParams` with a message naming the accepted combinations. [#8015](https://github.com/XRPLF/rippled/pull/8015)
|
||||
- `vault_info`: A well-formed all-zero `vault_id` now returns `entryNotFound` instead of being rejected as malformed, and `entryNotFound` responses now include `error_code` and `error_message`. Clients that request `ripplerpc` 3.0 or above therefore receive HTTP 400 with that error rather than HTTP 200. [#8015](https://github.com/XRPLF/rippled/pull/8015)
|
||||
- `vault_info`: `vault_id` and `owner` must now be strings, matching how `ledger_entry` reads the same fields. An object or an array in either field previously produced an internal error, and a number was silently converted to its decimal text; `vault_id` now returns `invalidParams` and `owner` returns `actMalformed`. [#8015](https://github.com/XRPLF/rippled/pull/8015)
|
||||
- `gateway_balances`: The `account` and `ident` fields now return an `invalidParams` error if the value is not a string, instead of an `internal` error. [#7655](https://github.com/XRPLF/rippled/pull/7655)
|
||||
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
|
||||
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
|
||||
|
||||
## XRP Ledger server version 3.3.0
|
||||
|
||||
[Version 3.3.0](https://github.com/XRPLF/rippled/releases/tag/3.3.0) was released on Aug 6, 2026.
|
||||
|
||||
### Additions in 3.3.0
|
||||
|
||||
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`. When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present. ([#6126](https://github.com/XRPLF/rippled/pull/6126))
|
||||
|
||||
## XRP Ledger server version 3.2.1
|
||||
|
||||
[Version 3.2.1](https://github.com/XRPLF/rippled/releases/tag/3.2.1) was released on Aug 1, 2026.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 3.2.0
|
||||
|
||||
[Version 3.2.0](https://github.com/XRPLF/rippled/releases/tag/3.2.0) was released on Jun 16, 2026.
|
||||
|
||||
### Additions in 3.2.0
|
||||
|
||||
- `ledger_entry`, `account_objects`: Added the `Ballot` and `BallotVote` ledger entry types introduced by the `ConfidentialVoting` amendment. `ledger_entry` accepts a `ballot` request object (`owner` + `seq`) and a `ballot_vote` request object (`ballot_id` + `account`), or a hex object ID for either. `account_objects` returns these entries and accepts them as `type` filters.
|
||||
|
||||
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
|
||||
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
|
||||
|
||||
- `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681))
|
||||
|
||||
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
||||
- `TRANSACTION_FORMATS`: Describes the fields and their optionality for each transaction type, including common fields shared across all transactions.
|
||||
- `LEDGER_ENTRY_FORMATS`: Describes the fields and their optionality for each ledger entry type, including common fields shared across all ledger entries.
|
||||
@@ -40,9 +79,8 @@ This section contains changes targeting a future version.
|
||||
- `LEDGER_ENTRY_FLAGS`: Maps ledger entry type names to their flags and flag values.
|
||||
- `ACCOUNT_SET_FLAGS`: Maps AccountSet flag names (asf flags) to their numeric values.
|
||||
|
||||
### Bugfixes
|
||||
### Bugfixes in 3.2.0
|
||||
|
||||
- `get_aggregate_price`: Duplicate entries in the `oracles` request array are now ignored. [#6586](https://github.com/XRPLF/rippled/pull/6586)
|
||||
- Peer Crawler: The `port` field in `overlay.active[]` now consistently returns an integer instead of a string for outbound peers. [#6318](https://github.com/XRPLF/rippled/pull/6318)
|
||||
- `ping`: The `ip` field is no longer returned as an empty string for proxied connections without a forwarded-for header. It is now omitted, consistent with the behavior for identified connections. [#6730](https://github.com/XRPLF/rippled/pull/6730)
|
||||
- gRPC `GetLedgerDiff`: Fixed error message that incorrectly said "base ledger not validated" when the desired ledger was not validated. [#6730](https://github.com/XRPLF/rippled/pull/6730)
|
||||
@@ -54,8 +92,24 @@ This section contains changes targeting a future version.
|
||||
- `submit`: The `fail_hard` field now returns an error if the value is not a boolean. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `subscribe`: The `taker` field in the `books` array now returns `actMalformed` instead of `badIssuer` if the value is not a valid account. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- Fixed a bug in `Forwarded` HTTP header parsing where the extracted IP address could be incorrect when no comma or semicolon delimiter follows the address. This could cause the server to misidentify a client's IP address when operating behind a reverse proxy. [#6529](https://github.com/XRPLF/rippled/pull/6529)
|
||||
- `gateway_balances`: The `account` and `ident` fields now return an `invalidParams` error if the value is not a string, instead of an `internal` error. [#7655](https://github.com/XRPLF/rippled/pull/7655)
|
||||
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
|
||||
|
||||
## XRP Ledger server version 3.1.3
|
||||
|
||||
[Version 3.1.3](https://github.com/XRPLF/rippled/releases/tag/3.1.3) was released on May 8, 2026.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 3.1.2
|
||||
|
||||
[Version 3.1.2](https://github.com/XRPLF/rippled/releases/tag/3.1.2) was released on Mar 12, 2026.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 3.1.1
|
||||
|
||||
[Version 3.1.1](https://github.com/XRPLF/rippled/releases/tag/3.1.1) was released on Feb 23, 2026.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
|
||||
25
BUILD.md
25
BUILD.md
@@ -304,6 +304,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
||||
| ---------------- | ------------- | ----------------------------------------------------------------------------- |
|
||||
| `assert` | OFF | Force enabling assertions. |
|
||||
| `coverage` | OFF | Prepare the coverage report. |
|
||||
| `rust` | OFF | Build the Rust crates and the C++ code that depends on them. |
|
||||
| `tests` | OFF | Build tests. |
|
||||
| `unity` | OFF | Configure a unity build. |
|
||||
| `verify_headers` | ON | Make the `verify-headers` target available to compile each header on its own. |
|
||||
@@ -316,6 +317,30 @@ memory) since they concatenate sources into fewer translation units. Non-unity
|
||||
builds may be faster for incremental builds, and can be helpful for detecting
|
||||
`#include` omissions.
|
||||
|
||||
### Rust crates
|
||||
|
||||
The Rust crates in `crates/` are only part of the build when `rust` is ON. With
|
||||
`-Drust=OFF` (the default) the `crates` directory is not added to the build, no
|
||||
cxxbridge bindings are generated, and the C++ tests that exercise the Rust
|
||||
interop are not compiled — so no Rust toolchain is needed. CI builds always pass
|
||||
`-Drust=ON`.
|
||||
|
||||
With `-Drust=ON` you need one extra dependency: a Rust toolchain (`cargo`,
|
||||
`rustc`) matching the channel pinned in
|
||||
[`rust-toolchain.toml`](./rust-toolchain.toml), which compiles the crates and
|
||||
generates the cxxbridge bindings. It is provided by the
|
||||
[Nix development shell](./docs/build/nix.md), so `-Drust=ON` works there without
|
||||
any extra setup; otherwise install it as described in
|
||||
[Rust](./docs/build/environment.md#rust).
|
||||
|
||||
The crates also have their own Rust unit tests. Those are run with `cargo` and
|
||||
need only the Rust toolchain, independently of CMake and of the `rust` option
|
||||
(CI runs them with `cargo nextest`):
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path crates/Cargo.toml --workspace
|
||||
```
|
||||
|
||||
### Verifying headers
|
||||
|
||||
The regular build only compiles `.cpp` files, so a header is only ever checked
|
||||
|
||||
@@ -99,6 +99,7 @@ if(only_docs)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(deps/dilithium)
|
||||
include(deps/Boost)
|
||||
|
||||
add_subdirectory(external/antithesis-sdk)
|
||||
@@ -114,6 +115,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(
|
||||
@@ -158,7 +160,13 @@ if(coverage)
|
||||
include(XrplCov)
|
||||
endif()
|
||||
|
||||
add_custom_target(tidy_prerequisites)
|
||||
|
||||
if(rust)
|
||||
add_subdirectory(crates)
|
||||
endif()
|
||||
include(XrplCore)
|
||||
|
||||
include(XrplProtocolAutogen)
|
||||
include(XrplInstall)
|
||||
include(XrplValidatorKeys)
|
||||
|
||||
@@ -59,6 +59,17 @@ to an existing XLS. Neither change will be released (in an amendment's
|
||||
case, marked as `Supported::yes`) until the corresponding XLS's status
|
||||
is `Final`.
|
||||
|
||||
## AI coding agents
|
||||
|
||||
[`AGENTS.md`](./AGENTS.md) (and its `CLAUDE.md` symlink, for Claude Code) holds shared, checked-in guidance for AI coding agents working in this repository — build/test/lint commands and architecture notes. Additional `AGENTS.md` files may exist in subdirectories to give agents context specific to that part of the codebase; whenever you add one, also add a `CLAUDE.md` symlink pointing to it (`ln -s AGENTS.md CLAUDE.md`) so Claude Code picks it up too.
|
||||
|
||||
If you want to give an agent personal instructions that shouldn't be shared with other contributors (e.g. your own workflow preferences), those are gitignored, not checked in:
|
||||
|
||||
- `CLAUDE.local.md` — read by Claude Code alongside `CLAUDE.md`.
|
||||
- `AGENTS.override.md` — read by AGENTS.md-compatible tools that support a personal override file layered on top of `AGENTS.md`.
|
||||
|
||||
Likewise, `.claude/settings.local.json` is for personal, untracked Claude Code settings, while `.claude/settings.json` is shared.
|
||||
|
||||
## Before making a pull request
|
||||
|
||||
(Or marking a draft pull request as ready.)
|
||||
@@ -82,7 +93,7 @@ If you create new source files, they must be organized as follows:
|
||||
under `include/xrpl`, and source (`.cpp`) files must go under
|
||||
`src/libxrpl`.
|
||||
- All other non-test files must go under `src/xrpld`.
|
||||
- All test source files must go under `src/test`.
|
||||
- New test source files should use `gtest` and go under `src/tests`, unless that isn't possible, in which case they should use our legacy test framework and go under `src/test`.
|
||||
- All benchmark source files must go under `src/benchmarks`.
|
||||
|
||||
The source must be formatted according to the style guide below. The easiest
|
||||
@@ -225,8 +236,9 @@ environment, so you don't need to install most of the individual tools
|
||||
yourself. The version of each hook sourced from an external repository
|
||||
(`clang-format`, `gersemi`, etc.) is pinned in that file, so running the hooks
|
||||
locally uses exactly the same versions as CI. A few `local` hooks — most notably
|
||||
`clang-tidy` — run tools from your own environment; see
|
||||
[Installing clang-tidy](#installing-clang-tidy) for how to get those.
|
||||
`clang-tidy` and `cargo fmt` — run tools from your own environment; see
|
||||
[Installing clang-tidy](#installing-clang-tidy) and
|
||||
[Rust](./docs/build/environment.md#rust) for how to get those.
|
||||
|
||||
To get started, install `pre-commit` and enable the git hook scripts:
|
||||
|
||||
@@ -255,6 +267,7 @@ The hooks configured in this repository include, among others:
|
||||
- `clang-tidy` — C++ static analysis (see [Clang-tidy](#clang-tidy)); opt in with `TIDY=1`
|
||||
- `fix-include-style`, `fix-pragma-once`, `check-doxygen-style` — C++ hygiene
|
||||
- `gersemi` — CMake formatting
|
||||
- `cargo fmt` — Rust formatting for the crates in `crates/`
|
||||
- `prettier`, `black`, `shfmt` — formatting for JavaScript/JSON/Markdown, Python, and shell
|
||||
- `cspell` — spell checking
|
||||
|
||||
@@ -319,7 +332,11 @@ See the [environment setup guide](./docs/build/environment.md#clang-tidy) for ho
|
||||
|
||||
### Running clang-tidy locally
|
||||
|
||||
Before running clang-tidy, you must build the project to generate required files (particularly protobuf headers). Refer to [`BUILD.md`](./BUILD.md) for build instructions.
|
||||
Before running clang-tidy, you must generate the files it depends on (protobuf headers, and, when the project is configured with `-Drust=ON`, the cxxbridge headers from the Rust crates). Configure the project as described in [`BUILD.md`](./BUILD.md), then build the `tidy_prerequisites` target, which generates all of them:
|
||||
|
||||
```bash
|
||||
cmake --build build --target tidy_prerequisites
|
||||
```
|
||||
|
||||
#### Via pre-commit (recommended)
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ Here are some good places to start learning the source code:
|
||||
| `./docs` | Source documentation files and doxygen config. |
|
||||
| `./cfg` | Example configuration files. |
|
||||
| `./src` | Source code. |
|
||||
| `./crates` | Rust source code. |
|
||||
|
||||
Some of the directories under `src` are external repositories included using
|
||||
git-subtree. See those directories' README files for more details.
|
||||
|
||||
111
bin/check-nix-store-refs.sh
Executable file
111
bin/check-nix-store-refs.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fail if a binary under <path> records a /nix/store path it resolves at run
|
||||
# time. See docs/build/nix.md#prebuilt-packages for why that matters.
|
||||
#
|
||||
# <path> is a file or a directory. macOS: nothing may reference the store, so
|
||||
# point it at whole trees. Linux: the toolchain always writes the store into
|
||||
# PT_INTERP and RUNPATH, so only at what cmake/PatchNixBinary.cmake retargets.
|
||||
#
|
||||
# Only Mach-O / ELF is inspected. Static archives hold store paths in debug info
|
||||
# alone; the scripts in a Conan cache are all git hook samples and autotools
|
||||
# scratch, 36 false positives to 0 real.
|
||||
#
|
||||
# Usage: bin/check-nix-store-refs.sh <path>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 <path>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -e "$1" ]; then
|
||||
echo "$0: no such path: $1" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
format=Mach-O
|
||||
recorded_paths=macho_recorded_paths
|
||||
tool=otool
|
||||
;;
|
||||
Linux)
|
||||
format=ELF
|
||||
recorded_paths=elf_recorded_paths
|
||||
tool=readelf
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported OS - skipping the Nix store reference check."
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# `pipefail` would catch this too, but only as a bare nonzero exit.
|
||||
if ! command -v "${tool}" >/dev/null; then
|
||||
echo "$0: ${tool} not found; cannot inspect binaries" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Both list what the file records. `ldd` would answer what this machine resolves
|
||||
# now, which is wrong both ways: store paths for a correctly patched binary,
|
||||
# silence for a store RUNPATH that resolves nowhere.
|
||||
|
||||
# `name` covers LC_ID_DYLIB and LC_LOAD*_DYLIB, `path` covers LC_RPATH.
|
||||
macho_recorded_paths() {
|
||||
otool -l "$1" | sed -nE 's#^ *(name|path) ([^ ]*).*#\2#p'
|
||||
}
|
||||
|
||||
# RPATH and RUNPATH are colon-separated.
|
||||
elf_recorded_paths() {
|
||||
readelf -ldW "$1" |
|
||||
sed -nE \
|
||||
-e 's#.*program interpreter: ([^]]*)\].*#\1#p' \
|
||||
-e 's#.*\((RPATH|RUNPATH|NEEDED)\).*\[([^]]*)\].*#\2#p' |
|
||||
tr ':' '\n'
|
||||
}
|
||||
|
||||
checked=0
|
||||
skipped=0
|
||||
leaked=0
|
||||
|
||||
while IFS= read -r file; do
|
||||
case "$(file -b "${file}" 2>/dev/null)" in
|
||||
*"${format}"*) ;;
|
||||
*)
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
checked=$((checked + 1))
|
||||
|
||||
# Filter after extracting, or a search path starting elsewhere ($ORIGIN)
|
||||
# hides the rest. `sed` not `grep`: grep calls "no matches" a failure, and
|
||||
# the `|| true` that would need masks a broken pipeline too.
|
||||
refs="$("${recorded_paths}" "${file}" | sed -n '\#^/nix/store/#p' | sort -u)"
|
||||
if [ -n "${refs}" ]; then
|
||||
leaked=$((leaked + 1))
|
||||
echo "::error file=${file}::references the Nix store at run time"
|
||||
echo "${file}"
|
||||
echo "${refs}" | sed 's/^/ /'
|
||||
fi
|
||||
done < <(find "$1" -type f \( -perm -u+x -o -name '*.dylib' -o -name '*.so*' \))
|
||||
|
||||
echo "$1: checked ${checked}, skipped ${skipped}, ${leaked} with Nix store references."
|
||||
|
||||
if [ "${leaked}" -ne 0 ]; then
|
||||
cat >&2 <<'EOF'
|
||||
|
||||
Fixes, in order of preference:
|
||||
- A Conan package built before this check existed: drop it
|
||||
(`conan remove '<name>/*'`) and rebuild.
|
||||
- A binary that should have been retargeted to the system loader: check that
|
||||
cmake/PatchNixBinary.cmake ran for it.
|
||||
- Link the macOS system library instead of the Nix one - see
|
||||
libresolvSystemStub in nix/darwin.nix.
|
||||
- No system library exists (libstdc++): link it statically.
|
||||
- None of the above: pin the toolchain into the package ID, following
|
||||
`user.package:libc_version` in conan/profiles/ci.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
@@ -15,10 +15,14 @@
|
||||
# - Windows: the core build tools only (CMake, Conan, Git, Python).
|
||||
# MSVC is expected to be provided separately and is not checked here.
|
||||
#
|
||||
# Some tools (clang-format, doxygen, gcovr, gh, git-cliff, gpg, pre-commit,
|
||||
# run-clang-tidy) are present in our Linux CI images and in local development
|
||||
# setups, but not in the macOS CI environment. They are checked everywhere
|
||||
# except when running in CI on macOS.
|
||||
# Some tools (clang-format, clang-tidy, doxygen, gcovr, gh, git-cliff, gpg,
|
||||
# pre-commit, run-clang-tidy) are present in our Linux CI images and in local
|
||||
# development setups, but not in the macOS CI environment. They are checked
|
||||
# everywhere except when running in CI on macOS.
|
||||
#
|
||||
# Tools that Nix also exposes under a version-suffixed name (`clang-tidy-22`,
|
||||
# `g++-15`, ...) are probed under both names: a suffixed name can break while
|
||||
# the plain one still works (see mkVersionedToolLinks in nix/packages.nix).
|
||||
#
|
||||
# Environment variables:
|
||||
# CI if set, skip the tools above when on macOS.
|
||||
@@ -26,14 +30,27 @@
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# Version suffixes of the Nix tool links, tracking nix/packages.nix.
|
||||
gcc_version=15
|
||||
llvm_version=22
|
||||
|
||||
missing=()
|
||||
checked=0
|
||||
|
||||
# tool_path <name>
|
||||
# Fully resolved path of a tool, so the snapshots record which derivation
|
||||
# provides it. Prints nothing when it isn't on PATH.
|
||||
tool_path() {
|
||||
local path
|
||||
path="$(command -v "$1" 2>/dev/null)" || return 0
|
||||
readlink -f "${path}" 2>/dev/null || printf '%s' "${path}"
|
||||
}
|
||||
|
||||
# check <name> [probe-command...]
|
||||
# Runs the probe (default: "<name> --version"), capturing both stdout and
|
||||
# stderr, and prints one aligned line: the status, the name, and the first
|
||||
# non-blank line of the probe output (its version). Records <name> as missing
|
||||
# if the command is not found or exits non-zero.
|
||||
# stderr, and prints three lines: the status and name, the first non-blank line
|
||||
# of the probe output (its version, or the error when it failed), and the tool's
|
||||
# resolved path. Records <name> as missing if it is not found or exits non-zero.
|
||||
check() {
|
||||
local name="$1"
|
||||
shift
|
||||
@@ -43,14 +60,17 @@ check() {
|
||||
fi
|
||||
|
||||
checked=$((checked + 1))
|
||||
local output version
|
||||
local output version path
|
||||
path="$(tool_path "${name}")"
|
||||
if output="$("${probe[@]}" 2>&1)"; then
|
||||
version="$(printf '%s\n' "${output}" | grep -m1 '[^[:space:]]' || true)"
|
||||
printf ' [ ok ] %-20s %s\n' "${name}" "${version}"
|
||||
printf ' ✅ %s\n' "${name}"
|
||||
else
|
||||
printf ' [MISS] %s\n' "${name}"
|
||||
printf ' ❌ %s\n' "${name}"
|
||||
missing+=("${name}")
|
||||
fi
|
||||
version="$(printf '%s\n' "${output}" | grep -m1 '[^[:space:]]' || true)"
|
||||
printf ' %s\n' "${version:-(no output)}"
|
||||
printf ' %s\n' "${path:-(not found)}"
|
||||
}
|
||||
|
||||
case "$(uname -s)" in
|
||||
@@ -82,7 +102,9 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
echo "Development tooling:"
|
||||
check ccache
|
||||
check clang
|
||||
check "clang-${llvm_version}"
|
||||
check clang++
|
||||
check "clang++-${llvm_version}"
|
||||
check ClangBuildAnalyzer
|
||||
check curl
|
||||
check file
|
||||
@@ -101,7 +123,14 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
# setups, but not in the macOS CI environment. So check them everywhere
|
||||
# except when running in CI on macOS.
|
||||
if [ "${os}" = "linux" ] || [ -z "${CI:-}" ]; then
|
||||
check clang-apply-replacements
|
||||
check "clang-apply-replacements-${llvm_version}"
|
||||
check clang-format
|
||||
check "clang-format-${llvm_version}"
|
||||
# clang-tidy leads --version with the LLVM banner, not the version.
|
||||
tidy_probe="--version | grep -m1 -oE 'LLVM version [0-9.]+'"
|
||||
check clang-tidy sh -c "clang-tidy ${tidy_probe}"
|
||||
check "clang-tidy-${llvm_version}" sh -c "clang-tidy-${llvm_version} ${tidy_probe}"
|
||||
check dot
|
||||
check doxygen
|
||||
check gcovr
|
||||
@@ -112,6 +141,7 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
# pre-commit, or its alternative implementation prek
|
||||
check pre-commit sh -c 'pre-commit --version || prek --version'
|
||||
check run-clang-tidy run-clang-tidy --help
|
||||
check "run-clang-tidy-${llvm_version}" "run-clang-tidy-${llvm_version}" --help
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -126,8 +156,9 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
check cargo-audit cargo audit --version
|
||||
check cargo-llvm-cov cargo llvm-cov --version
|
||||
check cargo-nextest cargo nextest --version
|
||||
check clippy clippy-driver --version
|
||||
check clippy-driver
|
||||
check rust-analyzer
|
||||
check rust-nightly rust-nightly run rustc --version
|
||||
check rustc
|
||||
check rustfmt
|
||||
fi
|
||||
@@ -138,7 +169,11 @@ if [ "${os}" = "linux" ]; then
|
||||
echo
|
||||
echo "GCC toolchain:"
|
||||
check gcc
|
||||
check "gcc-${gcc_version}"
|
||||
check g++
|
||||
check "g++-${gcc_version}"
|
||||
check cpp
|
||||
check "cpp-${gcc_version}"
|
||||
check gcov
|
||||
|
||||
echo
|
||||
@@ -163,9 +198,9 @@ else
|
||||
checked=$((checked + 1))
|
||||
tmp_clone="$(mktemp -d)"
|
||||
if git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions" >/dev/null 2>&1; then
|
||||
printf ' [ ok ] git clone over HTTPS\n'
|
||||
printf ' ✅ git clone over HTTPS\n'
|
||||
else
|
||||
printf ' [MISS] git clone over HTTPS\n'
|
||||
printf ' ❌ git clone over HTTPS\n'
|
||||
missing+=("git-https-clone")
|
||||
fi
|
||||
rm -rf "${tmp_clone}"
|
||||
@@ -173,9 +208,9 @@ fi
|
||||
|
||||
echo
|
||||
if [ "${#missing[@]}" -eq 0 ]; then
|
||||
echo "All ${checked} checked tools are present and runnable."
|
||||
echo "✅ All ${checked} checked tools are present and runnable."
|
||||
else
|
||||
echo "Missing or non-functional tools (${#missing[@]} of ${checked}):" >&2
|
||||
echo "❌ Missing or non-functional tools (${#missing[@]} of ${checked}):" >&2
|
||||
for tool in "${missing[@]}"; do
|
||||
echo " - ${tool}" >&2
|
||||
done
|
||||
|
||||
@@ -22,23 +22,41 @@ case "${ID}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# Packaging runs in a vanilla distro image, so the tooling comes from the distro's
|
||||
# archive rather than from nixpkgs:
|
||||
#
|
||||
# - debhelper and dpkg-dev build the DEB, and lintian checks it
|
||||
# - binutils gives debian/rules the readelf its glibc-floor check runs; it
|
||||
# already arrives via dpkg-dev, but that tool is called directly
|
||||
# - rpm-build builds the RPM, with systemd-rpm-macros and redhat-rpm-config
|
||||
# supplying the systemd and find-debuginfo macros the spec uses
|
||||
# - rpm-sign and gnupg2 sign the built RPM
|
||||
# - python3 runs the packaging scripts
|
||||
# - git gives build_pkg.py the commit timestamp it stamps files with
|
||||
# - ca-certificates lets git and the packaging scripts verify TLS
|
||||
function install() {
|
||||
case "${ID}" in
|
||||
debian | ubuntu)
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
binutils \
|
||||
ca-certificates \
|
||||
debhelper \
|
||||
debhelper-compat \
|
||||
dpkg-dev \
|
||||
git
|
||||
git \
|
||||
lintian \
|
||||
python3
|
||||
;;
|
||||
|
||||
rhel | centos | rocky | almalinux)
|
||||
dnf install -y --setopt=install_weak_deps=False \
|
||||
git \
|
||||
rpm-build \
|
||||
gnupg2 \
|
||||
python3 \
|
||||
redhat-rpm-config \
|
||||
rpm-build \
|
||||
rpm-sign \
|
||||
systemd-rpm-macros
|
||||
;;
|
||||
esac
|
||||
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check that Rust unit tests stay out of the coverage report.
|
||||
|
||||
cargo-llvm-cov instruments the test code along with everything else, so a test
|
||||
module that is not excluded counts its own body as covered and inflates the
|
||||
reported number. Excluding it takes two attributes:
|
||||
|
||||
* every `#[cfg(test)]` module carries
|
||||
`#[cfg_attr(coverage_nightly, coverage(off))]`;
|
||||
* every crate root (lib.rs, main.rs) carries
|
||||
`#![cfg_attr(coverage_nightly, feature(coverage_attribute))]`, which the
|
||||
attribute above needs in order to compile.
|
||||
|
||||
Both are inert outside the coverage job: cargo-llvm-cov defines
|
||||
`coverage_nightly` only when it runs on a nightly toolchain.
|
||||
|
||||
The crate-root gate is checked even in a crate that has no tests yet, because
|
||||
that is what lets the first test module added later carry the attribute without
|
||||
a build failure. Missing it is a hard error, so it cannot go unnoticed; a
|
||||
missing `coverage(off)` fails open, which is why this check exists.
|
||||
|
||||
Matching is on exact attribute text, which works because `cargo fmt` runs over
|
||||
the whole workspace in the hook ahead of this one: rustfmt puts every attribute
|
||||
on its own line and normalizes what is inside it, turning `#[cfg( test )]`
|
||||
and `#[cfg(test,)]` alike into `#[cfg(test)]`. So there is nothing here that
|
||||
parses Rust. The price is that a cfg this file does not spell out literally --
|
||||
`all(test, ...)`, `any(test, ...)`, `not(test)` -- is reported rather than
|
||||
classified, on the grounds that guessing at coverage semantics is how a check
|
||||
like this ends up quietly wrong.
|
||||
|
||||
Usage: ./bin/pre-commit/check_rust_coverage_attrs.py <file1> <file2> ...
|
||||
|
||||
Exit status is non-zero if any violation is found.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
CRATE_ROOTS = {"lib.rs", "main.rs"}
|
||||
|
||||
FEATURE_ATTR = "#![cfg_attr(coverage_nightly, feature(coverage_attribute))]"
|
||||
COVERAGE_OFF_ATTR = "#[cfg_attr(coverage_nightly, coverage(off))]"
|
||||
CFG_TEST_ATTR = "#[cfg(test)]"
|
||||
|
||||
# Any other cfg that mentions `test`. String literals are blanked before this
|
||||
# runs, so `feature = "test"` does not read as the `test` cfg.
|
||||
RE_CFG_MENTIONS_TEST = re.compile(r"^#\[cfg\(.*\btest\b.*\)\]$")
|
||||
RE_STRING = re.compile(r'"(?:[^"\\]|\\.)*"')
|
||||
RE_MOD = re.compile(r"^(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Finding:
|
||||
line: int
|
||||
label: str
|
||||
message: str
|
||||
|
||||
|
||||
def _check_module(attrs: list[str], line: int, name: str) -> list[Finding]:
|
||||
"""Findings for one module, given the attributes attached to it."""
|
||||
if COVERAGE_OFF_ATTR in attrs:
|
||||
return [] # excluded from coverage; which cfg gates it does not matter
|
||||
if CFG_TEST_ATTR in attrs:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"missing-coverage-off",
|
||||
f"`mod {name}` is #[cfg(test)] but not excluded from coverage; "
|
||||
f"add {COVERAGE_OFF_ATTR}",
|
||||
)
|
||||
]
|
||||
unclassified = [
|
||||
attr for attr in attrs if RE_CFG_MENTIONS_TEST.match(RE_STRING.sub('""', attr))
|
||||
]
|
||||
if unclassified:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"unclassified-cfg",
|
||||
f"`mod {name}` is gated on {unclassified[0]}, which this check "
|
||||
f"cannot tell apart from a module that ships in the library; "
|
||||
f"add {COVERAGE_OFF_ATTR} if it is test-only, or teach this "
|
||||
f"check the cfg if it is not",
|
||||
)
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def _check_test_modules(lines: list[str]) -> list[Finding]:
|
||||
"""Findings for every test module that is not excluded from coverage."""
|
||||
findings: list[Finding] = []
|
||||
attrs: list[str] = []
|
||||
attrs_line = 0
|
||||
for number, raw in enumerate(lines, start=1):
|
||||
stripped = raw.strip()
|
||||
# Blank lines and comments are allowed between an attribute and its item.
|
||||
if not stripped or stripped.startswith("//"):
|
||||
continue
|
||||
if stripped.startswith("#["):
|
||||
if not attrs:
|
||||
attrs_line = number
|
||||
attrs.append(stripped)
|
||||
continue
|
||||
module = RE_MOD.match(stripped)
|
||||
if module is not None and attrs:
|
||||
findings += _check_module(attrs, attrs_line, module.group(1))
|
||||
attrs = []
|
||||
return findings
|
||||
|
||||
|
||||
def _check_crate_root(name: str, lines: list[str]) -> list[Finding]:
|
||||
"""A finding if a crate root is missing the coverage_attribute feature gate."""
|
||||
if name not in CRATE_ROOTS:
|
||||
return []
|
||||
if any(line.strip() == FEATURE_ATTR for line in lines):
|
||||
return []
|
||||
return [
|
||||
Finding(
|
||||
1,
|
||||
"missing-feature-gate",
|
||||
f"crate root is missing {FEATURE_ATTR}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def check_source(name: str, text: str) -> list[Finding]:
|
||||
"""Findings for one file's contents; `name` is its base name (lib.rs, ...)."""
|
||||
lines = text.splitlines()
|
||||
return _check_crate_root(name, lines) + _check_test_modules(lines)
|
||||
|
||||
|
||||
def check_file(path: Path) -> list[Finding]:
|
||||
return check_source(path.name, path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
total = 0
|
||||
for path in (Path(name) for name in sys.argv[1:]):
|
||||
for finding in check_file(path):
|
||||
total += 1
|
||||
print(f"{path}:{finding.line}: {finding.label}: {finding.message}")
|
||||
return 1 if total else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1094,8 +1094,8 @@
|
||||
# Default is 100.
|
||||
#
|
||||
# back_off_milliseconds
|
||||
# Number of milliseconds to wait between
|
||||
# online_delete batches to allow other functions
|
||||
# Number of milliseconds to wait between online_delete
|
||||
# SQL deletion batches to allow other functions
|
||||
# to catch up.
|
||||
# Default is 100.
|
||||
#
|
||||
@@ -1109,10 +1109,22 @@
|
||||
# The online delete process checks periodically
|
||||
# that xrpld is still in sync with the network,
|
||||
# and that the validated ledger is less than
|
||||
# 'age_threshold_seconds' old. If not, then continue
|
||||
# 'age_threshold_seconds' old, and that all
|
||||
# recent ledgers are available. If not, then continue
|
||||
# sleeping for this number of seconds and
|
||||
# checking until healthy.
|
||||
# Default is 5.
|
||||
# Default is 2.
|
||||
#
|
||||
# max_waiting_ledgers
|
||||
# The maximum number of ledgers that may be validated
|
||||
# while online deletion is waiting for the node to get
|
||||
# fully synced with the rest of the network. If more than
|
||||
# this number of ledgers are validated while waiting, then
|
||||
# online deletion gives up on the current ledger and tries
|
||||
# again later. Note this only affects situations that cause
|
||||
# rotation to wait, such as going out of sync, or missing
|
||||
# ledgers. Forward progress is not penalized. Minimum is 64.
|
||||
# Default is the online_delete value.
|
||||
#
|
||||
# Notes:
|
||||
# The 'node_db' entry configures the primary, persistent storage.
|
||||
@@ -1348,6 +1360,39 @@
|
||||
# Example:
|
||||
# owner_reserve = 200000 # 0.2 XRP
|
||||
#
|
||||
# gas_limit = <gas>
|
||||
#
|
||||
# The gas 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:
|
||||
# gas_limit = 1000000 # 1 million gas
|
||||
#
|
||||
# bytecode_size_limit = <bytes>
|
||||
#
|
||||
# The bytecode 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:
|
||||
# bytecode_size_limit = 100000 # 100 kb
|
||||
#
|
||||
# gas_price = <micro-drops>
|
||||
#
|
||||
# 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
|
||||
|
||||
@@ -120,7 +120,10 @@ if(MSVC)
|
||||
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:_CRTDBG_MAP_ALLOC>
|
||||
)
|
||||
target_link_libraries(common INTERFACE -errorreport:none -machine:X64)
|
||||
target_link_libraries(
|
||||
common
|
||||
INTERFACE -errorreport:none -machine:X64 -ignore:4099
|
||||
)
|
||||
else()
|
||||
target_compile_options(
|
||||
common
|
||||
|
||||
@@ -51,6 +51,8 @@ target_compile_options(
|
||||
|
||||
target_link_libraries(xrpl.libpb PUBLIC protobuf::libprotobuf gRPC::grpc++)
|
||||
|
||||
add_dependencies(tidy_prerequisites xrpl.libpb)
|
||||
|
||||
# TODO: Clean up the number of library targets later.
|
||||
add_library(xrpl.imports.main INTERFACE)
|
||||
|
||||
@@ -67,6 +69,8 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
NIH::dilithium2_ref
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#[===================================================================[
|
||||
Linux packaging support: 'package' target.
|
||||
|
||||
The packaging script (package/build_pkg.sh) installs to FHS-standard
|
||||
The packaging script (package/build_pkg.py) installs to FHS-standard
|
||||
paths (/usr/bin, /etc/xrpld, etc.) regardless of CMAKE_INSTALL_PREFIX,
|
||||
so no prefix guard is needed here.
|
||||
#]===================================================================]
|
||||
@@ -38,19 +38,20 @@ if(NOT TARGET validator-keys)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(package_env
|
||||
SRC_DIR=${CMAKE_SOURCE_DIR}
|
||||
BUILD_DIR=${CMAKE_BINARY_DIR}
|
||||
PKG_RELEASE=${pkg_release}
|
||||
)
|
||||
if(DPKG_BUILDPACKAGE_EXECUTABLE)
|
||||
set(pkg_type deb)
|
||||
else()
|
||||
set(pkg_type rpm)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
package
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env ${package_env}
|
||||
${CMAKE_SOURCE_DIR}/package/build_pkg.sh
|
||||
${CMAKE_SOURCE_DIR}/package/build_pkg.py --package-type=${pkg_type}
|
||||
--build-dir=${CMAKE_BINARY_DIR} --pkg-release=${pkg_release}
|
||||
--channel=UNRELEASED
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS xrpld validator-keys
|
||||
COMMENT "Building Linux package (deb/rpm inferred from host tooling)"
|
||||
COMMENT "Building Linux ${pkg_type} package"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
@@ -32,6 +32,11 @@ endif()
|
||||
|
||||
option(benchmark "Build benchmarks" ON)
|
||||
|
||||
# When OFF, the crates directory is not added to the build at all: no Rust
|
||||
# toolchain is required, no cxxbridge bindings are generated, and the C++ tests
|
||||
# that consume those bindings are left out of the build tree.
|
||||
option(rust "Build the Rust crates and the C++ code that depends on them" OFF)
|
||||
|
||||
# Enabled by default so every header is compiled on its own as the main file of
|
||||
# its own compile_commands.json entry - this is what lets clang-tidy (and clangd
|
||||
# and IDEs) analyse a header's own includes directly. The per-header objects are
|
||||
|
||||
62
cmake/deps/dilithium.cmake
Normal file
62
cmake/deps/dilithium.cmake
Normal file
@@ -0,0 +1,62 @@
|
||||
include(FetchContent)
|
||||
|
||||
ExternalProject_Add(
|
||||
dilithium_src
|
||||
PREFIX ${nih_cache_path}
|
||||
# Pin to an explicit commit, not a moving branch ref. Bumping this SHA
|
||||
# is a supply-chain decision that must be reviewed; never revert to a
|
||||
# branch tag here. Upstream:
|
||||
# https://github.com/Transia-RnD/dilithium/commit/3032292cfd4d94e0df9bd49a0098669ca9166aa1
|
||||
GIT_REPOSITORY https://github.com/Transia-RnD/dilithium.git
|
||||
GIT_TAG 3032292cfd4d94e0df9bd49a0098669ca9166aa1
|
||||
GIT_SHALLOW FALSE
|
||||
CONFIGURE_COMMAND ""
|
||||
LOG_BUILD ON
|
||||
BUILD_IN_SOURCE 0
|
||||
BUILD_COMMAND
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/ref <BINARY_DIR>/ref
|
||||
COMMAND make -C <BINARY_DIR>/ref clean
|
||||
COMMAND /bin/sh -c "CFLAGS='-DDILITHIUM_MODE=2 -DDILITHIUM_RANDOMIZED_SIGNING' make -C <BINARY_DIR>/ref libdilithium2_ref.a libfips202_ref.a"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_BYPRODUCTS
|
||||
<BINARY_DIR>/ref/libdilithium2_ref.a
|
||||
<BINARY_DIR>/ref/libfips202_ref.a
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(dilithium_src SOURCE_DIR BINARY_DIR)
|
||||
set(dilithium_src_SOURCE_DIR "${SOURCE_DIR}")
|
||||
set(dilithium_src_BINARY_DIR "${BINARY_DIR}")
|
||||
|
||||
# Include the reference implementation headers from source
|
||||
include_directories("${dilithium_src_SOURCE_DIR}/ref")
|
||||
|
||||
# Create imported targets for each static library using BINARY_DIR
|
||||
add_library(dilithium::dilithium2_ref STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(dilithium::dilithium2_ref PROPERTIES
|
||||
IMPORTED_LOCATION "${dilithium_src_BINARY_DIR}/ref/libdilithium2_ref.a"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${dilithium_src_SOURCE_DIR}/ref/"
|
||||
)
|
||||
|
||||
add_library(dilithium::libfips202_ref STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(dilithium::libfips202_ref PROPERTIES
|
||||
IMPORTED_LOCATION "${dilithium_src_BINARY_DIR}/ref/libfips202_ref.a"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${dilithium_src_SOURCE_DIR}/ref/"
|
||||
)
|
||||
|
||||
# Add dependencies to ensure the external project is built first
|
||||
add_dependencies(dilithium::dilithium2_ref dilithium_src)
|
||||
add_dependencies(dilithium::libfips202_ref dilithium_src)
|
||||
|
||||
# Note: We do NOT link the Dilithium library's randombytes.c because we provide
|
||||
# our own thread-safe implementation in src/libxrpl/protocol/SecretKey.cpp
|
||||
# that uses xrpld's crypto_prng() instead of direct /dev/urandom access.
|
||||
|
||||
# Create an interface library that links to the Dilithium libraries
|
||||
# Note: Link order matters - libraries that provide symbols must come AFTER libraries that use them
|
||||
target_link_libraries(xrpl_libs INTERFACE
|
||||
dilithium::dilithium2_ref
|
||||
dilithium::libfips202_ref
|
||||
)
|
||||
|
||||
# Create alias for convenience
|
||||
add_library(NIH::dilithium2_ref ALIAS dilithium::dilithium2_ref)
|
||||
@@ -8,6 +8,7 @@ Uses pcpp to preprocess the macro file and pyparsing to parse the DSL.
|
||||
|
||||
import io
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
import pyparsing as pp
|
||||
|
||||
@@ -53,28 +54,91 @@ def create_transaction_parser():
|
||||
return macro_parser
|
||||
|
||||
|
||||
# Defaults for xrpl::TxSettings members, mirroring
|
||||
# include/xrpl/protocol/TxSettings.h. A transaction's settings blob only names
|
||||
# the members that differ from these.
|
||||
SETTING_DEFAULTS = {
|
||||
"delegable": "Delegation::NotDelegable",
|
||||
"amendment": "uint256{}",
|
||||
"privileges": "Privilege::NoPriv",
|
||||
"emittance": "Emittance::Emitable",
|
||||
"firewall": "FirewallAction::Allow",
|
||||
}
|
||||
|
||||
|
||||
def parse_settings(settings_str):
|
||||
"""Parse a TxSettings blob into a dict, filling in defaults.
|
||||
|
||||
Args:
|
||||
settings_str: A string like '({.delegable = Delegation::NotDelegable,
|
||||
.privileges = Privilege::CreateAcct})', or '({})'.
|
||||
|
||||
Returns:
|
||||
A dict with a value for every key in SETTING_DEFAULTS.
|
||||
"""
|
||||
body = settings_str.strip()
|
||||
if not (body.startswith("(") and body.endswith(")")):
|
||||
raise ValueError(
|
||||
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
|
||||
)
|
||||
body = body[1:-1].strip()
|
||||
if not (body.startswith("{") and body.endswith("}")):
|
||||
raise ValueError(
|
||||
f"Malformed settings blob, expected '({{...}})': {settings_str!r}"
|
||||
)
|
||||
body = body[1:-1]
|
||||
|
||||
# Strip comments, which may be interleaved with the designated initializers.
|
||||
body = re.sub(r"//[^\n]*", "", body)
|
||||
|
||||
settings = dict(SETTING_DEFAULTS)
|
||||
seen = set()
|
||||
# Each entry runs from '.key =' up to the next '.key =' or the end.
|
||||
for key, value in re.findall(
|
||||
r"\.(\w+)\s*=\s*(.*?)(?=,\s*\.\w+\s*=|,?\s*$)", body, re.S
|
||||
):
|
||||
if key not in SETTING_DEFAULTS:
|
||||
raise ValueError(f"Unknown TxSettings member '.{key}' in {settings_str!r}")
|
||||
settings[key] = " ".join(value.split()).rstrip(",")
|
||||
seen.add(key)
|
||||
|
||||
# Catch a typo'd or unparsed initializer rather than silently defaulting it.
|
||||
# Every '.member' in the blob must have been consumed above.
|
||||
if len(re.findall(r"\.\w+", body)) != len(seen):
|
||||
raise ValueError(f"Could not parse every setting in {settings_str!r}")
|
||||
|
||||
# A blob with content but no designated initializer is positional, which
|
||||
# would otherwise be read as "all defaults" and silently generate the
|
||||
# wrong output.
|
||||
if body.strip() and not seen:
|
||||
raise ValueError(
|
||||
"TxSettings requires designated initializers (.member = value), "
|
||||
f"got {settings_str!r}"
|
||||
)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def parse_transaction_args(args_list):
|
||||
"""Parse the arguments of a TRANSACTION macro call.
|
||||
|
||||
Args:
|
||||
args_list: A list of parsed arguments from pyparsing, e.g.,
|
||||
['ttPAYMENT', '0', 'Payment', 'Delegation::delegable',
|
||||
'uint256{}', 'createAcct', '({...})']
|
||||
['ttPAYMENT', '0', 'Payment',
|
||||
'({.privileges = Privilege::CreateAcct})', '({...})']
|
||||
|
||||
Returns:
|
||||
A dict with parsed transaction information.
|
||||
"""
|
||||
if len(args_list) < 7:
|
||||
if len(args_list) < 5:
|
||||
raise ValueError(
|
||||
f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}"
|
||||
f"Expected at least 5 parts in TRANSACTION, got {len(args_list)}: {args_list}"
|
||||
)
|
||||
|
||||
tag = args_list[0]
|
||||
value = args_list[1]
|
||||
name = args_list[2]
|
||||
delegable = args_list[3]
|
||||
amendments = args_list[4]
|
||||
privileges = args_list[5]
|
||||
settings = parse_settings(args_list[3])
|
||||
fields_str = args_list[-1]
|
||||
|
||||
# Parse fields: ({field1, field2, ...})
|
||||
@@ -84,9 +148,9 @@ def parse_transaction_args(args_list):
|
||||
"tag": tag,
|
||||
"value": value,
|
||||
"name": name,
|
||||
"delegable": delegable,
|
||||
"amendments": amendments,
|
||||
"privileges": privileges,
|
||||
"delegable": settings["delegable"],
|
||||
"amendments": settings["amendment"],
|
||||
"privileges": settings["privileges"],
|
||||
"fields": fields,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"requires": [
|
||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1782392402.420688",
|
||||
"wasmi/1.0.9#1fecdab9b90c96698eb35ea99ca4f5cb%1782307153.343419",
|
||||
"sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1782392403.185447",
|
||||
"soci/4.0.3#e726491a03468795453f7c83fc924a96%1782392402.679521",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
|
||||
@@ -23,6 +24,7 @@
|
||||
"fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1782494504.298",
|
||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
||||
"corrosion/0.6.1#bfa292df0a957bc70a450ff316cd9435%1786119416.131296",
|
||||
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
|
||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
|
||||
|
||||
@@ -28,6 +28,7 @@ class Xrpl(ConanFile):
|
||||
}
|
||||
|
||||
requires = [
|
||||
"corrosion/0.6.1",
|
||||
"ed25519/2015.03",
|
||||
"fast_float/8.2.10",
|
||||
"grpc/1.81.1",
|
||||
@@ -35,6 +36,7 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.9",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
@@ -223,6 +225,7 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
17
crates/.cargo/config.toml
Normal file
17
crates/.cargo/config.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
# The Rust static libraries are linked into C++ targets, so the runtime linkage
|
||||
# here has to match what the C++ build uses (see cmake/XrplCompiler.cmake).
|
||||
#
|
||||
# macOS needs nothing: AppleClang cannot link libgcc/libc++ statically, so the
|
||||
# C++ build skips those flags on Apple as well.
|
||||
|
||||
# Both amd64 and arm64 Linux builds link libgcc statically. This only affects
|
||||
# links that rustc itself drives (`cargo test` binaries and the like) — the
|
||||
# `staticlib` crates consumed by CMake are archived, not linked, so rustc
|
||||
# silently ignores link args for them. Keeping libgcc_s.so.1 off the xrpld link
|
||||
# line is handled in crates/CMakeLists.txt instead.
|
||||
[target.'cfg(target_os = "linux")']
|
||||
rustflags = ["-C", "link-args=-static-libgcc"]
|
||||
|
||||
# Windows builds use the static MSVC runtime.
|
||||
[target.'cfg(windows)']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
104
crates/CMakeLists.txt
Normal file
104
crates/CMakeLists.txt
Normal file
@@ -0,0 +1,104 @@
|
||||
find_package(Corrosion REQUIRED)
|
||||
|
||||
corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml)
|
||||
|
||||
# The generated C++ lands in the build tree, so put a .clang-tidy next to it to
|
||||
# keep clang-tidy from analyzing code we don't own.
|
||||
configure_file(
|
||||
generated.clang-tidy
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/.clang-tidy"
|
||||
COPYONLY
|
||||
)
|
||||
|
||||
add_custom_target(xrpl_crates)
|
||||
add_dependencies(tidy_prerequisites xrpl_crates)
|
||||
|
||||
# On macOS, ld warns `ignoring duplicate libraries` when linking a crate.
|
||||
# Corrosion is the source of both duplicates it names:
|
||||
#
|
||||
# * The crate archive and its cxxbridge archive, because
|
||||
# `corrosion_add_cxxbridge` makes the two depend on each other, and CMake
|
||||
# repeats a static library cycle on the link line so single-pass linkers can
|
||||
# resolve it. (LINK_INTERFACE_MULTIPLICITY can only raise that count.)
|
||||
# * `-lSystem`, which Corrosion copies from rustc's `native-static-libs` even
|
||||
# though the compiler driver always links libSystem.
|
||||
#
|
||||
# ld needs neither: it resolves the cycle from one copy of each archive and
|
||||
# links libSystem once. So silence the warning rather than rewrite Corrosion's
|
||||
# link interface, which the cycle is also part of. The option itself is old —
|
||||
# Xcode 15 is only where the warning became the default — and the check below
|
||||
# leaves it out on a linker that does not know it.
|
||||
if(is_macos)
|
||||
include(CheckLinkerFlag)
|
||||
check_linker_flag(
|
||||
CXX
|
||||
-Wl,-no_warn_duplicate_libraries
|
||||
have_no_warn_duplicate_libraries
|
||||
)
|
||||
endif()
|
||||
|
||||
function(_unlink_libgcc_s crate)
|
||||
if(NOT (is_linux AND static))
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Corrosion exposes a crate's staticlib as an imported `<crate>-static`
|
||||
# target and puts the native libs in its INTERFACE_LINK_LIBRARIES. If either
|
||||
# of those changes, warn instead of silently letting libgcc_s.so.1 return.
|
||||
set(imported "${crate}-static")
|
||||
if(NOT TARGET ${imported})
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Corrosion did not create the imported target '${imported}', so "
|
||||
"libgcc_s cannot be removed from the link interface of '${crate}'. "
|
||||
"xrpld will link libgcc_s.so.1 dynamically. Check where Corrosion "
|
||||
"${CORROSION_VERSION} now records `native-static-libs`."
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(libs ${imported} INTERFACE_LINK_LIBRARIES)
|
||||
if(NOT "gcc_s" IN_LIST libs)
|
||||
message(
|
||||
WARNING
|
||||
"'gcc_s' was not in the link interface of '${imported}' as "
|
||||
"expected. If the Rust toolchain stopped reporting it this "
|
||||
"workaround is obsolete and can be deleted; otherwise xrpld may "
|
||||
"link libgcc_s.so.1 dynamically. Verify with: "
|
||||
"objdump -p xrpld | grep NEEDED"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(REMOVE_ITEM libs gcc_s)
|
||||
set_property(TARGET ${imported} PROPERTY INTERFACE_LINK_LIBRARIES ${libs})
|
||||
endfunction()
|
||||
|
||||
function(add_xrpl_crate name)
|
||||
cmake_parse_arguments(ARG "" "CRATE" "FILES" ${ARGN})
|
||||
_unlink_libgcc_s(${ARG_CRATE})
|
||||
# `cc` picks its runtime flag from `crt-static` alone, so it compiles a
|
||||
# crate's C++ with `-MT`; Debug needs `-MTd` (to match cmake/XrplCompiler.cmake).
|
||||
if(is_msvc)
|
||||
corrosion_set_env_vars(
|
||||
${ARG_CRATE}
|
||||
"$<$<CONFIG:Debug>:CXXFLAGS=-MTd>"
|
||||
)
|
||||
endif()
|
||||
corrosion_add_cxxbridge(${name}_cxxbridge CRATE ${ARG_CRATE} FILES
|
||||
${ARG_FILES}
|
||||
)
|
||||
# Generated cxxbridge headers don't exist at configure time; CMake 3.28+
|
||||
# validates INTERFACE_SOURCES on consuming targets. Clear it to skip the
|
||||
# existence check — build-time ordering is enforced by the custom commands.
|
||||
set_target_properties(${name}_cxxbridge PROPERTIES INTERFACE_SOURCES "")
|
||||
if(have_no_warn_duplicate_libraries)
|
||||
target_link_options(
|
||||
${name}_cxxbridge
|
||||
INTERFACE -Wl,-no_warn_duplicate_libraries
|
||||
)
|
||||
endif()
|
||||
add_dependencies(xrpl_crates ${name}_cxxbridge)
|
||||
endfunction()
|
||||
|
||||
add_xrpl_crate(rs_hello_world CRATE rs_hello_world FILES lib.rs)
|
||||
301
crates/Cargo.lock
generated
Normal file
301
crates/Cargo.lock
generated
Normal file
@@ -0,0 +1,301 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.199"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824894a4a85dca76d4c95c2b9098c036f5a29f627b30c12780774f6654e60974"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxx-build",
|
||||
"cxxbridge-cmd",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"foldhash",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.199"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1ae0b651ea5b0000b19513aef5a03f194d7e3486f2d9258b658da8677fe9036"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.199"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb05f91d3fb8435d9bab6ac5ce6ac1868be774325fb7fb2a91be39393b21388e"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.199"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf293202e0e3e98495785745389e8d0755b217e66f19194a5c695c25e03282ef"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.199"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca001d746947c7249ed9d332a10f7a59daedbafeb0ec68c5c18a7db7a93f6ccc"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rs-hello_world"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
18
crates/Cargo.toml
Normal file
18
crates/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[workspace]
|
||||
members = ["hello_world"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.dependencies]
|
||||
cxx = { version = "1.0.198", features = ["c++20"] }
|
||||
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(coverage)', 'cfg(coverage_nightly)' ] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
overflow-checks = true
|
||||
lto = true
|
||||
debug = true
|
||||
10
crates/generated.clang-tidy
Normal file
10
crates/generated.clang-tidy
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# Neutralizes clang-tidy for the corrosion/cxxbridge-generated C++. Copied into
|
||||
# the crates build directory by crates/CMakeLists.txt, next to the generated
|
||||
# sources, so clang-tidy picks it up instead of the top-level configuration.
|
||||
#
|
||||
# One check is kept enabled to avoid clang-tidy's "no checks enabled" error.
|
||||
Checks: "-*,google-readability-todo"
|
||||
WarningsAsErrors: ""
|
||||
HeaderFilterRegex: ""
|
||||
InheritParentConfig: false
|
||||
13
crates/hello_world/Cargo.toml
Normal file
13
crates/hello_world/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "rs-hello_world"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
cxx.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
23
crates/hello_world/src/lib.rs
Normal file
23
crates/hello_world/src/lib.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
|
||||
|
||||
#[cxx::bridge(namespace = "rs::hello_world")]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
fn hello_world() -> String;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hello_world() -> String {
|
||||
"hello_world".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg_attr(coverage_nightly, coverage(off))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_world_returns_hello_world() {
|
||||
assert_eq!(hello_world(), "hello_world")
|
||||
}
|
||||
}
|
||||
25
docker/supported.Dockerfile
Normal file
25
docker/supported.Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
# Runtime image for the perf/test xrpld build with all amendments Supported::Yes.
|
||||
# Installs the .deb into ubuntu:jammy (matching rippleci/xrpld): gives
|
||||
# /usr/bin/xrpld, /etc/xrpld/xrpld.cfg, and the xrpld user.
|
||||
# NOT for production validators.
|
||||
ARG BASE_IMAGE=ubuntu:jammy
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
# Build context must contain the supported package as xrpld.deb.
|
||||
COPY xrpld.deb /tmp/xrpld.deb
|
||||
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ca-certificates jq /tmp/xrpld.deb; \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/xrpld.deb; \
|
||||
id -u xrpld >/dev/null 2>&1 || \
|
||||
useradd --system --home-dir /var/lib/xrpld --shell /sbin/nologin --user-group xrpld; \
|
||||
mkdir -p /var/log/xrpld /var/lib/xrpld; \
|
||||
chown -R xrpld:xrpld /var/log/xrpld /var/lib/xrpld; \
|
||||
# Symlink for consumers that exec /opt/xrpld/bin/xrpld.
|
||||
mkdir -p /opt/xrpld/bin; \
|
||||
ln -sf /usr/bin/xrpld /opt/xrpld/bin/xrpld
|
||||
|
||||
EXPOSE 2459/tcp 5005/tcp 6006/tcp
|
||||
USER xrpld
|
||||
ENTRYPOINT ["/usr/bin/xrpld"]
|
||||
279
docs/AMMCurveTemplate.md
Normal file
279
docs/AMMCurveTemplate.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# AMM Curve Implementation Template
|
||||
|
||||
Guide for adding a new curve type to the XRPL pluggable AMM curve framework.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Each token pair can have **multiple AMM pools**, one per curve type. The keylet
|
||||
hash includes `curveType`, so `RLUSD/USD ConstantProduct` and `RLUSD/USD
|
||||
StableSwap` are separate ledger entries with separate pseudo-accounts and LP
|
||||
tokens. BookStep automatically routes through the deepest-liquidity pool.
|
||||
|
||||
### Key Components
|
||||
|
||||
| Component | File | Purpose |
|
||||
| --------------------- | --------------------------------------------------- | --------------------------------------- |
|
||||
| CurveInterface | `include/xrpl/ledger/helpers/AMMCurve.h` | Abstract base for all curves |
|
||||
| Curve implementations | `src/libxrpl/ledger/helpers/AMMCurve.cpp` | CP, CL, StableSwap, Weighted |
|
||||
| Tick math | `src/libxrpl/ledger/helpers/AMMTickMath.cpp` | CL tick/sqrt price conversions |
|
||||
| Fee collection | `src/libxrpl/tx/transactors/dex/AMMCollectFees.cpp` | CL position fee harvesting |
|
||||
| Multi-curve routing | `src/libxrpl/tx/paths/BookStep.cpp` | Picks best pool per pair |
|
||||
| Keylet hashing | `src/libxrpl/protocol/Indexes.cpp` | `amm(asset1, asset2, curveType)` |
|
||||
| LP token identity | `src/libxrpl/protocol/AMMCore.cpp` | `ammLPTCurrency(cur1, cur2, curveType)` |
|
||||
|
||||
## Step 1: Define the Curve Type
|
||||
|
||||
Add to `include/xrpl/protocol/AMMCore.h`:
|
||||
|
||||
```cpp
|
||||
enum CurveType : std::uint8_t
|
||||
{
|
||||
ctCONSTANT_PRODUCT = 0,
|
||||
ctCONCENTRATED_LIQUIDITY = 1,
|
||||
ctSTABLE_SWAP = 2,
|
||||
ctWEIGHTED = 3,
|
||||
ctYOUR_CURVE = N, // next available ID
|
||||
};
|
||||
```
|
||||
|
||||
## Step 2: Define SFields for Curve Parameters
|
||||
|
||||
Add to `include/xrpl/protocol/detail/sfields.macro`:
|
||||
|
||||
```cpp
|
||||
// Use the appropriate type (UINT8, UINT16, UINT32, UINT64, UINT256, AMOUNT, etc.)
|
||||
// Check existing field codes to avoid collisions.
|
||||
TYPED_SFIELD(sfYourParam, UINT32, <next_available_code>)
|
||||
```
|
||||
|
||||
## Step 3: Implement the CurveInterface
|
||||
|
||||
Add a new class in `src/libxrpl/ledger/helpers/AMMCurve.cpp`:
|
||||
|
||||
```cpp
|
||||
class YourCurve final : public CurveInterface
|
||||
{
|
||||
public:
|
||||
Expected<STAmount, TER>
|
||||
swapIn(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
STAmount const& assetIn,
|
||||
std::uint16_t tfee,
|
||||
STObject const* curveParams) const override
|
||||
{
|
||||
if (!curveParams)
|
||||
return Unexpected(tecINTERNAL);
|
||||
|
||||
auto const param = curveParams->getFieldU32(sfYourParam);
|
||||
|
||||
auto const f = feeMult(tfee); // fee multiplier (1 - fee)
|
||||
Number const x = poolIn; // STAmount -> Number via implicit conversion
|
||||
Number const y = poolOut;
|
||||
Number const dx = Number(assetIn) * f;
|
||||
|
||||
// --- YOUR INVARIANT MATH HERE ---
|
||||
// Compute output amount `dy` from your invariant
|
||||
// F(x, y) = k => F(x + dx, y - dy) = k => solve for dy
|
||||
Number const dy = /* ... */;
|
||||
|
||||
if (dy <= Number{0})
|
||||
return Unexpected(tecAMM_FAILED);
|
||||
|
||||
// Round output DOWN (favorable to pool)
|
||||
NumberRoundModeGuard const mg(Number::downward);
|
||||
return toSTAmount(poolOut.issue(), dy);
|
||||
}
|
||||
|
||||
Expected<STAmount, TER>
|
||||
swapOut(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
STAmount const& assetOut,
|
||||
std::uint16_t tfee,
|
||||
STObject const* curveParams) const override
|
||||
{
|
||||
if (!curveParams)
|
||||
return Unexpected(tecINTERNAL);
|
||||
|
||||
auto const param = curveParams->getFieldU32(sfYourParam);
|
||||
|
||||
auto const f = feeMult(tfee);
|
||||
Number const x = poolIn;
|
||||
Number const y = poolOut;
|
||||
|
||||
// --- YOUR INVARIANT MATH (INVERSE) ---
|
||||
// Given desired output, compute required input
|
||||
// F(x, y) = k => F(x + dx, y - assetOut) = k => solve for dx
|
||||
Number const dx = /* ... */ / f;
|
||||
|
||||
if (dx <= Number{0})
|
||||
return Unexpected(tecAMM_FAILED);
|
||||
|
||||
// Round input UP (favorable to pool)
|
||||
NumberRoundModeGuard const mg(Number::upward);
|
||||
return toSTAmount(poolIn.issue(), dx);
|
||||
}
|
||||
|
||||
Expected<Number, TER>
|
||||
spotPrice(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
std::uint16_t tfee,
|
||||
STObject const* curveParams) const override
|
||||
{
|
||||
if (!curveParams)
|
||||
return Unexpected(tecINTERNAL);
|
||||
|
||||
auto const param = curveParams->getFieldU32(sfYourParam);
|
||||
auto const f = feeMult(tfee);
|
||||
|
||||
// Marginal price: -dF/dx / dF/dy evaluated at current reserves
|
||||
// Divided by (1 - fee) for the taker-facing price
|
||||
Number const price = /* partial derivatives of your invariant */;
|
||||
return price / f;
|
||||
}
|
||||
|
||||
TER
|
||||
validateParams(STObject const& curveParams) const override
|
||||
{
|
||||
// Validate curve-specific parameters at pool creation
|
||||
if (!curveParams.isFieldPresent(sfYourParam))
|
||||
return temMALFORMED;
|
||||
|
||||
auto const param = curveParams.getFieldU32(sfYourParam);
|
||||
if (param < MIN_YOUR_PARAM || param > MAX_YOUR_PARAM)
|
||||
return temMALFORMED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
Expected<STAmount, TER>
|
||||
initialLPTokens(
|
||||
STAmount const& asset1,
|
||||
STAmount const& asset2,
|
||||
Issue const& lptIssue,
|
||||
STObject const* curveParams) const override
|
||||
{
|
||||
if (!curveParams)
|
||||
return Unexpected(tecINTERNAL);
|
||||
|
||||
// Compute initial LP token amount from deposits
|
||||
// For many curves: geometric mean sqrt(asset1 * asset2)
|
||||
// Or curve-specific: D for StableSwap, weighted geometric mean, etc.
|
||||
Number const lp = /* ... */;
|
||||
return toSTAmount(lptIssue, lp);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Step 4: Register the Singleton and Dispatch
|
||||
|
||||
In `AMMCurve.cpp`, add the singleton and switch case:
|
||||
|
||||
```cpp
|
||||
// At file scope (inside anonymous namespace)
|
||||
static YourCurve const yourCurve_;
|
||||
|
||||
// In getCurve():
|
||||
case ctYOUR_CURVE:
|
||||
if (rules.enabled(featureAMMCurves))
|
||||
return &yourCurve_;
|
||||
return nullptr;
|
||||
```
|
||||
|
||||
## Step 5: Update AMMCreate Validation
|
||||
|
||||
In `src/libxrpl/tx/transactors/dex/AMMCreate.cpp`:
|
||||
|
||||
1. Update the max curve type check: `if (curveType > ctYOUR_CURVE)`
|
||||
2. Add params setup in the `applyGuts` section:
|
||||
|
||||
```cpp
|
||||
else if (curveType == ctYOUR_CURVE)
|
||||
{
|
||||
ammSle->setFieldU32(
|
||||
sfYourParam, ctx_.tx.getFieldU32(sfYourParam));
|
||||
}
|
||||
```
|
||||
|
||||
## Step 6: Multi-Curve Routing (BookStep)
|
||||
|
||||
BookStep automatically discovers and routes through the best pool for each
|
||||
token pair. When a new curve type is added, update the loop upper bound in
|
||||
`src/libxrpl/tx/paths/BookStep.cpp`:
|
||||
|
||||
```cpp
|
||||
for (std::uint8_t ct = 0; ct <= ctYOUR_CURVE; ++ct)
|
||||
{
|
||||
auto const ammSle = ctx.view.read(keylet::amm(in, out, ct));
|
||||
if (!ammSle || ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero)
|
||||
continue;
|
||||
if (!bestAmm ||
|
||||
ammSle->getFieldAmount(sfLPTokenBalance) >
|
||||
bestAmm->getFieldAmount(sfLPTokenBalance))
|
||||
bestAmm = ammSle;
|
||||
}
|
||||
```
|
||||
|
||||
The pool with the highest LP token balance wins. Curve-specific swap dispatch
|
||||
happens automatically via `getCurve()` in `AMMLiquidity`/`AMMOffer`.
|
||||
|
||||
## Step 7: Keylet and LP Token Identity
|
||||
|
||||
Each curve type for the same token pair gets a unique ledger key and LP token:
|
||||
|
||||
- **Keylet**: `keylet::amm(asset1, asset2, curveType)` hashes `curveType` into
|
||||
the AMM's ledger key (when curveType != 0, for backward compatibility)
|
||||
- **LP token**: `ammLPTCurrency(cur1, cur2, curveType)` hashes `curveType` into
|
||||
the LP token currency code
|
||||
|
||||
No changes needed here when adding a new curve — the default parameter
|
||||
propagates automatically.
|
||||
|
||||
## Concentrated Liquidity Extras
|
||||
|
||||
The CL curve type uses additional infrastructure not needed by other curves:
|
||||
|
||||
- **AMMTickMath** (`AMMTickMath.h/cpp`): `tickToSqrtPrice()`, `sqrtPriceToTick()`,
|
||||
`isValidTick()` for tick-based price representation
|
||||
- **AMMCollectFees** (`AMMCollectFees.h/cpp`): Transactor for position owners to
|
||||
collect accumulated swap fees using the Uniswap V3 fee growth formula
|
||||
- **Ledger entries**: `ltAMM_POSITION` (per-user tick range + liquidity) and
|
||||
`ltAMM_TICK` (per-tick fee growth and liquidity tracking)
|
||||
- **SFields**: `sfFeeGrowthGlobal0/1`, `sfFeeGrowthOutside0/1`,
|
||||
`sfFeeGrowthInsideLast0/1` (UINT256, Q128.128 fixed-point),
|
||||
`sfActiveLiquidity`, `sfPositionLiquidity`, `sfLiquidityGross/Net` (UINT64)
|
||||
|
||||
## Math Utilities Available
|
||||
|
||||
- `Number`: arbitrary-precision decimal arithmetic (see `include/xrpl/basics/Number.h`)
|
||||
- `power(f, n)`: f^n (integer exponent)
|
||||
- `power(f, n, d)`: f^(n/d) (rational exponent)
|
||||
- `root(f, d)`: f^(1/d)
|
||||
- `root2(f)`: sqrt(f)
|
||||
- `feeMult(tfee)`: returns `1 - fee` as Number
|
||||
- `feeMultHalf(tfee)`: returns `1 - fee/2` as Number
|
||||
- `toSTAmount(issue, number)`: convert Number to STAmount
|
||||
- `NumberRoundModeGuard`: RAII guard for rounding direction
|
||||
|
||||
## Rounding Convention
|
||||
|
||||
- `swapIn` output: round DOWN (pool keeps the rounding dust)
|
||||
- `swapOut` input: round UP (taker pays the rounding dust)
|
||||
- Use `NumberRoundModeGuard` to set the rounding mode before `toSTAmount()`
|
||||
|
||||
## Testing
|
||||
|
||||
Add tests in `src/test/app/AMMCurves_test.cpp`:
|
||||
|
||||
1. `swapIn` and `swapOut` are inverses (within rounding tolerance)
|
||||
2. Invariant is preserved: `F(reserves_new) >= F(reserves_old)` after every swap
|
||||
3. `spotPrice` matches actual swap rate at infinitesimal amounts
|
||||
4. Edge cases: zero input, max input, min reserves
|
||||
5. Parameter validation: `validateParams` rejects out-of-range values
|
||||
6. Integration: create pool, deposit, swap, withdraw full cycle
|
||||
|
||||
E2E tests go in `src/test/app/AMMCurvesE2E_test.cpp` for full transaction
|
||||
lifecycle tests (AMMCreate with curve params, swap through payment engine,
|
||||
deposit/withdraw).
|
||||
22
docs/build/environment.md
vendored
22
docs/build/environment.md
vendored
@@ -46,6 +46,9 @@ Besides a compiler, building `xrpld` requires:
|
||||
On Linux and macOS, the [Nix development shell](./nix.md) provides all of them
|
||||
(see below). On Windows they have to be installed manually.
|
||||
|
||||
Building with `-Drust=ON` additionally requires a Rust toolchain, see
|
||||
[Rust](#rust). A default build does not, so it is not in the table above.
|
||||
|
||||
Once they are in place, verify that everything is installed and runnable with:
|
||||
|
||||
```bash
|
||||
@@ -121,6 +124,25 @@ manually:
|
||||
- [Git for Windows](https://git-scm.com/download/win)
|
||||
- Python, Conan, and CMake, at the versions listed in
|
||||
[Required tools](#required-tools).
|
||||
- a [Rust toolchain](https://rustup.rs) — only needed to build with
|
||||
`-Drust=ON`, see [Rust](#rust)
|
||||
|
||||
## Rust
|
||||
|
||||
The repository contains a Rust workspace in [`crates/`](../../crates), whose
|
||||
crates are exposed to C++ through [cxx](https://cxx.rs) bindings. It is **not**
|
||||
part of a default build: the CMake `rust` option is OFF by default, and with it
|
||||
off no Rust toolchain is needed. It is only required when configuring with
|
||||
`-Drust=ON` (which is what CI does), see [Options](../../BUILD.md#options).
|
||||
|
||||
The toolchain (`cargo`, `rustc`) is pinned to the channel in
|
||||
[`rust-toolchain.toml`](../../rust-toolchain.toml) at the repository root. If
|
||||
you install Rust with [rustup](https://rustup.rs), that file is picked up
|
||||
automatically, and `cargo`/`rustc` in the repository will use the pinned
|
||||
version.
|
||||
|
||||
Everything else the Rust build needs on the CMake side comes from Conan along
|
||||
with the rest of the dependencies, so there is nothing further to install.
|
||||
|
||||
## Clang-tidy
|
||||
|
||||
|
||||
84
docs/build/nix.md
vendored
84
docs/build/nix.md
vendored
@@ -7,7 +7,7 @@ This guide explains how to use Nix to set up a reproducible development environm
|
||||
## Benefits of Using Nix
|
||||
|
||||
- **Reproducible environment**: Everyone gets the same versions of tools and compilers
|
||||
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment
|
||||
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment, and CI builds some macOS configurations in it as well
|
||||
- **No system pollution**: Dependencies are isolated and don't affect your system packages
|
||||
- **Consistent compilers**: The GCC and Clang shells use the same versions as CI
|
||||
- **Quick setup**: Get started with a single command
|
||||
@@ -68,7 +68,7 @@ A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix d
|
||||
|
||||
On Linux, `.#gcc` and `.#clang` provide the exact toolchain CI uses:
|
||||
the compiler (pinned in [`nix/packages.nix`](../../nix/packages.nix))
|
||||
rebuilt against the pinned custom glibc (see [`nix/compilers.nix`](../../nix/compilers.nix)).
|
||||
rebuilt against the pinned custom glibc (see [`nix/linux.nix`](../../nix/linux.nix)).
|
||||
Building that toolchain the first time is slow unless it is fetched from a Nix binary cache.
|
||||
If you don't need the custom glibc, the Linux-only `.#gcc-plain` and `.#clang-plain`
|
||||
give you the stock nixpkgs compilers of the same versions.
|
||||
@@ -128,6 +128,12 @@ Coverage builds (`-Dcoverage=ON`) work in the `gcc` shell (and `gcc-plain` on Li
|
||||
each ships a `gcov` matching its compiler, since Nix's cc-wrapper does not expose one.
|
||||
The `clang` shells do not include `llvm-cov`, so use a `gcc` shell for coverage.
|
||||
|
||||
Builds of the Rust crates (`-Drust=ON`) also work out of the box: every shell
|
||||
provides the Rust toolchain pinned in
|
||||
[`rust-toolchain.toml`](../../rust-toolchain.toml) (see
|
||||
[Rust](./environment.md#rust)), plus the `cargo-audit`, `cargo-llvm-cov` and
|
||||
`cargo-nextest` plugins.
|
||||
|
||||
## Conan configuration
|
||||
|
||||
The shell runs [`conan/init.sh`](../../conan/init.sh) on entry, so
|
||||
@@ -142,14 +148,80 @@ environment — CI runs in Docker images that bundle the dev shell's toolchain (
|
||||
`-plain` shells do not match that toolchain's glibc, so binaries from the remote
|
||||
are not a reliable match there.
|
||||
|
||||
On **macOS**, CI builds with Apple Clang, so the remote holds nothing for the Nix
|
||||
`clang` toolchain and dependencies are compiled locally. We do not publish
|
||||
Nix-built macOS binaries because a Conan package ID records the compiler version
|
||||
but not the nixpkgs revision.
|
||||
On **macOS**, CI also builds in this Nix environment, in Debug and Release (the
|
||||
`macos-arm64-*-nix` configurations — Debug because the profile defaults to it).
|
||||
The Nix build resolves to `compiler=clang`, so it gets its own package IDs,
|
||||
separate from the Apple Clang ones. The
|
||||
[dependency upload](../../.github/workflows/upload-conan-deps.yml) publishes them
|
||||
on pushes to `develop` and on manual runs — its nightly run rebuilds everything
|
||||
from source but uploads nothing — so once a set has been published `nix develop`
|
||||
can reuse it instead of compiling every dependency locally. These configurations
|
||||
run outside the reduced pull-request matrix, so label a PR `Full CI build` when it
|
||||
touches `flake.lock` or `nix/`.
|
||||
|
||||
To compile everything from source, add `--build '*'` to the `conan install`
|
||||
command.
|
||||
|
||||
### Why the nixpkgs revision is not part of the package ID
|
||||
|
||||
A Conan package ID records the compiler and its major version, but nothing about
|
||||
the nixpkgs revision the toolchain came from — and `flake.lock` moves far more
|
||||
often than the toolchain meaningfully changes, so folding it in would rebuild
|
||||
every dependency on every bump for nothing.
|
||||
|
||||
That is safe as long as no cached artifact resolves a `/nix/store` path at run
|
||||
time, because store paths change on every update and the old ones disappear with
|
||||
`nix-collect-garbage`. With the `clang` toolchain macOS CI and the dev shell use,
|
||||
they do not: it links against `/usr/lib/libc++` and `/usr/lib/libSystem`, and
|
||||
store paths reach the `.a` files only through debug info, which nothing resolves
|
||||
at link or run time.
|
||||
|
||||
> [!WARNING]
|
||||
> This does not hold for `nix develop .#gcc` on macOS. There is no system
|
||||
> libstdc++, so GCC links its own from the store and every binary keeps a
|
||||
> `/nix/store` reference. That shell is fine for tooling, but it is not a build
|
||||
> configuration CI covers, and no dependency binaries are published for it.
|
||||
|
||||
This is checked rather than assumed.
|
||||
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) takes one file
|
||||
or directory and fails if a binary under it resolves a store path at run time.
|
||||
CI runs it over the build output and the Conan cache, and again in the upload job
|
||||
before anything is published. You can run it yourself:
|
||||
|
||||
```bash
|
||||
bin/check-nix-store-refs.sh build
|
||||
bin/check-nix-store-refs.sh ~/.conan2-nix
|
||||
```
|
||||
|
||||
It works on Linux too, but asserts something narrower there: the toolchain always
|
||||
writes the store into `PT_INTERP` and `RUNPATH`, and CI builds inside an image
|
||||
whose store is fixed for its lifetime, so that is fine. Only the binaries
|
||||
[`PatchNixBinary.cmake`](../../cmake/PatchNixBinary.cmake) retargets to the
|
||||
system loader have to be clean, and those are what CI checks:
|
||||
|
||||
```bash
|
||||
bin/check-nix-store-refs.sh build/xrpld
|
||||
```
|
||||
|
||||
### The libresolv stub
|
||||
|
||||
This is not hypothetical: `xrpld` used to be caught by it. The c-ares package
|
||||
tells the linker to pass `-lresolv`, and nixpkgs keeps `libresolv` out of the
|
||||
macOS SDK and ships it as an ordinary store dylib — so every Nix-built `xrpld`
|
||||
recorded a `/nix/store/…-libresolv-93/lib/libresolv.9.dylib` load command and
|
||||
stopped running once that path was collected. Nothing in the link uses a single
|
||||
symbol from it.
|
||||
|
||||
Both environments now put a stub on the linker search path
|
||||
(`libresolvSystemStub` in [`nix/darwin.nix`](../../nix/darwin.nix)): the
|
||||
same library with its install name set to `/usr/lib/libresolv.9.dylib`, which is
|
||||
exactly the load command the Apple Clang build records.
|
||||
|
||||
Package IDs did not change, so Conan keeps serving anything built before the
|
||||
stub landed. If a binary fails to start with `Library not loaded: /nix/store/…`,
|
||||
see [that entry](./nix_troubleshooting.md#library-not-loaded-nixstore-from-a-binary-that-used-to-work)
|
||||
in the troubleshooting guide.
|
||||
|
||||
## Automatic Activation with direnv
|
||||
|
||||
[direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory.
|
||||
|
||||
88
docs/build/nix_troubleshooting.md
vendored
88
docs/build/nix_troubleshooting.md
vendored
@@ -131,3 +131,91 @@ once it picks up that rebuild, then re-run the `grep libgit2` check above to
|
||||
confirm it reports `1.9.4` or newer.
|
||||
|
||||
Until then, prefer the workarounds above.
|
||||
|
||||
## `wint_t` / `uint32_t` errors from the Nix libc++ headers
|
||||
|
||||
A build that mixes the Nix toolchain with the system SDK fails in libc++ itself,
|
||||
with errors that look nothing like your code:
|
||||
|
||||
```
|
||||
/nix/store/...-libcxx-.../include/c++/v1/cwchar:136:9: error: target of using declaration conflicts with declaration already in scope
|
||||
136 | using ::wint_t _LIBCPP_USING_IF_EXISTS;
|
||||
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_types/_wint_t.h:32:25: note: target of using declaration
|
||||
...
|
||||
error: use of undeclared identifier 'UINT32_C'
|
||||
```
|
||||
|
||||
The give-away is the second path: Nix's libc++ headers are being combined with
|
||||
the **Xcode Command Line Tools** SDK instead of the Nix one.
|
||||
|
||||
### Why it happens
|
||||
|
||||
`SDKROOT` and `DEVELOPER_DIR` are what point the toolchain at the Nix SDK, and
|
||||
they are not baked into the compiler — a dev shell gets them from the
|
||||
`apple-sdk` setup hook. CMake, finding neither, asks `xcrun`, which answers with
|
||||
the system SDK. Nix's `libc++` and Apple's headers then declare the same types
|
||||
twice.
|
||||
|
||||
### Fix
|
||||
|
||||
Run the build from inside the dev shell (`nix develop`), or from an environment
|
||||
that exports both variables. To confirm which SDK a configured build is using:
|
||||
|
||||
```bash
|
||||
grep -o '\-isysroot [^ ]*' build/compile_commands.json | sort -u
|
||||
```
|
||||
|
||||
It should print a `/nix/store/...-apple-sdk-*` path. If it prints
|
||||
`/Library/Developer/CommandLineTools/...`, re-configure from within the shell —
|
||||
CMake caches the sysroot, so an existing `build/` directory keeps the wrong one.
|
||||
|
||||
## `Library not loaded: /nix/store/…` from a binary that used to work
|
||||
|
||||
A binary stops starting after a `nix flake update`, or after
|
||||
`nix-collect-garbage` removes the paths the previous toolchain used:
|
||||
|
||||
```
|
||||
dyld[57271]: Library not loaded: /nix/store/…-libresolv-93/lib/libresolv.9.dylib
|
||||
```
|
||||
|
||||
[`bin/check-nix-store-refs.sh`](../../bin/check-nix-store-refs.sh) finds the same
|
||||
thing without having to run anything, and names the file:
|
||||
|
||||
```
|
||||
$ bin/check-nix-store-refs.sh ~/.conan2-nix
|
||||
::error file=/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig::references the Nix store at run time
|
||||
/Users/you/.conan2-nix/p/b/c-area24ded30c388c/p/bin/adig
|
||||
/nix/store/p4lp3xq4imd1qzqh08x8vcq2zfhi7rca-libresolv-93/lib/libresolv.9.dylib
|
||||
/Users/you/.conan2-nix: checked 135, skipped 2495, 1 with Nix store references.
|
||||
```
|
||||
|
||||
Conan's cache folders are named after a truncated package name plus a hash, so
|
||||
ask Conan which package the offending one belongs to — pass the folder holding
|
||||
the hash, not the file itself:
|
||||
|
||||
```
|
||||
$ conan cache ref ~/.conan2-nix/p/b/c-area24ded30c388c
|
||||
c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650:dab5992496abe6d219defb7986ecbf367615a5e5#…
|
||||
```
|
||||
|
||||
### Why it happens
|
||||
|
||||
The binary records a store path that no longer exists. Nothing we build should:
|
||||
see [Prebuilt packages](./nix.md#prebuilt-packages) for why, and
|
||||
`libresolvSystemStub` in [`nix/darwin.nix`](../../nix/darwin.nix) for the one
|
||||
dependency that needed help to comply.
|
||||
|
||||
A Conan package ID does not encode the nixpkgs revision, so a package built
|
||||
before that stub existed stays in your local cache and keeps being reused. The
|
||||
dev shell is also what tends to produce one: it is a slightly _less_ isolated
|
||||
build environment than CI's, because `mkShell` puts every tool's headers and
|
||||
libraries on the compiler's search path — which is how c-ares found the Nix
|
||||
`libresolv` in the first place.
|
||||
|
||||
### Fix
|
||||
|
||||
Drop that package and let Conan refetch or rebuild it:
|
||||
|
||||
```bash
|
||||
conan remove 'c-ares/*'
|
||||
```
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Installing xrpld 3.3.0 and earlier
|
||||
|
||||
> [!IMPORTANT]
|
||||
> These instructions apply to xrpld 3.3.0 and earlier, published to
|
||||
> repos.ripple.com.
|
||||
> For later releases see [install.md](./install.md).
|
||||
|
||||
This document contains instructions for installing xrpld.
|
||||
The APT package manager is common on Debian-based Linux distributions like
|
||||
Ubuntu,
|
||||
@@ -52,7 +59,7 @@ The default [prefix][1] is typically `/usr/local` on Linux and macOS and
|
||||
|
||||
5. Add the appropriate XRPL repository for your operating system version:
|
||||
|
||||
echo "deb [signed-by=/usr/local/share/keyrings/ripple-key.gpg] https://repos.ripple.com/repos/xrpld-deb focal stable" | \
|
||||
echo "deb [signed-by=/usr/local/share/keyrings/ripple-key.gpg] https://repos.ripple.com/repos/rippled-deb focal stable" | \
|
||||
sudo tee -a /etc/apt/sources.list.d/ripple.list
|
||||
|
||||
The above example is appropriate for **Ubuntu 20.04 Focal Fossa**. For other operating systems, replace the word `focal` with one of the following:
|
||||
@@ -106,8 +113,8 @@ The default [prefix][1] is typically `/usr/local` on Linux and macOS and
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=1
|
||||
baseurl=https://repos.ripple.com/repos/xrpld-rpm/stable/
|
||||
gpgkey=https://repos.ripple.com/repos/xrpld-rpm/stable/repodata/repomd.xml.key
|
||||
baseurl=https://repos.ripple.com/repos/rippled-rpm/stable/
|
||||
gpgkey=https://repos.ripple.com/repos/rippled-rpm/stable/repodata/repomd.xml.key
|
||||
REPOFILE
|
||||
|
||||
_Unstable_
|
||||
@@ -118,8 +125,8 @@ The default [prefix][1] is typically `/usr/local` on Linux and macOS and
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=1
|
||||
baseurl=https://repos.ripple.com/repos/xrpld-rpm/unstable/
|
||||
gpgkey=https://repos.ripple.com/repos/xrpld-rpm/unstable/repodata/repomd.xml.key
|
||||
baseurl=https://repos.ripple.com/repos/rippled-rpm/unstable/
|
||||
gpgkey=https://repos.ripple.com/repos/rippled-rpm/unstable/repodata/repomd.xml.key
|
||||
REPOFILE
|
||||
|
||||
_Nightly_
|
||||
@@ -130,8 +137,8 @@ The default [prefix][1] is typically `/usr/local` on Linux and macOS and
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=1
|
||||
baseurl=https://repos.ripple.com/repos/xrpld-rpm/nightly/
|
||||
gpgkey=https://repos.ripple.com/repos/xrpld-rpm/nightly/repodata/repomd.xml.key
|
||||
baseurl=https://repos.ripple.com/repos/rippled-rpm/nightly/
|
||||
gpgkey=https://repos.ripple.com/repos/rippled-rpm/nightly/repodata/repomd.xml.key
|
||||
REPOFILE
|
||||
|
||||
2. Fetch the latest repo updates:
|
||||
144
docs/install.md
Normal file
144
docs/install.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Installing xrpld
|
||||
|
||||
> [!NOTE]
|
||||
> These instructions apply to packages published from 2026-08-19 onwards.
|
||||
> For xrpld 3.3.0 and earlier see [install-legacy.md](./install-legacy.md).
|
||||
|
||||
`xrpld` is published as DEB and RPM packages for 64-bit x86 Linux.
|
||||
Use APT on Debian-based distributions such as Debian and Ubuntu,
|
||||
and YUM on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux.
|
||||
To build from source instead, see [BUILD.md](../BUILD.md).
|
||||
|
||||
## Release channels
|
||||
|
||||
Packages are published to four channels:
|
||||
|
||||
- `stable` - production releases
|
||||
- `rc` - release candidates
|
||||
- `beta` - beta builds
|
||||
- `develop` - every push to the [`develop` branch](https://github.com/XRPLF/rippled/tree/develop)
|
||||
|
||||
See [Publishing packages](../package/README.md#publishing-packages) for how channels are produced.
|
||||
|
||||
The instructions below use `stable`.
|
||||
To follow another channel, replace `stable` with its name
|
||||
wherever it appears in the repository configuration.
|
||||
|
||||
> [!WARNING]
|
||||
> Channels other than `stable` may be broken at any time.
|
||||
> Do not use them for production servers.
|
||||
|
||||
## Install the xrpld package
|
||||
|
||||
### With the APT package manager
|
||||
|
||||
1. Install utilities:
|
||||
|
||||
```bash
|
||||
sudo apt update -y
|
||||
sudo apt install -y apt-transport-https ca-certificates curl gnupg
|
||||
```
|
||||
|
||||
2. Add the XRPL Foundation package-signing key to your list of trusted keys:
|
||||
|
||||
```bash
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
sudo curl -fsS https://packages.xrplf.org/xrplf.asc -o /etc/apt/keyrings/xrplf.asc
|
||||
```
|
||||
|
||||
3. Check the fingerprint of the newly-added key:
|
||||
|
||||
```bash
|
||||
gpg --show-keys /etc/apt/keyrings/xrplf.asc
|
||||
```
|
||||
|
||||
The output should be:
|
||||
|
||||
```text
|
||||
pub rsa4096 2026-08-18 [SC]
|
||||
B655416741221F780FBCFBC9AA84D41A11D29FA9
|
||||
uid XRPLF Packages <distribution@xrplf.org>
|
||||
```
|
||||
|
||||
In particular, make sure that the fingerprint matches.
|
||||
|
||||
4. Add the repository, using the channel you picked in [Release channels](#release-channels):
|
||||
|
||||
```bash
|
||||
echo "deb [signed-by=/etc/apt/keyrings/xrplf.asc] https://packages.xrplf.org/repository/deb-stable any main" | \
|
||||
sudo tee /etc/apt/sources.list.d/xrplf.list
|
||||
```
|
||||
|
||||
5. Fetch the repository:
|
||||
|
||||
```bash
|
||||
sudo apt -y update
|
||||
```
|
||||
|
||||
6. Install the `xrpld` software package:
|
||||
|
||||
```bash
|
||||
sudo apt -y install xrpld
|
||||
```
|
||||
|
||||
### With the YUM package manager
|
||||
|
||||
1. Add the XRPL Foundation package-signing key:
|
||||
|
||||
```bash
|
||||
sudo rpm --import https://packages.xrplf.org/xrplf.asc
|
||||
```
|
||||
|
||||
2. Add the repository, using the channel you picked in [Release channels](#release-channels):
|
||||
|
||||
```bash
|
||||
cat << 'REPOFILE' | sudo tee /etc/yum.repos.d/xrplf.repo
|
||||
[xrplf-stable]
|
||||
name=XRP Ledger Packages
|
||||
enabled=1
|
||||
baseurl=https://packages.xrplf.org/repository/rpm-stable/$basearch/
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://packages.xrplf.org/xrplf.asc
|
||||
REPOFILE
|
||||
```
|
||||
|
||||
`gpgcheck=1` verifies each package against the key above.
|
||||
`repo_gpgcheck=1` verifies the repository metadata, which the server signs with the same key.
|
||||
|
||||
3. Install the `xrpld` package:
|
||||
|
||||
```bash
|
||||
sudo yum install -y xrpld
|
||||
```
|
||||
|
||||
## The xrpld service
|
||||
|
||||
Both package managers install a systemd unit and enable it, so `xrpld` starts on boot.
|
||||
Check whether it is already running:
|
||||
|
||||
```bash
|
||||
systemctl status xrpld.service
|
||||
```
|
||||
|
||||
The APT packages start it immediately as well; the YUM packages do not, so start it yourself:
|
||||
|
||||
```bash
|
||||
sudo systemctl start xrpld.service
|
||||
```
|
||||
|
||||
### Optional: binding to privileged ports
|
||||
|
||||
To serve incoming API requests on port 80 or 443, grant the service the capability to bind them.
|
||||
You must also update the config file's port settings.
|
||||
|
||||
```bash
|
||||
sudo install -d -m 0755 /etc/systemd/system/xrpld.service.d
|
||||
sudo tee /etc/systemd/system/xrpld.service.d/privileged-ports.conf >/dev/null <<'EOF'
|
||||
[Service]
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart xrpld.service
|
||||
```
|
||||
@@ -543,8 +543,21 @@ public:
|
||||
setround(RoundingMode inMode);
|
||||
|
||||
/**
|
||||
* Returns which mantissa scale is currently in use for normalization.
|
||||
* Convert an integer to a RoundingMode, validating that it is in range.
|
||||
*
|
||||
* Returns std::nullopt if the value does not correspond to a valid
|
||||
* RoundingMode.
|
||||
*/
|
||||
static std::optional<RoundingMode>
|
||||
checkedRoundingMode(int mode) noexcept
|
||||
{
|
||||
if (mode < static_cast<int>(RoundingMode::ToNearest) ||
|
||||
mode > static_cast<int>(RoundingMode::Upward))
|
||||
return std::nullopt;
|
||||
return static_cast<RoundingMode>(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* If you think you need to call this outside of unit tests, no you don't.
|
||||
*/
|
||||
static MantissaRange::MantissaScale
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -82,4 +82,10 @@ base64Encode(std::string_view s)
|
||||
std::string
|
||||
base64Decode(std::string_view data);
|
||||
|
||||
/** Decode a base64url-encoded string (RFC 4648 S5).
|
||||
Converts '-' to '+' and '_' to '/', adds padding, then decodes.
|
||||
*/
|
||||
std::string
|
||||
base64urlDecode(std::string_view data);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace boost {
|
||||
template <>
|
||||
struct hash<::beast::ip::Address>
|
||||
{
|
||||
explicit hash() = default;
|
||||
hash() = default;
|
||||
|
||||
std::size_t
|
||||
operator()(::beast::ip::Address const& addr) const
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace beast::rfc2616 {
|
||||
@@ -186,7 +187,7 @@ splitCommas(FwdIt first, FwdIt last)
|
||||
|
||||
template <class Result = std::vector<std::string>>
|
||||
Result
|
||||
splitCommas(boost::beast::string_view const& s)
|
||||
splitCommas(std::string_view s)
|
||||
{
|
||||
return splitCommas(s.begin(), s.end());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -11,6 +11,7 @@ struct Sections
|
||||
static constexpr auto kCompression = "compression";
|
||||
static constexpr auto kCrawl = "crawl";
|
||||
static constexpr auto kDatabasePath = "database_path";
|
||||
static constexpr auto kDatagramMonitor = "datagram_monitor";
|
||||
static constexpr auto kDebugLogfile = "debug_logfile";
|
||||
static constexpr auto kElbSupport = "elb_support";
|
||||
static constexpr auto kFeatures = "features";
|
||||
@@ -67,6 +68,7 @@ struct Sections
|
||||
static constexpr auto kValidationSeed = "validation_seed";
|
||||
static constexpr auto kValidatorKeys = "validator_keys";
|
||||
static constexpr auto kValidatorKeyRevocation = "validator_key_revocation";
|
||||
static constexpr auto kValidatorKeyType = "validator_key_type";
|
||||
static constexpr auto kValidatorListKeys = "validator_list_keys";
|
||||
static constexpr auto kValidatorListSites = "validator_list_sites";
|
||||
static constexpr auto kValidatorListThreshold = "validator_list_threshold";
|
||||
@@ -94,6 +96,7 @@ struct Keys
|
||||
static constexpr auto kBbtOptions = "bbt_options";
|
||||
static constexpr auto kBgThreads = "bg_threads";
|
||||
static constexpr auto kBlockSize = "block_size";
|
||||
static constexpr auto kBytecodeSizeLimit = "bytecode_size_limit";
|
||||
static constexpr auto kCacheAge = "cache_age";
|
||||
static constexpr auto kCacheMb = "cache_mb";
|
||||
static constexpr auto kCacheSize = "cache_size";
|
||||
@@ -108,6 +111,8 @@ struct Keys
|
||||
static constexpr auto kFileSizeMult = "file_size_mult";
|
||||
static constexpr auto kFilterBits = "filter_bits";
|
||||
static constexpr auto kFilterFull = "filter_full";
|
||||
static constexpr auto kGasLimit = "gas_limit";
|
||||
static constexpr auto kGasPrice = "gas_price";
|
||||
static constexpr auto kHardSet = "hard_set";
|
||||
static constexpr auto kHighThreads = "high_threads";
|
||||
static constexpr auto kHoldTime = "hold_time";
|
||||
@@ -125,6 +130,7 @@ struct Keys
|
||||
static constexpr auto kMaximumTxnInLedger = "maximum_txn_in_ledger";
|
||||
static constexpr auto kMaximumTxnPerAccount = "maximum_txn_per_account";
|
||||
static constexpr auto kMemoryLevel = "memory_level";
|
||||
static constexpr auto kMaxWaitingLedgers = "max_waiting_ledgers";
|
||||
static constexpr auto kMinLedgersToComputeSizeLimit = "min_ledgers_to_compute_size_limit";
|
||||
static constexpr auto kMinimumEscalationMultiplier = "minimum_escalation_multiplier";
|
||||
static constexpr auto kMinimumLastLedgerBuffer = "minimum_last_ledger_buffer";
|
||||
|
||||
@@ -34,7 +34,10 @@ enum class HashRouterFlags : std::uint16_t {
|
||||
PRIVATE4 = 0x0800,
|
||||
// Used in EscrowFinish.cpp
|
||||
PRIVATE5 = 0x1000,
|
||||
PRIVATE6 = 0x2000
|
||||
PRIVATE6 = 0x2000,
|
||||
// Used in apply.cpp
|
||||
PRIVATE7 = 0x4000,
|
||||
PRIVATE8 = 0x8000
|
||||
};
|
||||
|
||||
constexpr HashRouterFlags
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@@ -246,6 +247,9 @@ public:
|
||||
virtual DatabaseCon&
|
||||
getWalletDB() = 0;
|
||||
|
||||
[[nodiscard]] 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&
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace json {
|
||||
|
||||
class Value;
|
||||
|
||||
using Output = std::function<void(boost::beast::string_view const&)>;
|
||||
using Output = std::function<void(std::string_view)>;
|
||||
|
||||
inline Output
|
||||
stringOutput(std::string& s)
|
||||
{
|
||||
return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); };
|
||||
return [&](std::string_view b) { s.append(b.data(), b.size()); };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -528,6 +528,7 @@ public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using size_t = unsigned int;
|
||||
using difference_type = int;
|
||||
using value_type = Value;
|
||||
using SelfType = ValueIteratorBase;
|
||||
|
||||
ValueIteratorBase();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
@@ -68,6 +69,18 @@ public:
|
||||
deliver_ = amount;
|
||||
}
|
||||
|
||||
void
|
||||
setGasUsed(std::optional<std::uint32_t> const gasUsed)
|
||||
{
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
void
|
||||
setVMReturnCode(std::int32_t const vmReturnCode)
|
||||
{
|
||||
vmReturnCode_ = vmReturnCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of modified entries
|
||||
*/
|
||||
@@ -88,6 +101,8 @@ public:
|
||||
|
||||
private:
|
||||
std::optional<STAmount> deliver_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> vmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -78,6 +78,12 @@ public:
|
||||
return base_.succ(key, last);
|
||||
}
|
||||
|
||||
std::optional<key_type>
|
||||
pred(key_type const& key, std::optional<key_type> const& first = std::nullopt) const override
|
||||
{
|
||||
return base_.pred(key, first);
|
||||
}
|
||||
|
||||
std::unique_ptr<SlesType::iter_base>
|
||||
slesBegin() const override
|
||||
{
|
||||
|
||||
@@ -186,6 +186,9 @@ public:
|
||||
std::optional<uint256>
|
||||
succ(uint256 const& key, std::optional<uint256> const& last = std::nullopt) const override;
|
||||
|
||||
std::optional<uint256>
|
||||
pred(uint256 const& key, std::optional<uint256> const& first = std::nullopt) const override;
|
||||
|
||||
SLE::const_pointer
|
||||
read(Keylet const& k) const override;
|
||||
|
||||
|
||||
@@ -221,6 +221,9 @@ public:
|
||||
std::optional<key_type>
|
||||
succ(key_type const& key, std::optional<key_type> const& last = std::nullopt) const override;
|
||||
|
||||
std::optional<key_type>
|
||||
pred(key_type const& key, std::optional<key_type> const& first = std::nullopt) const override;
|
||||
|
||||
SLE::const_pointer
|
||||
read(Keylet const& k) const override;
|
||||
|
||||
|
||||
82
include/xrpl/ledger/OpenViewSandbox.h
Normal file
82
include/xrpl/ledger/OpenViewSandbox.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class OpenViewSandbox
|
||||
{
|
||||
private:
|
||||
OpenView& parent_;
|
||||
std::unique_ptr<OpenView> sandbox_;
|
||||
|
||||
public:
|
||||
using key_type = ReadView::key_type;
|
||||
|
||||
OpenViewSandbox(OpenView& parent)
|
||||
: parent_(parent), sandbox_(std::make_unique<OpenView>(kBatchView, parent))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
rawErase(std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
sandbox_->rawErase(sle);
|
||||
}
|
||||
|
||||
void
|
||||
rawInsert(std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
sandbox_->rawInsert(sle);
|
||||
}
|
||||
|
||||
void
|
||||
rawReplace(std::shared_ptr<SLE> const& sle)
|
||||
{
|
||||
sandbox_->rawReplace(sle);
|
||||
}
|
||||
|
||||
void
|
||||
rawDestroyXRP(XRPAmount const& fee)
|
||||
{
|
||||
sandbox_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
void
|
||||
rawTxInsert(
|
||||
key_type const& key,
|
||||
std::shared_ptr<Serializer const> const& txn,
|
||||
std::shared_ptr<Serializer const> const& metaData)
|
||||
{
|
||||
sandbox_->rawTxInsert(key, txn, metaData);
|
||||
}
|
||||
|
||||
void
|
||||
commit()
|
||||
{
|
||||
sandbox_->apply(parent_);
|
||||
sandbox_ = std::make_unique<OpenView>(kBatchView, parent_);
|
||||
}
|
||||
|
||||
void
|
||||
discard()
|
||||
{
|
||||
sandbox_ = std::make_unique<OpenView>(kBatchView, parent_);
|
||||
}
|
||||
|
||||
OpenView const&
|
||||
view() const
|
||||
{
|
||||
return *sandbox_;
|
||||
}
|
||||
|
||||
OpenView&
|
||||
view()
|
||||
{
|
||||
return *sandbox_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -154,6 +154,19 @@ public:
|
||||
[[nodiscard]] virtual std::optional<key_type>
|
||||
succ(key_type const& key, std::optional<key_type> const& last = std::nullopt) const = 0;
|
||||
|
||||
/** Return the key of the previous state item.
|
||||
|
||||
This returns the key of the first state item
|
||||
whose key is less than the specified key. If
|
||||
no such key is present, std::nullopt is returned.
|
||||
|
||||
If `first` is engaged, returns std::nullopt when
|
||||
the key returned would be outside the open
|
||||
interval (first, key).
|
||||
*/
|
||||
[[nodiscard]] virtual std::optional<key_type>
|
||||
pred(key_type const& key, std::optional<key_type> const& first = std::nullopt) const = 0;
|
||||
|
||||
/**
|
||||
* Return the state item associated with a key.
|
||||
*
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -24,6 +28,7 @@
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -78,6 +83,13 @@ isVaultPseudoAccountFrozen(
|
||||
MPTIssue const& mptShare,
|
||||
std::uint8_t depth);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isVaultPseudoAccountFrozen(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
SLE const& issuanceSle,
|
||||
std::uint8_t depth);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isLPTokenFrozen(
|
||||
ReadView const& view,
|
||||
@@ -85,6 +97,26 @@ isLPTokenFrozen(
|
||||
Asset const& asset,
|
||||
Asset const& asset2);
|
||||
|
||||
/**
|
||||
* Check whether an AMM LPToken may be transferred between @p from and @p to.
|
||||
*
|
||||
* @p lpTokenIssuer is the issuer of the LPToken being moved. If it is not an
|
||||
* AMM account the token is not an LPToken and the transfer is unconditionally
|
||||
* permitted. Otherwise, for each MPT pool asset of that AMM, canTransfer() must
|
||||
* permit the transfer (which exempts the MPT issuer). Non-MPT pool assets are
|
||||
* always transferable by this check, so it is implicitly gated by
|
||||
* featureMPTokensV2 (MPTs can only be AMM pool assets once V2 is enabled).
|
||||
*
|
||||
* @return tesSUCCESS if permitted, otherwise the canTransfer() failure code
|
||||
* (e.g. tecNO_AUTH) of the first MPT pool asset that disallows it.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canTransferLPToken(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
AccountID const& lpTokenIssuer);
|
||||
|
||||
// Return the list of enabled amendments
|
||||
[[nodiscard]] std::set<uint256>
|
||||
getEnabledAmendments(ReadView const& view);
|
||||
@@ -171,7 +203,10 @@ dirLink(
|
||||
* if withdrawing to self.
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* the sender has it (account-based or credential-based).
|
||||
* - Expects any credentials passed in to already exist in the ledger, and
|
||||
* returns an internal error otherwise. Validate them beforehand with
|
||||
* credentials::valid().
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
@@ -182,7 +217,8 @@ canWithdraw(
|
||||
AccountID const& to,
|
||||
SLE::const_ref toSle,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag);
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<uint256>> const& credentialIDs = std::nullopt);
|
||||
|
||||
/**
|
||||
* Checks that can withdraw funds from an object to itself or a destination.
|
||||
@@ -195,7 +231,10 @@ canWithdraw(
|
||||
* if withdrawing to self.
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* the sender has it (account-based or credential-based).
|
||||
* - Expects any credentials passed in to already exist in the ledger, and
|
||||
* returns an internal error otherwise. Validate them beforehand with
|
||||
* credentials::valid().
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
@@ -205,20 +244,25 @@ canWithdraw(
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag);
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<uint256>> const& credentialIDs = std::nullopt);
|
||||
|
||||
/**
|
||||
* Checks that can withdraw funds from an object to itself or a destination.
|
||||
*
|
||||
* The receiver may be either the submitting account (sfAccount) or a different
|
||||
* destination account (sfDestination).
|
||||
* destination account (sfDestination). Credentials, if any, are taken from the
|
||||
* transaction's sfCredentialIDs field.
|
||||
*
|
||||
* - Checks that the receiver account exists.
|
||||
* - If the receiver requires a destination tag, check that one exists, even
|
||||
* if withdrawing to self.
|
||||
* - If withdrawing to self, succeed.
|
||||
* - If not, checks if the receiver requires deposit authorization, and if
|
||||
* the sender has it.
|
||||
* the sender has it (account-based or credential-based).
|
||||
* - Expects any credentials in sfCredentialIDs to already exist in the
|
||||
* ledger, and returns an internal error otherwise. Validate them
|
||||
* beforehand with credentials::valid().
|
||||
* - Checks that the receiver will not exceed the limit (IOU trustline limit
|
||||
* or MPT MaximumAmount).
|
||||
*/
|
||||
@@ -235,6 +279,27 @@ doWithdraw(
|
||||
STAmount const& amount,
|
||||
beast::Journal j);
|
||||
|
||||
enum class SendIssuerHandling { ihSENDER_NOT_ALLOWED, ihRECEIVER_NOT_ALLOWED, ihIGNORE };
|
||||
enum class SendEscrowHandling { ehIGNORE, ehCHECK };
|
||||
enum class SendAuthHandling { ahCHECK_SENDER, ahCHECK_RECEIVER, ahBOTH, ahNEITHER };
|
||||
enum class SendFreezeHandling { fhCHECK_SENDER, fhCHECK_RECEIVER, fhBOTH, fhNEITHER };
|
||||
enum class SendTransferHandling { thIGNORE, thCHECK };
|
||||
enum class SendBalanceHandling { bhIGNORE, bhCHECK };
|
||||
|
||||
TER
|
||||
canTransferFT(
|
||||
ReadView const& view,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
beast::Journal j,
|
||||
SendIssuerHandling issuerHandling,
|
||||
SendEscrowHandling escrowHandling,
|
||||
SendAuthHandling authHandling,
|
||||
SendFreezeHandling freezeHandling,
|
||||
SendTransferHandling transferHandling,
|
||||
SendBalanceHandling balanceHandling);
|
||||
|
||||
/**
|
||||
* Deleter function prototype. Returns the status of the entry deletion
|
||||
* (if should not be skipped) and if the entry should be skipped. The status
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -62,6 +63,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& vmReturnCode,
|
||||
bool isDryRun,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -71,6 +74,9 @@ public:
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
succ(ReadView const& base, key_type const& key, std::optional<key_type> const& last) const;
|
||||
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
pred(ReadView const& base, key_type const& key, std::optional<key_type> const& first) const;
|
||||
|
||||
[[nodiscard]] SLE::const_pointer
|
||||
read(ReadView const& base, Keylet const& k) const;
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ public:
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
succ(key_type const& key, std::optional<key_type> const& last = std::nullopt) const override;
|
||||
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
pred(key_type const& key, std::optional<key_type> const& first = std::nullopt) const override;
|
||||
|
||||
[[nodiscard]] SLE::const_pointer
|
||||
read(Keylet const& k) const override;
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ public:
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
succ(ReadView const& base, key_type const& key, std::optional<key_type> const& last) const;
|
||||
|
||||
[[nodiscard]] std::optional<key_type>
|
||||
pred(ReadView const& base, key_type const& key, std::optional<key_type> const& first) const;
|
||||
|
||||
void
|
||||
erase(SLE::ref sle);
|
||||
|
||||
|
||||
249
include/xrpl/ledger/helpers/AMMCurve.h
Normal file
249
include/xrpl/ledger/helpers/AMMCurve.h
Normal file
@@ -0,0 +1,249 @@
|
||||
// Pluggable AMM curve architecture.
|
||||
// Concentrated liquidity (CurveType 1) based on XRPL-Standards Discussion #427
|
||||
// by Roman Thpt (@RomThpt), which adapted Uniswap v3 tick math, fee tier
|
||||
// structure, and fee accounting to the XRPL. This implementation extends that
|
||||
// work with a pluggable curve interface, StableSwap, and Smart AMM.
|
||||
// See: https://github.com/XRPLF/XRPL-Standards/discussions/427
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ReadView;
|
||||
class ApplyView;
|
||||
|
||||
struct CurveContext
|
||||
{
|
||||
ReadView const* view = nullptr;
|
||||
uint256 const* ammID = nullptr;
|
||||
// Optional out: set to true if a CL swap-walk terminated because it hit
|
||||
// maxTickCrossings. Callers pass a pointer when they want to detect
|
||||
// the cap (e.g. to emit tecAMM_TICK_CAP_HIT); pass nullptr to ignore.
|
||||
bool* tickCapHit = nullptr;
|
||||
};
|
||||
|
||||
class CurveInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CurveInterface() = default;
|
||||
|
||||
virtual std::expected<STAmount, TER>
|
||||
swapIn(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
STAmount const& assetIn,
|
||||
std::uint16_t tfee,
|
||||
STObject const* ammSle,
|
||||
CurveContext const& ctx = {}) const = 0;
|
||||
|
||||
virtual std::expected<STAmount, TER>
|
||||
swapOut(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
STAmount const& assetOut,
|
||||
std::uint16_t tfee,
|
||||
STObject const* ammSle,
|
||||
CurveContext const& ctx = {}) const = 0;
|
||||
|
||||
virtual std::expected<Number, TER>
|
||||
spotPrice(
|
||||
STAmount const& poolIn,
|
||||
STAmount const& poolOut,
|
||||
std::uint16_t tfee,
|
||||
STObject const* ammSle,
|
||||
CurveContext const& ctx = {}) const = 0;
|
||||
|
||||
[[nodiscard]] virtual TER
|
||||
validateParams(STObject const& tx) const = 0;
|
||||
|
||||
virtual std::expected<STAmount, TER>
|
||||
initialLPTokens(
|
||||
STAmount const& asset1,
|
||||
STAmount const& asset2,
|
||||
Issue const& lptIssue,
|
||||
STObject const* txParams) const = 0;
|
||||
|
||||
virtual bool
|
||||
checkInvariant(
|
||||
STAmount const& oldIn,
|
||||
STAmount const& oldOut,
|
||||
STAmount const& newIn,
|
||||
STAmount const& newOut,
|
||||
STObject const* ammSle) const = 0;
|
||||
|
||||
// Apply a realized swap to the AMM SLE. Trustline balances are updated
|
||||
// by the caller (BookStep). For CP and StableSwap the pool state is fully
|
||||
// implicit in trustline balances, so the default is a no-op. CL must
|
||||
// mutate currentTick/activeLiquidity/feeGrowthGlobal and flip
|
||||
// feeGrowthOutside on crossed ticks, because none of that state is
|
||||
// derivable from the trustlines alone.
|
||||
virtual TER
|
||||
applySwap(
|
||||
ApplyView& /*view*/,
|
||||
uint256 const& /*ammID*/,
|
||||
STAmount const& /*assetIn*/,
|
||||
STAmount const& /*assetOut*/,
|
||||
std::uint16_t /*tfee*/,
|
||||
STObject const* /*curveParams*/) const
|
||||
{
|
||||
return tesSUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
CurveInterface const*
|
||||
getCurve(std::uint8_t curveType, Rules const& rules);
|
||||
|
||||
// Max output the pool can deliver before crossing the next initialised
|
||||
// tick boundary in the swap direction. Audit #19: caller (AMMLiquidity's
|
||||
// offer generation) uses this to cap an advertised AMM offer at the
|
||||
// current tick range, so the offer's quality reflects only the marginal
|
||||
// range — not a blended average across multiple tick crossings.
|
||||
//
|
||||
// Returns std::nullopt if there is no further tick in the swap direction
|
||||
// (no cap; offer is bounded only by reserves) or if the pool has no
|
||||
// active liquidity. Returns 0 if the swap is already at the boundary.
|
||||
// Caller should treat nullopt as "no cap".
|
||||
std::optional<Number>
|
||||
maxClOutputWithinCurrentRange(
|
||||
ReadView const& view,
|
||||
uint256 const& ammID,
|
||||
STObject const& ammSle,
|
||||
bool zeroForOne);
|
||||
|
||||
// Equivalent for CtBinned: cap the advertised AMMOffer output at the
|
||||
// active bin's reserve. Without this cap, AMMLiquidity quotes against
|
||||
// the pool's aggregate balance — which spans multiple bins at different
|
||||
// prices — and BookStep mispricesthe offer's quality vs CLOB. Capping
|
||||
// per active bin lets BookStep iterate naturally, getting each bin's
|
||||
// marginal price one offer at a time.
|
||||
//
|
||||
// Returns std::nullopt if no active bin exists (empty pool) or the
|
||||
// active bin lacks the output asset.
|
||||
std::optional<Number>
|
||||
maxBinnedOutputAtActiveBin(
|
||||
ReadView const& view,
|
||||
uint256 const& ammID,
|
||||
STObject const& ammSle,
|
||||
bool inIsAsset0);
|
||||
|
||||
// ─── CL tick bitmap ─────────────────────────────────────────────────────
|
||||
//
|
||||
// Sparse 256-tick-per-word presence bitmap. See spec §3.6.
|
||||
|
||||
// Convert a tick to its (wordIndex, bitInWord) position. Offset-binary so
|
||||
// arithmetic stays in unsigned domain. Uses kTickBitmapOffset (= -minTick)
|
||||
// from AMMCore.h — single source of truth, do NOT duplicate inline.
|
||||
inline std::pair<std::uint16_t, std::uint8_t>
|
||||
tickToBitmapPos(std::int32_t tick) noexcept
|
||||
{
|
||||
auto const offsetT = static_cast<std::uint32_t>(
|
||||
tick + static_cast<std::int32_t>(kTickBitmapOffset));
|
||||
return {static_cast<std::uint16_t>(offsetT >> 8),
|
||||
static_cast<std::uint8_t>(offsetT & 0xFFu)};
|
||||
}
|
||||
|
||||
inline std::int32_t
|
||||
bitmapPosToTick(std::uint16_t wordIndex, std::uint8_t bitInWord) noexcept
|
||||
{
|
||||
auto const offsetT =
|
||||
(static_cast<std::uint32_t>(wordIndex) << 8) | bitInWord;
|
||||
return static_cast<std::int32_t>(offsetT) -
|
||||
static_cast<std::int32_t>(kTickBitmapOffset);
|
||||
}
|
||||
|
||||
// Bit-test for the bitmap word storage. `bits` is the raw `sfBitmapBits`
|
||||
// value read off the SLE. Convention: bit i = LSB of byte (i/8), little-
|
||||
// endian within bytes. The convention is internal — callers that write
|
||||
// bits must use the same scheme (and do, via the maintenance helpers).
|
||||
inline bool
|
||||
bitmapBitIsSet(uint256 const& bits, std::uint8_t pos) noexcept
|
||||
{
|
||||
return ((bits.data()[pos / 8]) >> (pos % 8)) & 1u;
|
||||
}
|
||||
|
||||
// AMMDeposit and AMMWithdraw call these when a tick crosses the
|
||||
// "initialised / uninitialised" boundary (sfLiquidityGross transitioning
|
||||
// 0↔>0). Each pool has a sparse set of `ltAMM_TICK_BITMAP` SLEs covering
|
||||
// 256 ticks each; setting / clearing creates and deletes those SLEs on
|
||||
// demand. Idempotent — calling set on an already-set bit is a no-op.
|
||||
//
|
||||
// Returns tesSUCCESS on the happy path. The current implementation has
|
||||
// no failure path beyond the AMM SLE being missing; reserved as TER for
|
||||
// forward compatibility.
|
||||
TER
|
||||
setTickBitmap(ApplyView& view, uint256 const& ammID, std::int32_t tick, beast::Journal j);
|
||||
|
||||
TER
|
||||
clearTickBitmap(ApplyView& view, uint256 const& ammID, std::int32_t tick, beast::Journal j);
|
||||
|
||||
inline std::uint8_t
|
||||
getCurveType(SLE const& ammSle)
|
||||
{
|
||||
if (ammSle.isFieldPresent(sfCurveType))
|
||||
return ammSle.getFieldU8(sfCurveType);
|
||||
return CtConstantProduct;
|
||||
}
|
||||
|
||||
template <typename TIn, typename TOut>
|
||||
TOut
|
||||
curveSwapIn(
|
||||
TAmounts<TIn, TOut> const& pool,
|
||||
TIn const& assetIn,
|
||||
std::uint16_t tfee,
|
||||
std::uint8_t curveType,
|
||||
STObject const* ammSle,
|
||||
CurveContext const& cctx = {})
|
||||
{
|
||||
if (curveType == CtConstantProduct)
|
||||
return swapAssetIn(pool, assetIn, tfee);
|
||||
|
||||
if (auto const* curve = getCurve(curveType, *getCurrentTransactionRules()))
|
||||
{
|
||||
auto const stPoolIn = toSTAmount(pool.in);
|
||||
auto const stPoolOut = toSTAmount(pool.out);
|
||||
auto const stAssetIn = toSTAmount(assetIn);
|
||||
if (auto const result = curve->swapIn(stPoolIn, stPoolOut, stAssetIn, tfee, ammSle, cctx))
|
||||
return get<TOut>(*result);
|
||||
}
|
||||
return toAmount<TOut>(getAsset(pool.out), 0);
|
||||
}
|
||||
|
||||
template <typename TIn, typename TOut>
|
||||
TIn
|
||||
curveSwapOut(
|
||||
TAmounts<TIn, TOut> const& pool,
|
||||
TOut const& assetOut,
|
||||
std::uint16_t tfee,
|
||||
std::uint8_t curveType,
|
||||
STObject const* ammSle,
|
||||
CurveContext const& cctx = {})
|
||||
{
|
||||
if (curveType == CtConstantProduct)
|
||||
return swapAssetOut(pool, assetOut, tfee);
|
||||
|
||||
if (auto const* curve = getCurve(curveType, *getCurrentTransactionRules()))
|
||||
{
|
||||
auto const stPoolIn = toSTAmount(pool.in);
|
||||
auto const stPoolOut = toSTAmount(pool.out);
|
||||
auto const stAssetOut = toSTAmount(assetOut);
|
||||
if (auto const result = curve->swapOut(stPoolIn, stPoolOut, stAssetOut, tfee, ammSle, cctx))
|
||||
return get<TIn>(*result);
|
||||
}
|
||||
return toMaxAmount<TIn>(getAsset(pool.in));
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -837,7 +837,8 @@ ammLPHolds(
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j);
|
||||
beast::Journal const j,
|
||||
std::uint8_t curveType = CtConstantProduct);
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
@@ -865,7 +866,12 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
|
||||
* AMM object and account are deleted. Otherwise tecINCOMPLETE is returned.
|
||||
*/
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::Journal j);
|
||||
deleteAMMAccount(
|
||||
Sandbox& view,
|
||||
Asset const& asset,
|
||||
Asset const& asset2,
|
||||
beast::Journal j,
|
||||
std::uint8_t curveType = 0);
|
||||
|
||||
/**
|
||||
* Initialize Auction and Voting slots and set the trading/discounted fee.
|
||||
|
||||
19
include/xrpl/ledger/helpers/AMMTickMath.h
Normal file
19
include/xrpl/ledger/helpers/AMMTickMath.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
Number
|
||||
tickToSqrtPrice(std::int32_t tick);
|
||||
|
||||
std::int32_t
|
||||
sqrtPriceToTick(Number const& sqrtPrice);
|
||||
|
||||
bool
|
||||
isValidTick(std::int32_t tick, std::int32_t tickSpacing);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -339,6 +338,17 @@ adjustLoanBrokerOwnerCount(
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer);
|
||||
|
||||
/**
|
||||
* Returns the transfer fee charged for a specific currency of the issuer.
|
||||
* A per-currency TransferFee on the currency's TokenIssuance overrides the
|
||||
* account-wide TransferRate.
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer, Currency const& currency);
|
||||
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, Issue const& issue);
|
||||
|
||||
/**
|
||||
* Generate a pseudo-account address from a pseudo owner key.
|
||||
* @param pseudoOwnerKey The key to generate the address from
|
||||
@@ -353,14 +363,14 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
|
||||
*
|
||||
* The list is constructed during initialization and is const after that.
|
||||
* Pseudo-account designator fields MUST be maintained by including the
|
||||
* SField::sMD_PseudoAccount flag in the SField definition.
|
||||
* SField::kSmdPseudoAccount flag in the SField definition.
|
||||
*/
|
||||
[[nodiscard]] std::vector<SField const*> const&
|
||||
getPseudoAccountFields();
|
||||
|
||||
/**
|
||||
* Returns true if and only if sleAcct is a pseudo-account or specific
|
||||
* pseudo-accounts in pseudoFieldFilter.
|
||||
* Returns true if and only if sleAcct is a pseudo-account of any kind
|
||||
* (i.e. carries at least one field flagged with SField::kSmdPseudoAccount).
|
||||
*
|
||||
* Returns false if sleAcct is:
|
||||
* - NOT a pseudo-account OR
|
||||
@@ -368,18 +378,15 @@ getPseudoAccountFields();
|
||||
* - null pointer
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(SLE::const_pointer sleAcct, std::set<SField const*> const& pseudoFieldFilter = {});
|
||||
isPseudoAccount(SLE::const_pointer sleAcct);
|
||||
|
||||
/**
|
||||
* Convenience overload that reads the account from the view.
|
||||
*/
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(
|
||||
ReadView const& view,
|
||||
AccountID const& accountId,
|
||||
std::set<SField const*> const& pseudoFieldFilter = {})
|
||||
isPseudoAccount(ReadView const& view, AccountID const& accountId)
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
102
include/xrpl/ledger/helpers/ContractUtils.h
Normal file
102
include/xrpl/ledger/helpers/ContractUtils.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STData.h>
|
||||
#include <xrpl/protocol/STJson.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ContractDataMap : public std::map<xrpl::AccountID, std::pair<bool, STJson>>
|
||||
{
|
||||
public:
|
||||
uint32_t modifiedCount = 0;
|
||||
};
|
||||
|
||||
class ContractEventMap : public std::map<std::string, STJson>
|
||||
{
|
||||
};
|
||||
|
||||
namespace contract {
|
||||
|
||||
/** The maximum number of data modifications in a single function. */
|
||||
int64_t constexpr maxDataModifications = 1000;
|
||||
|
||||
/** The maximum number of bytes the data can occupy. */
|
||||
int64_t constexpr maxContractDataSize = 1024;
|
||||
|
||||
/** The multiplier for contract data size calculations. */
|
||||
int64_t constexpr dataByteMultiplier = 512;
|
||||
|
||||
/** The cost multiplier of creating a contract in bytes. */
|
||||
int64_t constexpr createByteMultiplier = 500ULL;
|
||||
|
||||
/** The value to return when the fee calculation failed. */
|
||||
int64_t constexpr feeCalculationFailed = 0x7FFFFFFFFFFFFFFFLL;
|
||||
|
||||
/** The maximum number of contract parameters that can be in a transaction. */
|
||||
std::size_t constexpr maxContractParams = 8;
|
||||
|
||||
/** The maximum number of contract functions that can be in a transaction. */
|
||||
std::size_t constexpr maxContractFunctions = 32;
|
||||
|
||||
int64_t
|
||||
contractCreateFee(uint64_t byteCount);
|
||||
|
||||
NotTEC
|
||||
preflightFunctions(STTx const& tx, beast::Journal j);
|
||||
|
||||
NotTEC
|
||||
preflightInstanceParameters(STTx const& tx, beast::Journal j);
|
||||
|
||||
bool
|
||||
validateParameterMapping(STArray const& params, STArray const& values, beast::Journal j);
|
||||
|
||||
NotTEC
|
||||
preflightInstanceParameterValues(STTx const& tx, beast::Journal j);
|
||||
|
||||
NotTEC
|
||||
preflightFlagParameters(STArray const& parameters, beast::Journal j);
|
||||
|
||||
bool
|
||||
isValidParameterFlag(std::uint32_t flags);
|
||||
|
||||
TER
|
||||
preclaimFlagParameters(
|
||||
ReadView const& view,
|
||||
AccountID const& sourceAccount,
|
||||
AccountID const& contractAccount,
|
||||
STArray const& parameters,
|
||||
beast::Journal j);
|
||||
|
||||
TER
|
||||
doApplyFlagParameters(
|
||||
ApplyView& view,
|
||||
STTx const& tx,
|
||||
AccountID const& sourceAccount,
|
||||
AccountID const& contractAccount,
|
||||
STArray const& parameters,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal j);
|
||||
|
||||
TER
|
||||
finalizeContractData(
|
||||
ServiceRegistry& registry,
|
||||
ApplyView& view,
|
||||
AccountID const& contractAccount,
|
||||
ContractDataMap const& dataMap,
|
||||
ContractEventMap const& eventMap,
|
||||
uint256 const& txnID);
|
||||
|
||||
} // namespace contract
|
||||
} // namespace xrpl
|
||||
49
include/xrpl/ledger/helpers/CouponHelpers.h
Normal file
49
include/xrpl/ledger/helpers/CouponHelpers.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Settle a holder's accrued coupons on their MPToken.
|
||||
*
|
||||
* Adds units * (AccruedPerUnit - CouponIndex) to CouponAccrued, rounded
|
||||
* once downward to the coupon asset, then advances CouponIndex to
|
||||
* AccruedPerUnit. Units are MPTAmount + LockedAmount. Increments the
|
||||
* schedule's ClaimantCount when CouponAccrued becomes non-zero on a
|
||||
* holder that had none. The caller must view.update() both entries on
|
||||
* tesSUCCESS.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
couponSettleMPToken(SLE::ref schedule, SLE::ref mptoken, beast::Journal j);
|
||||
|
||||
/**
|
||||
* Settle a holder against the schedule of the issuance their MPToken
|
||||
* belongs to, if that issuance carries lsfMPTCouponSchedule. Does
|
||||
* nothing when the flag is clear, which is the case for every issuance
|
||||
* without a coupon schedule.
|
||||
*
|
||||
* This is the entry point called from the MPT unit-change helpers.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
couponSettleIfScheduled(
|
||||
ApplyView& view,
|
||||
SLE::const_ref issuance,
|
||||
SLE::ref mptoken,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* The units a holder is credited for: MPTAmount + LockedAmount.
|
||||
*/
|
||||
[[nodiscard]] std::uint64_t
|
||||
couponHolderUnits(SLE::const_ref mptoken);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -25,8 +27,295 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Validate that @p account may lock @p amount of a token for later delivery
|
||||
* to @p dest.
|
||||
*
|
||||
* The lock-side counterpart of escrowUnlockPreclaimHelper: every issuer
|
||||
* control (locking opt-in, authorization, freeze/lock, transferability,
|
||||
* spendable balance) that gates locking token value lives here, so any
|
||||
* transactor that locks funds applies the same rules. The signature is
|
||||
* view-based rather than PreclaimContext-based so it can also run from
|
||||
* doApply.
|
||||
*/
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount =
|
||||
accountHolds(view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowLockApplyHelper(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockPreclaimHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze = true);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (checkFreeze &&
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
escrowUnlockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (checkFreeze && isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockApplyHelper(
|
||||
@@ -55,9 +344,6 @@ escrowUnlockApplyHelper<Issue>(
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
@@ -67,6 +353,10 @@ escrowUnlockApplyHelper<Issue>(
|
||||
if (receiverIssuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
@@ -272,4 +562,18 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
journal);
|
||||
}
|
||||
|
||||
// calculateAdditionalReserve computes the owner count impact of an Escrow.
|
||||
// An escrow without a FinishFunction costs 1 reserve. With a FinishFunction,
|
||||
// each additional 500 bytes beyond the first 500 adds another reserve slot.
|
||||
template <class T>
|
||||
static int32_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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
@@ -58,11 +60,65 @@ canApplyToBrokerCover(
|
||||
bool
|
||||
checkLendingProtocolDependencies(Rules const& rules, STTx const& tx);
|
||||
|
||||
/**
|
||||
* The accounts and asset that LoanManage::defaultLoan's fixCleanup3_4_0
|
||||
* freeze/lock exemption applies to.
|
||||
*
|
||||
* `defaultLoan` moves funds from the LoanBroker pseudo-account to the Vault
|
||||
* pseudo-account via `accountSend`. Since neither is the vault asset's
|
||||
* issuer, this is a third-party transfer that transits through the issuer in
|
||||
* two hops (broker -> issuer, issuer -> vault; see
|
||||
* `directSendNoLimitIOU`/`directSendNoLimitMPT`), so the exemption must cover
|
||||
* both the issuer/broker and issuer/vault pairs, not a direct broker/vault
|
||||
* pair. `asset` scopes it further to the vault's own currency/MPT issuance,
|
||||
* so an unrelated one the same accounts happen to hold is still protected.
|
||||
*/
|
||||
struct LoanDefaultFreezeExemptAccounts
|
||||
{
|
||||
AccountID issuer;
|
||||
AccountID broker;
|
||||
AccountID vault;
|
||||
Asset asset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves the accounts and asset a LoanManage default transaction is
|
||||
* exempt from freeze/lock for.
|
||||
*
|
||||
* @param view Ledger view used to resolve the Loan -> LoanBroker -> Vault
|
||||
* chain.
|
||||
* @param tx The transaction under invariant review.
|
||||
* @return The exempt accounts and asset if `tx` is a `ttLOAN_MANAGE`
|
||||
* transaction with the `tfLoanDefault` flag set, `fixCleanup3_4_0` is
|
||||
* enabled, and the loan/broker/vault objects it references can all be
|
||||
* resolved; `std::nullopt` otherwise.
|
||||
*/
|
||||
[[nodiscard]] std::optional<LoanDefaultFreezeExemptAccounts>
|
||||
getLoanDefaultFreezeExemptAccounts(ReadView const& view, STTx const& tx);
|
||||
|
||||
static constexpr std::uint32_t kSecondsInYear = 365 * 24 * 60 * 60;
|
||||
|
||||
Number
|
||||
loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
|
||||
|
||||
/**
|
||||
* Assets a loan earns per second at this principal outstanding.
|
||||
*
|
||||
* Equation (27) of XLS-66 is linear in elapsed time, and sfPaymentInterval
|
||||
* cancels out of it, so a loan's accrual rate depends only on its principal
|
||||
* and interest rate. Principal is flat between payments, which makes the rate
|
||||
* piecewise-constant with breakpoints exactly at the events that update it —
|
||||
* summing it across a vault's loans is therefore exact, not an approximation.
|
||||
*/
|
||||
inline Number
|
||||
loanAccrualRate(Number const& principalOutstanding, TenthBips32 interestRate)
|
||||
{
|
||||
if (interestRate == TenthBips32{0} || principalOutstanding <= Number{})
|
||||
return Number{};
|
||||
|
||||
return tenthBipsOfValue(principalOutstanding, interestRate) / Number{kSecondsInYear};
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the periodic payment is always rounded consistently
|
||||
*/
|
||||
@@ -286,6 +342,12 @@ computeFullPaymentInterest(
|
||||
std::uint32_t startDate,
|
||||
TenthBips32 closeInterestRate);
|
||||
|
||||
// Returns true if the loan's next payment is late per protocol rules. The
|
||||
// boundary is amendment-gated: with fixCleanup3_4_0 the due date must be
|
||||
// strictly in the past, otherwise the exact due-date instant counts as late.
|
||||
[[nodiscard]] bool
|
||||
isPaymentLate(ReadView const& view, SLE::const_ref loanSle);
|
||||
|
||||
// Deltas applied to Vault.AssetsTotal and LoanBroker.DebtTotal at a single
|
||||
// accounting touch point (origination, payment, impair/unimpair/default).
|
||||
struct AccountingDeltas
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace xrpl {
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isGlobalFrozen(SLE const& issuanceSle);
|
||||
|
||||
/**
|
||||
* Returns true if @p account's MPToken for @p mptIssue carries the
|
||||
* individual-lock flag (lsfMPTLocked).
|
||||
@@ -40,9 +43,29 @@ isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
|
||||
* receive tokens — it combines isIndividualFrozen, isGlobalFrozen, and
|
||||
* isVaultPseudoAccountFrozen into a single complete check.
|
||||
*/
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isIndividualFrozen(SLE const& mptSle);
|
||||
|
||||
/**
|
||||
* Returns true if @p account cannot send or receive tokens of @p mptIssue
|
||||
* because a freeze applies. This is the complete check callers should use
|
||||
* before moving MPT value: it combines @ref isGlobalFrozen (issuance-level
|
||||
* lock), @ref isIndividualFrozen (per-holder lock bit), and the transitive
|
||||
* vault pseudo-account check (if @p mptIssue is a vault share, the underlying
|
||||
* asset is checked, and so on recursively up to @c maxAssetCheckDepth).
|
||||
*
|
||||
* The @c SLE overload takes an already-loaded ltMPTOKEN or ltMPTOKEN_ISSUANCE
|
||||
* ledger entry; for ltMPTOKEN it can skip the per-holder individual-lock lookup.
|
||||
* @ref isAnyFrozen answers the same question for a set of accounts and returns true
|
||||
* if the freeze applies to any of them.
|
||||
*
|
||||
* @param depth Current recursion depth for the vault-share walk. Callers
|
||||
* outside this module should leave it at the default.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isFrozen(
|
||||
ReadView const& view,
|
||||
@@ -50,6 +73,18 @@ isFrozen(
|
||||
MPTIssue const& mptIssue,
|
||||
std::uint8_t depth = 0);
|
||||
|
||||
/**
|
||||
* SLE overload: pass an already-loaded ltMPTOKEN (holder row) or
|
||||
* ltMPTOKEN_ISSUANCE to reuse it for the freeze checks and avoid re-reading
|
||||
* the same object. For an ltMPTOKEN, @p sle is used directly for the
|
||||
* individual-lock check and the issuance is read once for global-freeze and
|
||||
* vault-pseudo-account. For an ltMPTOKEN_ISSUANCE, @p sle is used directly
|
||||
* for global-freeze and vault-pseudo-account, and the caller's holder row is
|
||||
* read for the individual-lock check.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isFrozen(ReadView const& view, AccountID const& account, SLE const& sle, std::uint8_t depth = 0);
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAnyFrozen(
|
||||
ReadView const& view,
|
||||
@@ -97,6 +132,29 @@ authorizeMPToken(
|
||||
std::uint32_t flags = 0,
|
||||
std::optional<AccountID> holderID = std::nullopt);
|
||||
|
||||
// Authorize an AMM-issued MPT and apply the reserve-exemption rule in
|
||||
// one shot: standard authorize (which increments owner count) followed
|
||||
// by an immediate adjustOwnerCount(-1) so the LP doesn't pay reserve
|
||||
// for the AMM-issued holding. Returns the same TER as authorizeMPToken.
|
||||
//
|
||||
// Callers must ensure the MPT's issuance is owned by an AMM pseudo-
|
||||
// account; mis-using this helper for non-AMM-issued MPTs would let an
|
||||
// LP hold an arbitrary issuer's MPT for free.
|
||||
[[nodiscard]] TER
|
||||
authorizeAMMIssuedMPT(
|
||||
ApplyViewContext ctx,
|
||||
XRPAmount const& priorBalance,
|
||||
MPTID const& mptIssuanceID,
|
||||
AccountID const& account,
|
||||
beast::Journal journal);
|
||||
|
||||
// Symmetric for snapshot-style SLEs the AMM owns on behalf of an LP
|
||||
// (e.g. ltAMM_BIN_HOLDING). The SLE is inserted into the LP's owner
|
||||
// directory by the caller; this helper compensates the owner-count
|
||||
// increment so the LP doesn't pay reserve.
|
||||
void
|
||||
exemptAMMOwnedSLE(ApplyView& view, AccountID const& account, beast::Journal journal);
|
||||
|
||||
/**
|
||||
* Check if the account lacks required authorization for MPT.
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user