mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-15 20:08:34 +00:00
Compare commits
353 Commits
alphanet
...
ximinez/on
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7cfc46275 | ||
|
|
c3c555c260 | ||
|
|
3cc27a4a67 | ||
|
|
17bec9e4d4 | ||
|
|
0e54845db0 | ||
|
|
6f1f53a152 | ||
|
|
080ad9fe34 | ||
|
|
2620ae59ae | ||
|
|
d3d5f39787 | ||
|
|
77793df659 | ||
|
|
6abcc9fe1a | ||
|
|
e67e9f6660 | ||
|
|
6718b3276b | ||
|
|
642dbad885 | ||
|
|
833433b25e | ||
|
|
84e32ed33c | ||
|
|
23369e114a | ||
|
|
d3d0c6366a | ||
|
|
c15c7bfd54 | ||
|
|
af61140cd9 | ||
|
|
4e1de43939 | ||
|
|
dc71651429 | ||
|
|
e372201499 | ||
|
|
8b2a7f6253 | ||
|
|
6211dbd1cc | ||
|
|
d89063acae | ||
|
|
0e328d8277 | ||
|
|
7d55bb7d8e | ||
|
|
14c190fd31 | ||
|
|
efe843d6b2 | ||
|
|
41e422d2c1 | ||
|
|
7f10f3938f | ||
|
|
9c8b45f0ed | ||
|
|
f7e96beba7 | ||
|
|
5a7a715283 | ||
|
|
8f1974d0ec | ||
|
|
f84e07427c | ||
|
|
d375fa356f | ||
|
|
5d5e4d3405 | ||
|
|
1eacb33d72 | ||
|
|
fafc48bda3 | ||
|
|
9a2b6866a7 | ||
|
|
8a49f93b78 | ||
|
|
c5c85eef48 | ||
|
|
5aed870ff0 | ||
|
|
0fe2be63f4 | ||
|
|
4c93c8d5be | ||
|
|
5817d3547e | ||
|
|
7a8d86b0b1 | ||
|
|
1bbe54ad60 | ||
|
|
0bb9716784 | ||
|
|
295c71b007 | ||
|
|
78f3148e4c | ||
|
|
894263bb89 | ||
|
|
9de4e7e58d | ||
|
|
ec844351d9 | ||
|
|
41b4cf36e6 | ||
|
|
6f7ad510e3 | ||
|
|
5db10a4d3b | ||
|
|
919cfd5f49 | ||
|
|
476a64d5ce | ||
|
|
303952aeb4 | ||
|
|
85a6dfee1f | ||
|
|
621f876337 | ||
|
|
da2f36c8a6 | ||
|
|
fab69c28d2 | ||
|
|
afedf445f0 | ||
|
|
fe1b1c8f92 | ||
|
|
e61b134742 | ||
|
|
2ba3edaf14 | ||
|
|
7cf063b1d8 | ||
|
|
5f087f6446 | ||
|
|
a2446e8f99 | ||
|
|
6accc87a10 | ||
|
|
313d0d188b | ||
|
|
53dc3b4051 | ||
|
|
b589985dae | ||
|
|
180c979835 | ||
|
|
88665a6de0 | ||
|
|
66526a8a48 | ||
|
|
18c11097b6 | ||
|
|
bfe54849c9 | ||
|
|
831ac87627 | ||
|
|
1b7af3165d | ||
|
|
48ca6e0025 | ||
|
|
0047ed4db2 | ||
|
|
6dc73cb528 | ||
|
|
7f58c3d7d6 | ||
|
|
86f52c412c | ||
|
|
4dce2f22b6 | ||
|
|
fe8368b970 | ||
|
|
d68273c755 | ||
|
|
df489c7a6e | ||
|
|
b45038f70a | ||
|
|
44cc4e656f | ||
|
|
ac3435bf0a | ||
|
|
bd2f5dbd35 | ||
|
|
4644347d21 | ||
|
|
e34e05ebec | ||
|
|
9b22c6c80b | ||
|
|
d4e166bbdc | ||
|
|
b80776b0fe | ||
|
|
9af4fb521e | ||
|
|
7ee290674c | ||
|
|
68f050b186 | ||
|
|
2be13c18a1 | ||
|
|
df5fa5aade | ||
|
|
51ef3a7343 | ||
|
|
dd4eca22b7 | ||
|
|
1116dbfd0e | ||
|
|
258d5e1f1f | ||
|
|
4270367efc | ||
|
|
fad34e852d | ||
|
|
f5b4ac358f | ||
|
|
875bcc530e | ||
|
|
e75f5b101b | ||
|
|
95f74d61b2 | ||
|
|
4e12b787be | ||
|
|
8b6c80027b | ||
|
|
ace73678cf | ||
|
|
12bbba00b0 | ||
|
|
2affb66f50 | ||
|
|
d09e785c39 | ||
|
|
97fdf310f9 | ||
|
|
d3ce76825c | ||
|
|
1e7e274727 | ||
|
|
b272d71cc7 | ||
|
|
21268d9c36 | ||
|
|
932e22df7d | ||
|
|
652a7cd225 | ||
|
|
4a224dbfa4 | ||
|
|
d2a5981f87 | ||
|
|
a977836630 | ||
|
|
bf075200bb | ||
|
|
ee49e76a16 | ||
|
|
c1318990f3 | ||
|
|
69128294f8 | ||
|
|
149e884ebc | ||
|
|
d27353225c | ||
|
|
13a0f77eb5 | ||
|
|
549c093398 | ||
|
|
fb66ca7a2e | ||
|
|
e2af73b0a0 | ||
|
|
14c3c9a256 | ||
|
|
4b6851a287 | ||
|
|
d182673d24 | ||
|
|
bea609d805 | ||
|
|
56eeb20bc8 | ||
|
|
e293a3d918 | ||
|
|
3dff580d39 | ||
|
|
487f5a0fd3 | ||
|
|
d8134f98e9 | ||
|
|
450a623d4b | ||
|
|
9e517be4ce | ||
|
|
b8370438fb | ||
|
|
ecb5604d3b | ||
|
|
c4527e7b0f | ||
|
|
778e2b3ce8 | ||
|
|
295d03aec8 | ||
|
|
9262c2e624 | ||
|
|
b385a41aa5 | ||
|
|
4eb9726097 | ||
|
|
28a38ef1cc | ||
|
|
39f9380b2b | ||
|
|
239fcaceb4 | ||
|
|
8ab86f009e | ||
|
|
d4b58a74f4 | ||
|
|
ff900591ae | ||
|
|
a5b7471af6 | ||
|
|
e0734986dd | ||
|
|
8440f479e5 | ||
|
|
ac390622e0 | ||
|
|
5d807f0d6d | ||
|
|
b93294f26b | ||
|
|
ef95ace0f9 | ||
|
|
fc58bf6edf | ||
|
|
348555d5ba | ||
|
|
302802e42d | ||
|
|
5d881f87a3 | ||
|
|
a8a8035b32 | ||
|
|
45af14231f | ||
|
|
380bf274d0 | ||
|
|
460ec5eeea | ||
|
|
14be8ca4ea | ||
|
|
948264d44c | ||
|
|
9c816b2043 | ||
|
|
f68402acd1 | ||
|
|
5358e25eaa | ||
|
|
a34d1e5537 | ||
|
|
78a122943d | ||
|
|
06135e7203 | ||
|
|
f20425fa4f | ||
|
|
3e94546acc | ||
|
|
3b088ed0dc | ||
|
|
26182ed52e | ||
|
|
de2a3e10f5 | ||
|
|
e17f8554fc | ||
|
|
386a7192ba | ||
|
|
12cc6e424d | ||
|
|
c9deecf1b7 | ||
|
|
ddd1b49f38 | ||
|
|
c1b2a24005 | ||
|
|
86d88eca31 | ||
|
|
ef09eaea00 | ||
|
|
c504cfb291 | ||
|
|
0c217dfa2b | ||
|
|
b0198d2566 | ||
|
|
7eee8ca802 | ||
|
|
2a079a0154 | ||
|
|
40989c1178 | ||
|
|
addc831eb3 | ||
|
|
b4efc6d116 | ||
|
|
125d075d6e | ||
|
|
370a775479 | ||
|
|
1a2ee706eb | ||
|
|
2a981357ba | ||
|
|
1ae475e724 | ||
|
|
a3e9401fbc | ||
|
|
9091469f9e | ||
|
|
17fa54f1f9 | ||
|
|
8fb5347c2d | ||
|
|
6739bf998f | ||
|
|
6eea38ba67 | ||
|
|
e9cf88b359 | ||
|
|
645b203476 | ||
|
|
be2aff1f4c | ||
|
|
56ed237e82 | ||
|
|
fd7b0fd135 | ||
|
|
e700994891 | ||
|
|
c76f7029ac | ||
|
|
d535c5fb2a | ||
|
|
54f860463e | ||
|
|
950434b8ff | ||
|
|
ee365e876d | ||
|
|
c6c59834b9 | ||
|
|
63b47914b8 | ||
|
|
9e02e5be2e | ||
|
|
093cd70fa1 | ||
|
|
376d65a483 | ||
|
|
a0d9a2458e | ||
|
|
456f639cf7 | ||
|
|
2c559ec2f3 | ||
|
|
619c81f463 | ||
|
|
f1490df960 | ||
|
|
7bdf74de98 | ||
|
|
1743d6fb98 | ||
|
|
ca7a5bb926 | ||
|
|
ce8b1a3f1e | ||
|
|
486fa75a10 | ||
|
|
f8d68cd3d3 | ||
|
|
ef7a3f5606 | ||
|
|
4f84ed7490 | ||
|
|
d534103131 | ||
|
|
82dff3c2ce | ||
|
|
30d73eb5ba | ||
|
|
1b2754bac2 | ||
|
|
cf80cafc75 | ||
|
|
b8897d51de | ||
|
|
3ff25eeb65 | ||
|
|
2bbfc4e786 | ||
|
|
2b1eb052e6 | ||
|
|
360e214e54 | ||
|
|
2618afed94 | ||
|
|
698ba2c788 | ||
|
|
b614e99588 | ||
|
|
fe8e4af2fa | ||
|
|
0a897f1528 | ||
|
|
cf8a3f5779 | ||
|
|
db39a39868 | ||
|
|
37a03d28c2 | ||
|
|
19d275425a | ||
|
|
88e9045602 | ||
|
|
5adbc536b6 | ||
|
|
e27af94ba9 | ||
|
|
43fe1e7e9c | ||
|
|
f456a858c8 | ||
|
|
084c3aa88e | ||
|
|
34f9b63921 | ||
|
|
bd3de79817 | ||
|
|
304eee2259 | ||
|
|
9e729b7f59 | ||
|
|
dd141468c4 | ||
|
|
933147c21f | ||
|
|
9201a4f591 | ||
|
|
5adb1e9b8b | ||
|
|
4df84d7988 | ||
|
|
cd87c0968b | ||
|
|
8a8e7c90bf | ||
|
|
e806069065 | ||
|
|
ce948cbec0 | ||
|
|
6ed34b3294 | ||
|
|
7161a235ca | ||
|
|
71463810de | ||
|
|
e997219a85 | ||
|
|
895cc13fa6 | ||
|
|
8d3c3ca29a | ||
|
|
9829553807 | ||
|
|
e551f9731a | ||
|
|
fd827bf58b | ||
|
|
5a3baba34d | ||
|
|
c78f5b160f | ||
|
|
485f78761a | ||
|
|
23cd2f7b21 | ||
|
|
5753266c43 | ||
|
|
4722d2607d | ||
|
|
85b5b4f855 | ||
|
|
a16f492f0f | ||
|
|
3633dc632c | ||
|
|
b3b30c3a86 | ||
|
|
c78a7684f4 | ||
|
|
cf83d92630 | ||
|
|
a56b1274d8 | ||
|
|
ae4bdd0492 | ||
|
|
e90102dd3b | ||
|
|
71f0e8db3d | ||
|
|
638929373a | ||
|
|
8440654377 | ||
|
|
9fa66c4741 | ||
|
|
38a9235145 | ||
|
|
c7a3cc9108 | ||
|
|
248337908d | ||
|
|
3d003619fd | ||
|
|
f163dca12c | ||
|
|
6e0ce458e5 | ||
|
|
5fae8480f1 | ||
|
|
e6587d374a | ||
|
|
376cc404e0 | ||
|
|
9898ca638f | ||
|
|
34b46d8f7c | ||
|
|
fe7d0798a7 | ||
|
|
0cecc09d71 | ||
|
|
e091d55561 | ||
|
|
69cf18158b | ||
|
|
6513c53817 | ||
|
|
e13baa58a5 | ||
|
|
951056fe9b | ||
|
|
67700ea6bd | ||
|
|
e5442cf3f1 | ||
|
|
da68076f04 | ||
|
|
b24116a118 | ||
|
|
f67398c6bf | ||
|
|
43d3eb1a24 | ||
|
|
0993315ed5 | ||
|
|
0bc383ada9 | ||
|
|
1841ceca43 | ||
|
|
2714cebabd | ||
|
|
e184db4ce2 | ||
|
|
ac6dc6943c | ||
|
|
ddd53806df | ||
|
|
e629a1f70e | ||
|
|
68076d969c | ||
|
|
d3009d3e1c | ||
|
|
54f7f3c894 |
@@ -7,8 +7,6 @@ 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)
|
||||
@@ -45,7 +43,6 @@ suggestWords:
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
words:
|
||||
- cusip
|
||||
- abempty
|
||||
- AMMID
|
||||
- AMMMPT
|
||||
@@ -71,11 +68,8 @@ words:
|
||||
- Btrfs
|
||||
- Buildx
|
||||
- canonicality
|
||||
- cdylib
|
||||
- canonicalised
|
||||
- canonicality
|
||||
- cctools
|
||||
- CGNAT
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
@@ -120,7 +114,7 @@ words:
|
||||
- dsymutil
|
||||
- dxrpl
|
||||
- elgamal
|
||||
- emittance
|
||||
- enabled
|
||||
- enablerepo
|
||||
- endmacro
|
||||
- envrc
|
||||
@@ -183,11 +177,11 @@ words:
|
||||
- mathbunnyru
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
- Merkle
|
||||
- misprediction
|
||||
- mispricing
|
||||
- missingok
|
||||
- MPTAMM
|
||||
- mptbalance
|
||||
- MPTDEX
|
||||
- mptflags
|
||||
@@ -262,8 +256,8 @@ words:
|
||||
- replayer
|
||||
- repodata
|
||||
- repomd
|
||||
- rerandomization
|
||||
- rerandomize
|
||||
- rerandomization
|
||||
- rerandomized
|
||||
- rerandomizes
|
||||
- rerere
|
||||
@@ -286,8 +280,8 @@ words:
|
||||
- rustup
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- Schnorr
|
||||
- scons
|
||||
- Schnorr
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
@@ -312,9 +306,7 @@ words:
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
- stjson
|
||||
- stnum
|
||||
- stnumber
|
||||
- stobj
|
||||
- stobject
|
||||
- stpath
|
||||
@@ -341,8 +333,8 @@ words:
|
||||
- txn
|
||||
- txns
|
||||
- txs
|
||||
- UBSAN
|
||||
- ubsan
|
||||
- UBSAN
|
||||
- ufdio
|
||||
- umant
|
||||
- unacquired
|
||||
@@ -354,9 +346,7 @@ words:
|
||||
- unfindable
|
||||
- unflatten
|
||||
- unfund
|
||||
- ungated
|
||||
- unimpair
|
||||
- unmetered
|
||||
- unroutable
|
||||
- unscalable
|
||||
- unserviced
|
||||
@@ -382,11 +372,9 @@ words:
|
||||
- writeme
|
||||
- wsrch
|
||||
- wthread
|
||||
- Xahau
|
||||
- xbridge
|
||||
- xchain
|
||||
- xcrun
|
||||
- xfloat
|
||||
- ximinez
|
||||
- XMACRO
|
||||
- xored
|
||||
@@ -397,3 +385,4 @@ words:
|
||||
- xxhash
|
||||
- xxhasher
|
||||
- zstdio
|
||||
- CGNAT
|
||||
|
||||
3
.envrc
3
.envrc
@@ -1,8 +1,5 @@
|
||||
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
2
.github/CODEOWNERS
vendored
@@ -1,2 +0,0 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
58
.github/actions/release-info/action.yml
vendored
58
.github/actions/release-info/action.yml
vendored
@@ -7,10 +7,10 @@ outputs:
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
channel:
|
||||
description: "The release channel this build belongs to."
|
||||
value: ${{ steps.release_info.outputs.channel }}
|
||||
value: ${{ steps.channel.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 }}
|
||||
description: "The package release number: 1 for a tag, the run number otherwise."
|
||||
value: ${{ steps.pkg_release.outputs.pkg_release }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -39,6 +39,52 @@ runs:
|
||||
|
||||
echo "version=${version}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Determine release channel and package release
|
||||
id: release_info
|
||||
uses: XRPLF/actions/release-info@7cc0e4a8d9d0b838f92c48d312856b190341bbba
|
||||
# Only a tag says how mature a build is: a push is a develop build whatever
|
||||
# its version, and a non-public codebase keeps its packages to itself.
|
||||
- name: Determine release channel
|
||||
id: channel
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
VISIBILITY: ${{ github.event.repository.visibility }}
|
||||
run: |
|
||||
pre_release=""
|
||||
if [[ "${REF_NAME}" == *-* ]]; then
|
||||
pre_release="${REF_NAME#*-}"
|
||||
fi
|
||||
|
||||
if [[ "${VISIBILITY}" != "public" ]]; then
|
||||
channel=private
|
||||
elif [[ "${IS_TAG}" != "true" ]]; then
|
||||
channel=develop
|
||||
elif [[ -z "${pre_release}" ]]; then
|
||||
channel=stable
|
||||
elif [[ "${pre_release}" =~ ^rc[0-9]+(\+.*)?$ ]]; then
|
||||
channel=unstable
|
||||
elif [[ "${pre_release}" =~ ^b(0|[1-9][0-9]*)(\+.*)?$ ]]; then
|
||||
channel=experimental
|
||||
else
|
||||
echo "Unsupported pre-release in tag '${REF_NAME}'. Use bN or rcN." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "channel=${channel}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
# A tag is packaged once, so its release number is fixed at 1. Develop builds
|
||||
# repeat the same version, so the run number is what makes each push an
|
||||
# upgrade rather than a reinstall.
|
||||
- name: Determine package release
|
||||
id: pkg_release
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
RUN_NUMBER: ${{ github.run_number }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
pkg_release=1
|
||||
else
|
||||
pkg_release="${RUN_NUMBER}"
|
||||
fi
|
||||
|
||||
echo "pkg_release=${pkg_release}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
9
.github/actions/setup-nix-env/action.yml
vendored
9
.github/actions/setup-nix-env/action.yml
vendored
@@ -40,11 +40,10 @@ runs:
|
||||
# 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}"
|
||||
# Workspace-local, so `cleanup-workspace` clears it, but not the
|
||||
# `.conan2` prepare-runner hands the system toolchain: that Conan is a
|
||||
# different version, and the two would migrate each other's cache.
|
||||
echo "CONAN_HOME=${{ github.workspace }}/.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.
|
||||
|
||||
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,7 +19,6 @@ 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
|
||||
@@ -207,7 +206,6 @@ 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
|
||||
|
||||
105
.github/scripts/strategy-matrix/generate.py
vendored
105
.github/scripts/strategy-matrix/generate.py
vendored
@@ -23,19 +23,6 @@ _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()
|
||||
@@ -50,27 +37,17 @@ 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.
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class LinuxConfig:
|
||||
"""One entry in a linux.json 'configs' array."""
|
||||
"""One entry in linux.json's 'configs' or 'package_configs' arrays."""
|
||||
|
||||
compiler: list[str]
|
||||
build_type: list[str]
|
||||
@@ -80,13 +57,7 @@ class LinuxConfig:
|
||||
sanitizers: list[str] = dataclasses.field(default_factory=list)
|
||||
suffix: str = ""
|
||||
extra_cmake_args: str = ""
|
||||
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)
|
||||
image: str = "" # only used by package_configs entries
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -95,16 +66,22 @@ 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={
|
||||
distro: [LinuxConfig(**c) for c in cfgs]
|
||||
for distro, cfgs in data["configs"].items()
|
||||
},
|
||||
configs=parse(data["configs"]),
|
||||
package_configs=parse(data.get("package_configs", {})),
|
||||
)
|
||||
|
||||
|
||||
@@ -170,7 +147,6 @@ class MatrixEntry:
|
||||
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
|
||||
@@ -180,7 +156,7 @@ class PackagingEntry:
|
||||
xrpld_artifact_name: str
|
||||
validator_keys_artifact_name: str
|
||||
image: str
|
||||
package_type: str # "deb" or "rpm"; drives the format-specific steps
|
||||
distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -221,9 +197,13 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
effective_sanitizers,
|
||||
effective_archs.items(),
|
||||
):
|
||||
name = config_name(
|
||||
distro, compiler, build_type, arch, cfg.suffix, sanitizer
|
||||
)
|
||||
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)
|
||||
|
||||
entries.append(
|
||||
MatrixEntry(
|
||||
config_name=name,
|
||||
@@ -236,7 +216,6 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
architecture=arch_info,
|
||||
sanitizers=sanitizer,
|
||||
compiler=compiler,
|
||||
force_supported=cfg.force_supported,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -244,33 +223,27 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
|
||||
|
||||
def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
||||
"""Generate the packaging matrix from the configs that carry a 'package' map.
|
||||
"""Generate the packaging matrix from a LinuxFile's package_configs section.
|
||||
|
||||
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.
|
||||
Packaging uses vanilla distro images (debian:bookworm, almalinux:9) 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 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.
|
||||
The artifact names must match what the build job uploads: one artifact per
|
||||
binary, each named after the build config.
|
||||
"""
|
||||
entries = []
|
||||
for distro, configs in linux.configs.items():
|
||||
for distro, configs in linux.package_configs.items():
|
||||
for cfg in configs:
|
||||
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)
|
||||
for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type):
|
||||
config_name = f"{distro}-{compiler}-{build_type.lower()}-amd64"
|
||||
entries.append(
|
||||
PackagingEntry(
|
||||
xrpld_artifact_name=f"xrpld-{name}",
|
||||
validator_keys_artifact_name=f"validator-keys-{name}",
|
||||
image=cfg.package.image,
|
||||
package_type=cfg.package.type,
|
||||
xrpld_artifact_name=f"xrpld-{config_name}",
|
||||
validator_keys_artifact_name=f"validator-keys-{config_name}",
|
||||
image=cfg.image,
|
||||
distro=distro,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
53
.github/scripts/strategy-matrix/linux.json
vendored
53
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-060957e",
|
||||
"image_tag": "sha-a0074f8",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
@@ -17,6 +17,7 @@
|
||||
"minimal": true,
|
||||
"benchmark": true
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
@@ -29,6 +30,7 @@
|
||||
"arch": ["arm64"],
|
||||
"minimal": false
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["gcc", "clang"],
|
||||
"build_type": ["Debug", "Release"],
|
||||
@@ -36,6 +38,7 @@
|
||||
"minimal": false,
|
||||
"sanitizers": ["address", "undefinedbehavior"]
|
||||
},
|
||||
|
||||
{
|
||||
"compiler": ["clang"],
|
||||
"build_type": ["Debug"],
|
||||
@@ -59,45 +62,47 @@
|
||||
"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",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
}
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON"
|
||||
}
|
||||
],
|
||||
|
||||
"rhel": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON",
|
||||
"package": {
|
||||
"type": "rpm",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-49cdc10"
|
||||
}
|
||||
"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-a6983f8"
|
||||
}
|
||||
],
|
||||
|
||||
"rhel": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-a6983f8"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
6
.github/workflows/build-nix-images.yml
vendored
6
.github/workflows/build-nix-images.yml
vendored
@@ -5,13 +5,14 @@ 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"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
pull_request:
|
||||
@@ -23,7 +24,6 @@ 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@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@65d5a0bd72be4ecea95cff0673a6e0672ab5243a
|
||||
with:
|
||||
image_name: xrpld/nix-${{ matrix.distro.name }}
|
||||
dockerfile: nix/docker/Dockerfile
|
||||
|
||||
20
.github/workflows/build-packaging-images.yml
vendored
20
.github/workflows/build-packaging-images.yml
vendored
@@ -6,13 +6,13 @@ on:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "package/Dockerfile"
|
||||
- "package/install-packaging-tools.sh"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/build-packaging-images.yml"
|
||||
- "bin/install-packaging-tools.sh"
|
||||
- "package/docker/**"
|
||||
- "package/Dockerfile"
|
||||
- "package/install-packaging-tools.sh"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -33,17 +33,15 @@ 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:trixie
|
||||
# AlmaLinux rather than UBI, which does not ship rpm-sign.
|
||||
base_image: debian:bookworm
|
||||
# AlmaLinux rather than UBI9, which does not ship rpm-sign.
|
||||
- name: rhel
|
||||
base_image: almalinux:10
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
base_image: almalinux:9
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@65d5a0bd72be4ecea95cff0673a6e0672ab5243a
|
||||
with:
|
||||
image_name: xrpld/packaging-${{ matrix.distro.name }}
|
||||
dockerfile: package/docker/Dockerfile
|
||||
dockerfile: package/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@034e87065fcd0100214cf0672923bd38d193cf78
|
||||
uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@65d5a0bd72be4ecea95cff0673a6e0672ab5243a
|
||||
with:
|
||||
image_name: xrpld/pre-commit
|
||||
dockerfile: bin/pre-commit/Dockerfile
|
||||
|
||||
103
.github/workflows/build-supported-image.yml
vendored
103
.github/workflows/build-supported-image.yml
vendored
@@ -1,103 +0,0 @@
|
||||
# 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 }}
|
||||
2
.github/workflows/cargo-audit.yml
vendored
2
.github/workflows/cargo-audit.yml
vendored
@@ -34,7 +34,7 @@ permissions:
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
|
||||
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@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
uses: XRPLF/actions/prepare-runner@7bf7ceca5932114abdd0d43493c3c30c5a654e13
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
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@279ec358f4a1be4088be3e024b07916fa97c75b6
|
||||
uses: XRPLF/actions/.github/workflows/pre-commit.yml@f1952595d212e86169935135efc66294b4574131
|
||||
with:
|
||||
runs_on: ubuntu-latest
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-473fe44" }'
|
||||
container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-f56b79f" }'
|
||||
|
||||
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-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
uses: XRPLF/actions/prepare-runner@7bf7ceca5932114abdd0d43493c3c30c5a654e13
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
@@ -91,4 +91,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy
|
||||
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
||||
|
||||
21
.github/workflows/reusable-build-test-config.yml
vendored
21
.github/workflows/reusable-build-test-config.yml
vendored
@@ -74,11 +74,6 @@ on:
|
||||
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:
|
||||
@@ -133,22 +128,8 @@ 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@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
uses: XRPLF/actions/prepare-runner@7bf7ceca5932114abdd0d43493c3c30c5a654e13
|
||||
with:
|
||||
enable_ccache: ${{ inputs.ccache_enabled }}
|
||||
|
||||
|
||||
1
.github/workflows/reusable-build-test.yml
vendored
1
.github/workflows/reusable-build-test.yml
vendored
@@ -52,6 +52,5 @@ jobs:
|
||||
sanitizers: ${{ matrix.sanitizers }}
|
||||
compiler: ${{ matrix.compiler || '' }}
|
||||
toolchain: ${{ matrix.toolchain || '' }}
|
||||
force_supported: ${{ matrix.force_supported || false }}
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
4
.github/workflows/reusable-clang-tidy.yml
vendored
4
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
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-060957e"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-a0074f8"
|
||||
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@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
uses: XRPLF/actions/prepare-runner@7bf7ceca5932114abdd0d43493c3c30c5a654e13
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
220
.github/workflows/reusable-package.yml
vendored
220
.github/workflows/reusable-package.yml
vendored
@@ -1,14 +1,11 @@
|
||||
# Build, verify and publish Linux packages from the pre-built xrpld and
|
||||
# validator-keys artifacts, in three stages:
|
||||
# Build Linux packages from the pre-built xrpld and validator-keys artifacts:
|
||||
#
|
||||
# - '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'
|
||||
# - one job per distro, taken from "package_configs" in linux.json
|
||||
# - each job runs in that distro's container, which is what decides DEB or RPM
|
||||
# - with 'publish: true' a job also uploads what it built
|
||||
# (see package/publish_pkg.sh)
|
||||
#
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the jobs below.
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the job below.
|
||||
name: Package
|
||||
|
||||
on:
|
||||
@@ -42,7 +39,6 @@ defaults:
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
PACKAGE_DIR: packages
|
||||
|
||||
jobs:
|
||||
generate-matrix:
|
||||
@@ -74,17 +70,12 @@ jobs:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 10
|
||||
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
|
||||
|
||||
- name: Download pre-built xrpld binary
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
@@ -106,204 +97,33 @@ jobs:
|
||||
|
||||
- name: Build package
|
||||
env:
|
||||
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}"
|
||||
PKG_CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: ./package/build_pkg.sh
|
||||
|
||||
# Before the upload, so the artifact, the tested package and the published
|
||||
# package are the same bytes.
|
||||
# Before the upload, so the artifact and the published package are the
|
||||
# same bytes. DEBs are not signed, so the key is never set on that job.
|
||||
- name: Sign RPM
|
||||
if: ${{ inputs.publish && matrix.package_type == 'rpm' }}
|
||||
if: ${{ inputs.publish && matrix.distro == 'rhel' }}
|
||||
env:
|
||||
PKG_SIGNING_KEY: ${{ secrets.signing_key }}
|
||||
run: ./package/sign_rpm.py --package-dir "${BUILD_DIR}"
|
||||
run: ./package/sign_rpm.sh "${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/xrpld_*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-[0-9]*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.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
|
||||
if: ${{ inputs.publish }}
|
||||
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}
|
||||
NEXUS_USERNAME: ${{ secrets.remote_username }}
|
||||
NEXUS_PASSWORD: ${{ secrets.remote_password }}
|
||||
run: ./package/publish_pkg.sh "${CHANNEL}" "${BUILD_DIR}"
|
||||
|
||||
14
.github/workflows/reusable-rust.yml
vendored
14
.github/workflows/reusable-rust.yml
vendored
@@ -1,9 +1,8 @@
|
||||
# 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.
|
||||
# (and therefore the lints, coverage instrumentation and the cargo cache) matches
|
||||
# what production builds use.
|
||||
#
|
||||
# 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
|
||||
@@ -28,7 +27,7 @@ permissions:
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -41,14 +40,11 @@ jobs:
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
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
|
||||
|
||||
@@ -70,7 +66,7 @@ jobs:
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
7
.github/workflows/reusable-upload-recipe.yml
vendored
7
.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-060957e
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-a0074f8
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
@@ -49,11 +49,6 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- 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
|
||||
|
||||
2
.github/workflows/upload-conan-deps.yml
vendored
2
.github/workflows/upload-conan-deps.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare runner
|
||||
uses: XRPLF/actions/prepare-runner@b3e255d74d785d053e4903da8ac90983cd7d9e82
|
||||
uses: XRPLF/actions/prepare-runner@7bf7ceca5932114abdd0d43493c3c30c5a654e13
|
||||
with:
|
||||
enable_ccache: false
|
||||
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -72,16 +72,11 @@ 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/settings.local.json
|
||||
AGENTS.override.md
|
||||
CLAUDE.local.md
|
||||
GEMINI.local.md
|
||||
/.claude
|
||||
/CLAUDE.md
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
|
||||
@@ -70,11 +70,6 @@ repos:
|
||||
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
|
||||
@@ -87,27 +82,11 @@ 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
42
AGENTS.md
@@ -1,42 +0,0 @@
|
||||
# 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,56 +22,17 @@ 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.
|
||||
|
||||
## XRP Ledger server version 3.4.0
|
||||
## Unreleased
|
||||
|
||||
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
||||
This section contains changes targeting a future version.
|
||||
|
||||
### 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.
|
||||
### Additions
|
||||
|
||||
- `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.
|
||||
@@ -79,8 +40,9 @@ This release contains bug fixes only and no API changes.
|
||||
- `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 in 3.2.0
|
||||
### Bugfixes
|
||||
|
||||
- `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)
|
||||
@@ -92,24 +54,11 @@ This release contains bug fixes only and no API changes.
|
||||
- `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)
|
||||
|
||||
## 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.
|
||||
- `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)
|
||||
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
|
||||
@@ -99,7 +99,6 @@ if(only_docs)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(deps/dilithium)
|
||||
include(deps/Boost)
|
||||
|
||||
add_subdirectory(external/antithesis-sdk)
|
||||
@@ -115,7 +114,6 @@ 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(
|
||||
|
||||
@@ -59,17 +59,6 @@ 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.)
|
||||
@@ -93,7 +82,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`.
|
||||
- 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 test source files must 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
|
||||
|
||||
@@ -158,7 +158,6 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||
check cargo-nextest cargo nextest --version
|
||||
check clippy-driver
|
||||
check rust-analyzer
|
||||
check rust-nightly rust-nightly run rustc --version
|
||||
check rustc
|
||||
check rustfmt
|
||||
fi
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/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())
|
||||
@@ -1360,39 +1360,6 @@
|
||||
# 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
|
||||
|
||||
@@ -69,8 +69,6 @@ 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.py) installs to FHS-standard
|
||||
The packaging script (package/build_pkg.sh) installs to FHS-standard
|
||||
paths (/usr/bin, /etc/xrpld, etc.) regardless of CMAKE_INSTALL_PREFIX,
|
||||
so no prefix guard is needed here.
|
||||
#]===================================================================]
|
||||
@@ -38,20 +38,19 @@ if(NOT TARGET validator-keys)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(DPKG_BUILDPACKAGE_EXECUTABLE)
|
||||
set(pkg_type deb)
|
||||
else()
|
||||
set(pkg_type rpm)
|
||||
endif()
|
||||
set(package_env
|
||||
SRC_DIR=${CMAKE_SOURCE_DIR}
|
||||
BUILD_DIR=${CMAKE_BINARY_DIR}
|
||||
PKG_RELEASE=${pkg_release}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
package
|
||||
COMMAND
|
||||
${CMAKE_SOURCE_DIR}/package/build_pkg.py --package-type=${pkg_type}
|
||||
--build-dir=${CMAKE_BINARY_DIR} --pkg-release=${pkg_release}
|
||||
--channel=UNRELEASED
|
||||
${CMAKE_COMMAND} -E env ${package_env}
|
||||
${CMAKE_SOURCE_DIR}/package/build_pkg.sh
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS xrpld validator-keys
|
||||
COMMENT "Building Linux ${pkg_type} package"
|
||||
COMMENT "Building Linux package (deb/rpm inferred from host tooling)"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
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)
|
||||
@@ -61,8 +61,6 @@ SETTING_DEFAULTS = {
|
||||
"delegable": "Delegation::NotDelegable",
|
||||
"amendment": "uint256{}",
|
||||
"privileges": "Privilege::NoPriv",
|
||||
"emittance": "Emittance::Emitable",
|
||||
"firewall": "FirewallAction::Allow",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"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",
|
||||
|
||||
@@ -36,7 +36,6 @@ class Xrpl(ConanFile):
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.6.3",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.9",
|
||||
"zlib/1.3.2",
|
||||
]
|
||||
|
||||
@@ -225,7 +224,6 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -8,9 +8,6 @@ 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
|
||||
|
||||
@@ -8,6 +8,3 @@ crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
cxx.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
|
||||
|
||||
#[cxx::bridge(namespace = "rs::hello_world")]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
@@ -10,14 +8,3 @@ mod ffi {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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"]
|
||||
@@ -1,279 +0,0 @@
|
||||
# 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).
|
||||
@@ -13,9 +13,9 @@ To build from source instead, see [BUILD.md](../BUILD.md).
|
||||
|
||||
Packages are published to four channels:
|
||||
|
||||
- `stable` - production releases
|
||||
- `rc` - release candidates
|
||||
- `beta` - beta builds
|
||||
- `stable` - the latest production release
|
||||
- `unstable` - release candidates
|
||||
- `experimental` - 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.
|
||||
|
||||
@@ -543,21 +543,8 @@ public:
|
||||
setround(RoundingMode inMode);
|
||||
|
||||
/**
|
||||
* Convert an integer to a RoundingMode, validating that it is in range.
|
||||
* Returns which mantissa scale is currently in use for normalization.
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -82,10 +82,4 @@ 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>
|
||||
{
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
|
||||
std::size_t
|
||||
operator()(::beast::ip::Address const& addr) const
|
||||
|
||||
@@ -11,7 +11,6 @@ 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";
|
||||
@@ -68,7 +67,6 @@ 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";
|
||||
@@ -96,7 +94,6 @@ 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";
|
||||
@@ -111,8 +108,6 @@ 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";
|
||||
|
||||
@@ -34,10 +34,7 @@ enum class HashRouterFlags : std::uint16_t {
|
||||
PRIVATE4 = 0x0800,
|
||||
// Used in EscrowFinish.cpp
|
||||
PRIVATE5 = 0x1000,
|
||||
PRIVATE6 = 0x2000,
|
||||
// Used in apply.cpp
|
||||
PRIVATE7 = 0x4000,
|
||||
PRIVATE8 = 0x8000
|
||||
PRIVATE6 = 0x2000
|
||||
};
|
||||
|
||||
constexpr HashRouterFlags
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#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>
|
||||
|
||||
@@ -247,9 +246,6 @@ 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&
|
||||
|
||||
@@ -528,7 +528,6 @@ 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,7 +13,6 @@
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
@@ -69,18 +68,6 @@ 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
|
||||
*/
|
||||
@@ -101,8 +88,6 @@ public:
|
||||
|
||||
private:
|
||||
std::optional<STAmount> deliver_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> vmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -78,12 +78,6 @@ 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,9 +186,6 @@ 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,9 +221,6 @@ 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;
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#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,19 +154,6 @@ 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,10 +5,6 @@
|
||||
#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>
|
||||
@@ -279,27 +275,6 @@ 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,7 +16,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -63,8 +62,6 @@ 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);
|
||||
|
||||
@@ -74,9 +71,6 @@ 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,9 +50,6 @@ 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,9 +56,6 @@ 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);
|
||||
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
// 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,8 +837,7 @@ ammLPHolds(
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j,
|
||||
std::uint8_t curveType = CtConstantProduct);
|
||||
beast::Journal const j);
|
||||
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
@@ -866,12 +865,7 @@ 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,
|
||||
std::uint8_t curveType = 0);
|
||||
deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::Journal j);
|
||||
|
||||
/**
|
||||
* Initialize Auction and Voting slots and set the trading/discounted fee.
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#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,6 +15,7 @@
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -338,17 +339,6 @@ 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
|
||||
@@ -363,14 +353,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::kSmdPseudoAccount flag in the SField definition.
|
||||
* SField::sMD_PseudoAccount flag in the SField definition.
|
||||
*/
|
||||
[[nodiscard]] std::vector<SField const*> const&
|
||||
getPseudoAccountFields();
|
||||
|
||||
/**
|
||||
* 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 true if and only if sleAcct is a pseudo-account or specific
|
||||
* pseudo-accounts in pseudoFieldFilter.
|
||||
*
|
||||
* Returns false if sleAcct is:
|
||||
* - NOT a pseudo-account OR
|
||||
@@ -378,15 +368,18 @@ getPseudoAccountFields();
|
||||
* - null pointer
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(SLE::const_pointer sleAcct);
|
||||
isPseudoAccount(SLE::const_pointer sleAcct, std::set<SField const*> const& pseudoFieldFilter = {});
|
||||
|
||||
/**
|
||||
* Convenience overload that reads the account from the view.
|
||||
*/
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(ReadView const& view, AccountID const& accountId)
|
||||
isPseudoAccount(
|
||||
ReadView const& view,
|
||||
AccountID const& accountId,
|
||||
std::set<SField const*> const& pseudoFieldFilter = {})
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)));
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
#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
|
||||
@@ -1,49 +0,0 @@
|
||||
#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,10 +2,8 @@
|
||||
|
||||
#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>
|
||||
@@ -27,295 +25,8 @@
|
||||
#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(
|
||||
@@ -344,6 +55,9 @@ 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;
|
||||
|
||||
@@ -353,10 +67,6 @@ 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?
|
||||
@@ -562,18 +272,4 @@ 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
|
||||
|
||||
@@ -101,24 +101,6 @@ 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
|
||||
*/
|
||||
@@ -342,12 +324,6 @@ 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
|
||||
|
||||
@@ -132,29 +132,6 @@ 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.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/nft.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -162,12 +161,4 @@ checkTrustlineDeepFrozen(
|
||||
beast::Journal const j,
|
||||
Issue const& issue);
|
||||
|
||||
TER
|
||||
transferNFToken(
|
||||
ApplyView& view,
|
||||
AccountID const& buyer,
|
||||
AccountID const& seller,
|
||||
uint256 const& nftokenID,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl::nft
|
||||
|
||||
@@ -2,85 +2,28 @@
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Validate the token amount of a PaymentChannelCreate or PaymentChannelFund
|
||||
* transaction during preflight.
|
||||
*
|
||||
* @param rules The current ledger rules used to check amendment status.
|
||||
* @param amount The channel or funding amount from the transaction.
|
||||
* @return tesSUCCESS if the amount is valid; temBAD_AMOUNT, temBAD_CURRENCY,
|
||||
* or temDISABLED otherwise.
|
||||
*/
|
||||
template <ValidIssueType T>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper(Rules const& rules, STAmount const& amount);
|
||||
|
||||
template <>
|
||||
inline NotTEC
|
||||
payChanAmountPreflightHelper<Issue>(Rules const&, STAmount const& amount)
|
||||
{
|
||||
if (amount.native() || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (badCurrency() == amount.get<Issue>().currency)
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline NotTEC
|
||||
payChanAmountPreflightHelper<MPTIssue>(Rules const& rules, STAmount const& amount)
|
||||
{
|
||||
if (!rules.enabled(fixCleanup3_2_0) && !rules.enabled(featureMPTokensV1))
|
||||
return temDISABLED;
|
||||
|
||||
if (amount.native() || amount.mpt() > MPTAmount{kMaxMpTokenAmount} || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a payment channel and return its remaining funds to the channel owner.
|
||||
*
|
||||
* @param slep The SLE for the PayChannel object to close.
|
||||
* @param ctx The apply view context (view and transaction) in which ledger
|
||||
* state modifications are made.
|
||||
* @param key The ledger key identifying the PayChannel entry.
|
||||
* @param txAccount The account submitting the transaction that closes the
|
||||
* channel.
|
||||
* @param j Journal used for fatal-level diagnostic messages.
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
* @param slep The SLE for the PayChannel object to close.
|
||||
* @param view The apply view in which ledger state modifications are made.
|
||||
* @param key The ledger key identifying the PayChannel entry.
|
||||
* @param j Journal used for fatal-level diagnostic messages.
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
*/
|
||||
TER
|
||||
closeChannel(
|
||||
SLE::ref slep,
|
||||
ApplyViewContext ctx,
|
||||
uint256 const& key,
|
||||
AccountID const& txAccount,
|
||||
beast::Journal j);
|
||||
closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j);
|
||||
|
||||
/**
|
||||
* Add two uint32_t values with saturation at UINT32_MAX.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#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/Asset.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
namespace repo {
|
||||
|
||||
/**
|
||||
* Seconds in a year, the denominator of the annualized interest rate.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A repo is active once the buyer has accepted it, which is recorded by
|
||||
* sfStartDate. Before that it is a pending offer.
|
||||
*/
|
||||
[[nodiscard]] inline bool
|
||||
isActive(SLE::const_ref sleRepo)
|
||||
{
|
||||
return sleRepo->isFieldPresent(sfStartDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount the seller owes to repurchase the collateral.
|
||||
*
|
||||
* PurchasePrice * (1 + InterestRate * elapsed / kSecondsInYear), where elapsed
|
||||
* runs from StartDate to the close time, capped at MaturityDate so the seller
|
||||
* never pays for time past maturity. Computed in Number and rounded up, so
|
||||
* rounding never favours the seller.
|
||||
*/
|
||||
[[nodiscard]] STAmount
|
||||
repurchaseAmount(SLE::const_ref sleRepo, std::uint32_t closeTime);
|
||||
|
||||
/**
|
||||
* Lock the collateral out of the seller's spendable balance.
|
||||
*
|
||||
* XRP is deducted from the account balance by the caller; this handles the
|
||||
* issued-asset cases the same way an escrow does.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
lockCollateral(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& seller,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal);
|
||||
|
||||
/**
|
||||
* The XLS-85 checks that decide whether collateral may be locked at all.
|
||||
*
|
||||
* Both parties are checked, not just the seller: the buyer receives the
|
||||
* collateral if the repo defaults, so an unauthorized or frozen buyer would
|
||||
* leave the collateral unable to move at exactly the moment it must.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkCollateral(
|
||||
ReadView const& view,
|
||||
AccountID const& seller,
|
||||
AccountID const& buyer,
|
||||
STAmount const& collateral,
|
||||
beast::Journal journal);
|
||||
|
||||
/**
|
||||
* Return the locked collateral to an account and remove the entry.
|
||||
*
|
||||
* Used by cancel, close and default; they differ only in who receives the
|
||||
* collateral and whether any cash moved first.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
releaseAndDelete(
|
||||
ApplyViewContext ctx,
|
||||
SLE::ref sleRepo,
|
||||
AccountID const& receiver,
|
||||
beast::Journal journal);
|
||||
|
||||
} // namespace repo
|
||||
} // namespace xrpl
|
||||
@@ -239,13 +239,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* XRP and the issuer itself are always tesSUCCESS. Otherwise, after
|
||||
* fixCleanup3_4_0, an existing trust line returns tecDUPLICATE without
|
||||
* consulting issuer freeze or DefaultRipple; both still apply on the create
|
||||
* path (DefaultRipple off is terNO_RIPPLE). canAddHolding() ignores existing
|
||||
* holdings, so transactors that may create a holding in doApply should gate
|
||||
* their preclaim call on it: after the amendment only when no holding
|
||||
* exists, before it always.
|
||||
* Any transactors that call addEmptyHolding() in doApply must call
|
||||
* canAddHolding() in preflight with the same View and Asset
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
|
||||
@@ -1,429 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.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>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
canTransferTokenHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal const& j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
canTransferTokenHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
AccountID issuer = amount.getIssuer();
|
||||
if (issuer == account)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Issuer is the same as the account.";
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// If the issuer does not exist, return tecNO_ISSUER
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Issuer does not exist.";
|
||||
return tecNO_ISSUER;
|
||||
}
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState =
|
||||
view.read(keylet::trustLine(account, issuer, amount.get<Issue>().currency));
|
||||
if (!sleRippleState)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Trust line does not exist.";
|
||||
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)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Invalid trust line state.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Invalid trust line state.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Account is not authorized";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), dest); ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Destination is not authorized.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, amount.get<Issue>()) ||
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.get<Issue>().account))
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Account is frozen.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, amount.get<Issue>()) ||
|
||||
isDeepFrozen(view, dest, amount.get<Issue>().currency, amount.get<Issue>().account))
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Destination is frozen.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view, account, amount.get<Issue>().currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Spendable amount is less "
|
||||
"than or equal to 0.";
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Spendable amount is less "
|
||||
"than the 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
|
||||
canTransferTokenHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
AccountID issuer = amount.getIssuer();
|
||||
if (issuer == account)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Issuer is the same as the 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)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: MPT issuance does not exist.";
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
}
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Issuer is not the same as "
|
||||
"the issuer of the MPT.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Account does not have the MPT.";
|
||||
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);
|
||||
ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Account is not authorized.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Destination is not authorized.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// If the issuer has locked the account, return tecLOCKED
|
||||
if (isFrozen(view, account, mptIssue))
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Account is locked.";
|
||||
return tecLOCKED;
|
||||
}
|
||||
|
||||
// If the issuer has locked the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, mptIssue))
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Destination is locked.";
|
||||
return tecLOCKED;
|
||||
}
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: MPT cannot be transferred.";
|
||||
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)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Spendable amount is less "
|
||||
"than or equal to 0.";
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
{
|
||||
JLOG(j.trace()) << "canTransferTokenHelper: Spendable amount is less "
|
||||
"than the 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 <ValidIssueType T>
|
||||
TER
|
||||
doTransferTokenHelper(
|
||||
ApplyView& view,
|
||||
SLE::ref sleDest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
doTransferTokenHelper<Issue>(
|
||||
ApplyView& view,
|
||||
SLE::ref sleDest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, amount.get<Issue>());
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
// Review Note: We could remove this and just say to use batch to auth the
|
||||
// token first
|
||||
if (!view.exists(trustLineKey) && createAsset && issuer != receiver)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
if (xrpBalance < accountReserve(view, sleDest, journal, {.ownerCountDelta = 1}))
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: Trust line does not exist. "
|
||||
"Insufficent reserve to create line.";
|
||||
|
||||
return tecNO_LINE_INSUF_RESERVE;
|
||||
}
|
||||
|
||||
Currency const currency = amount.get<Issue>().currency;
|
||||
STAmount initialBalance(amount.get<Issue>());
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
// clang-format off
|
||||
if (TER const ter = trustCreate(
|
||||
view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
(sleDest->getFlags() & lsfDefaultRipple) == 0,
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
SLE::pointer(), // sponsor
|
||||
journal); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: Failed to create trust line: " << transToken(ter);
|
||||
return ter;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
view.update(sleDest);
|
||||
}
|
||||
|
||||
if (!view.exists(trustLineKey) && issuer != receiver)
|
||||
return tecNO_LINE;
|
||||
|
||||
auto const ter =
|
||||
accountSend(view, sender, receiver, amount, journal, SLE::pointer(), WaiveTransferFee::No);
|
||||
if (ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: Failed to send token: " << transToken(ter);
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
doTransferTokenHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
SLE::ref sleDest,
|
||||
STAmount const& xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const mptID = amount.get<MPTIssue>().getMptID();
|
||||
auto const issuanceKey = keylet::mptokenIssuance(mptID);
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset &&
|
||||
issuer != receiver)
|
||||
{
|
||||
if (xrpBalance < accountReserve(view, sleDest, journal, {.ownerCountDelta = 1}))
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: MPT does not exist. "
|
||||
"Insufficent reserve to create MPT.";
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
if (auto const ter = createMPToken(view, mptID, receiver, SLE::pointer(), 0);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: Failed to create MPT: "
|
||||
<< transToken(ter);
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(view, sleDest, SLE::pointer(), 1, journal);
|
||||
}
|
||||
|
||||
if (issuer != receiver && !view.exists(keylet::mptoken(issuanceKey.key, receiver)))
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: MPT does not exist.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
auto const ter =
|
||||
accountSend(view, sender, receiver, amount, journal, SLE::pointer(), WaiveTransferFee::No);
|
||||
if (ter != tesSUCCESS)
|
||||
{
|
||||
JLOG(journal.trace()) << "doTransferTokenHelper: Failed to send MPT: " << transToken(ter);
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Remove a subscription from both owner directories, release the owner's
|
||||
// reserve, and erase the object. Shared by SubscriptionCancel and the
|
||||
// single-use claim path so the two never diverge.
|
||||
inline TER
|
||||
deleteSubscription(ApplyView& view, SLE::ref sleSub, beast::Journal journal)
|
||||
{
|
||||
AccountID const account{sleSub->getAccountID(sfAccount)};
|
||||
AccountID const dstAcct{sleSub->getAccountID(sfDestination)};
|
||||
|
||||
std::uint64_t const ownerPage{(*sleSub)[sfOwnerNode]};
|
||||
if (!view.dirRemove(keylet::ownerDir(account), ownerPage, sleSub->key(), true))
|
||||
{
|
||||
JLOG(journal.fatal()) << "deleteSubscription: Unable to delete from source.";
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
std::uint64_t const destPage{(*sleSub)[sfDestinationNode]};
|
||||
if (!view.dirRemove(keylet::ownerDir(dstAcct), destPage, sleSub->key(), true))
|
||||
{
|
||||
JLOG(journal.fatal()) << "deleteSubscription: Unable to delete from destination.";
|
||||
return tefBAD_LEDGER;
|
||||
}
|
||||
|
||||
auto const sleSrc = view.peek(keylet::account(account));
|
||||
decreaseOwnerCount(view, sleSrc, SLE::pointer(), 1, journal);
|
||||
view.erase(sleSub);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/TokenIssuanceHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -39,12 +38,6 @@ enum class FreezeHandling { IgnoreFreeze, ZeroIfFrozen };
|
||||
*/
|
||||
enum class AuthHandling { IgnoreAuth, ZeroIfUnauthorized };
|
||||
|
||||
/**
|
||||
* Controls whether the recipient owner-reserve check is enforced when
|
||||
* auto-creating a trustline or MPToken during AMMWithdraw or AMMClawback.
|
||||
*/
|
||||
enum class ReserveHandling : bool { EnforceReserve, IgnoreReserve };
|
||||
|
||||
/**
|
||||
* Controls whether to include the account's full spendable balance
|
||||
*/
|
||||
@@ -301,14 +294,6 @@ accountFunds(
|
||||
AuthHandling authHandling,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
* @param asset The asset being transferred
|
||||
*/
|
||||
[[nodiscard]] Rate
|
||||
transferRate(ReadView const& view, Asset const& asset);
|
||||
|
||||
/**
|
||||
* Returns the transfer fee as Rate based on the type of token
|
||||
* @param view The ledger view
|
||||
@@ -326,12 +311,6 @@ transferRate(ReadView const& view, STAmount const& amount);
|
||||
[[nodiscard]] TER
|
||||
canAddHolding(ReadView const& view, Asset const& asset);
|
||||
|
||||
/**
|
||||
* True if the account already holds this asset (or is the issuer / XRP).
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
holdingExists(ReadView const& view, AccountID const& account, Asset const& asset);
|
||||
|
||||
[[nodiscard]] TER
|
||||
addEmptyHolding(
|
||||
ApplyViewContext ctx,
|
||||
@@ -392,8 +371,7 @@ directSendNoFee(
|
||||
AccountID const& uReceiverID,
|
||||
STAmount const& saAmount,
|
||||
bool bCheckIssuer,
|
||||
beast::Journal j,
|
||||
EnforceSupplyCap enforceSupplyCap = EnforceSupplyCap::Yes);
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Calls static accountSendIOU if saAmount represents Issue.
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* ceil(value * 10^scale), exact.
|
||||
*
|
||||
* The shift is a pure exponent adjustment; the +1 for a fractional
|
||||
* positive value is exact because a fractional Number is < 10^16.
|
||||
* Truncation toward zero already is the ceiling for negative values.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
tokenScaledCeil(Number const& value, std::uint8_t scale);
|
||||
|
||||
/**
|
||||
* floor(value * 10^scale) as integer base units; nullopt if it does not
|
||||
* fit in a non-negative int64.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::int64_t>
|
||||
tokenBaseUnits(Number const& value, std::uint8_t scale);
|
||||
|
||||
/**
|
||||
* True if the issuance violates
|
||||
* ceil(IssuedAmount * 10^TokenScale) + MPT OutstandingAmount > MaximumAmount.
|
||||
* Always false for an uncapped issuance.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
tokenSupplyExceeded(ReadView const& view, SLE::const_ref sleIssuance);
|
||||
|
||||
/**
|
||||
* Controls whether an IOU credit hard-fails when a capped TokenIssuance
|
||||
* would exceed its MaximumAmount. Flow-engine steps pass No: their send
|
||||
* results are advisory (return values ignored, reverse-pass execution can
|
||||
* legitimately overshoot transiently) and the supply-cap invariant checker
|
||||
* gates the final state instead.
|
||||
*/
|
||||
enum class EnforceSupplyCap : bool { No = false, Yes = true };
|
||||
|
||||
/**
|
||||
* Maintain IssuedAmount on the issuer's TokenIssuance for a trust-line
|
||||
* balance move of `amount` from `sender` to `receiver`. Only the amount's
|
||||
* issuer is adjusted: balance moving away from the issuer increases its
|
||||
* net issuance, balance returning decreases it.
|
||||
*
|
||||
* No-op when the amendment is disabled, neither party is the issuer, or no
|
||||
* TokenIssuance exists. Returns tecSUPPLY_EXCEEDED when the cap is enforced
|
||||
* and a capped issuer would exceed MaximumAmount.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
adjustTokenIssuance(
|
||||
ApplyView& view,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
STAmount const& amount,
|
||||
EnforceSupplyCap enforceCap,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Remaining IOU the issuer can issue under the supply cap, floored to the
|
||||
* representable amount; nullopt when there is no TokenIssuance or no cap.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
tokenIssuanceHeadroom(ReadView const& view, Issue const& issue);
|
||||
|
||||
/**
|
||||
* True when the issue's TokenIssuance carries the per-currency lock
|
||||
* (lsfTokenLocked). The per-currency analog of lsfGlobalFreeze.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isTokenLocked(ReadView const& view, Issue const& issue);
|
||||
|
||||
/**
|
||||
* For an MPT issuance bound to a capped TokenIssuance: the additional MPT
|
||||
* base units that can be minted under the shared cap
|
||||
* (MaximumAmount - OutstandingAmount - ceil(IssuedAmount * 10^TokenScale)).
|
||||
* nullopt when unbound or uncapped.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::int64_t>
|
||||
mptBoundHeadroom(ReadView const& view, SLE::const_ref sleMptIssuance);
|
||||
|
||||
/**
|
||||
* Validate binding an MPTokenIssuance to a TokenIssuance: the MPT issuance
|
||||
* exists, is issued by `account`, is not already bound, and its
|
||||
* MaximumAmount/AssetScale equal the TokenIssuance's MaximumAmount/
|
||||
* TokenScale.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
validateTokenBinding(
|
||||
ReadView const& view,
|
||||
MPTID const& mptId,
|
||||
AccountID const& account,
|
||||
std::optional<std::uint64_t> const& maximumAmount,
|
||||
std::uint8_t tokenScale);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,127 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class STTx;
|
||||
/**
|
||||
* Interest the vault's loans have earned since sfLastAccrualTime, capped by
|
||||
* the sfUnearnedInterest budget still left to recognize.
|
||||
*
|
||||
* sfAssetsTotal is only credited when a loan event settles the vault, so
|
||||
* between those events it lags by this amount. Adding it back at read time
|
||||
* recognizes interest continuously as it is earned, without writing to
|
||||
* sfAssetsTotal outside the loan transactions.
|
||||
*
|
||||
* Returns zero when nothing is accruing — no rate, no budget left, or a vault
|
||||
* created before featureLendingProtocolV1_1.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
vaultAccruedInterest(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* Whether a rolling vault is inside a dealing window at this close time.
|
||||
*
|
||||
* A rolling vault deals in [SubscriptionDate + k * DealingInterval,
|
||||
* SubscriptionDate + k * DealingInterval + DealingWindow) for integer k >= 0.
|
||||
* Returns true for any vault that is not rolling, which has no windows to be
|
||||
* outside of, and false before the first window opens.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
inDealingWindow(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* End of the dealing window containing this close time.
|
||||
*
|
||||
* Only meaningful when inDealingWindow is true for a rolling vault; it is the
|
||||
* sfStruckUntil written when a window's price is struck.
|
||||
*/
|
||||
[[nodiscard]] std::uint32_t
|
||||
dealingWindowEnd(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* The price every deal in the current window converts at, in vault asset per
|
||||
* share, or nullopt when no struck price governs this ledger.
|
||||
*
|
||||
* Returns a price only for a rolling vault inside a window whose sfStruckUntil
|
||||
* matches that window's end. Accrual continues underneath it: the dealing price
|
||||
* is frozen for the window, the accounting is not.
|
||||
*/
|
||||
/**
|
||||
* The interest recognition method of a vault.
|
||||
*
|
||||
* Returns sfAccountingMethod where it is present. A vault created before
|
||||
* featureVaultContinuousAccrual carries no such field, so the method is derived
|
||||
* from its schema version instead: CashBasis recognizes interest as it is
|
||||
* collected, and anything older is Legacy, which recognizes a loan's whole-life
|
||||
* interest at origination. Every vault that exists today therefore resolves
|
||||
* without being touched.
|
||||
*/
|
||||
[[nodiscard]] std::uint8_t
|
||||
getAccountingMethod(SLE::const_ref vault);
|
||||
|
||||
[[nodiscard]] std::optional<Number>
|
||||
struckPriceInForce(ReadView const& view, SLE::const_ref vault);
|
||||
|
||||
/**
|
||||
* Strike the price for the current window if it has not been struck yet.
|
||||
*
|
||||
* Called by the first deposit or withdrawal of a window. Does nothing for a
|
||||
* vault that is not rolling, outside a window, or where this window's price is
|
||||
* already struck, so it is safe to call unconditionally.
|
||||
*/
|
||||
void
|
||||
strikeWindowPrice(ApplyView& view, SLE::ref vault, SLE::const_ref issuance);
|
||||
|
||||
/**
|
||||
* Credit interest earned since the last settlement into sfAssetsTotal, draw
|
||||
* it out of the sfUnearnedInterest budget, and stamp the current close time.
|
||||
*
|
||||
* Must be called before adjusting sfAccrualRate, so the elapsed period is
|
||||
* charged at the rate that was in effect over it. Only the ttLOAN_*
|
||||
* transactions may call this: ValidVault requires sfAssetsTotal to move with
|
||||
* the vault balance on deposit and withdraw, which settling would violate.
|
||||
* Pricing does not need it — the conversion helpers add elapsed interest
|
||||
* themselves.
|
||||
*/
|
||||
void
|
||||
accrueVault(ApplyView& view, SLE::ref vault);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of shares to give
|
||||
* depositor when they offer a fixed amount of assets. Note, since shares are
|
||||
* MPT, this number is integral and always truncated in this calculation.
|
||||
*
|
||||
* /**
|
||||
* * From the perspective of a vault, return the number of shares to give
|
||||
* * depositor when they offer a fixed amount of assets. Note, since shares are
|
||||
* * MPT, this number is integral and always truncated in this calculation.
|
||||
* *
|
||||
* * @param vault The vault SLE.
|
||||
* * @param issuance The MPTokenIssuance SLE for the vault's shares.
|
||||
* * @param assets The amount of assets to convert.
|
||||
* *
|
||||
* * @return The number of shares, or nullopt on error.
|
||||
* @param vault The vault SLE.
|
||||
* @param issuance The MPTokenIssuance SLE for the vault's shares.
|
||||
* @param assets The amount of assets to convert.
|
||||
*
|
||||
* @return The number of shares, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& assets);
|
||||
assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of assets to take from
|
||||
@@ -135,37 +41,7 @@ assetsToSharesDeposit(
|
||||
* @return The number of assets, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
* updated total vault assets. This ensures `sfAssetsTotal`, `sfAssetsAvailable`,
|
||||
* and the actual asset transfer change by the exact same representable amount.
|
||||
*
|
||||
* Rounding strategy:
|
||||
* - Debits (withdrawals): Rounds down `|delta|` on the new scale to prevent
|
||||
* paying out more than requested.
|
||||
* - Credits (deposits): Floors the resulting total asset balance and returns the
|
||||
* difference from the current total. This prevents crediting the vault with
|
||||
* more assets than the user deposited.
|
||||
*
|
||||
* Key rules:
|
||||
* - The returned magnitude never exceeds `|delta|`.
|
||||
* - Returns `tecPRECISION_LOSS` if the change is smaller than 1 ULP of the target scale
|
||||
* (prevents share operations when totals cannot change).
|
||||
* - For integer assets (XRP, MPT), rounding is a no-op.
|
||||
*
|
||||
* @param vault The vault ledger entry.
|
||||
* @param delta The requested signed change to sfAssetsTotal.
|
||||
* @return The rounded, positive magnitude, or `tecPRECISION_LOSS` if the
|
||||
* change is below representable precision.
|
||||
*/
|
||||
[[nodiscard]] std::expected<STAmount, TER>
|
||||
clampToAssetsTotalScale(SLE::const_ref vault, STAmount const& delta);
|
||||
sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Controls whether to truncate shares instead of rounding.
|
||||
@@ -182,31 +58,33 @@ enum class TruncateShares : bool { No = false, Yes = true };
|
||||
enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
|
||||
|
||||
/**
|
||||
* Returns the assets backing outstanding shares for a withdrawal:
|
||||
* sfAssetsTotal minus sfLossUnrealized, or sfAssetsTotal alone when the
|
||||
* unrealized loss is waived. Used by assetsToSharesWithdraw and
|
||||
* sharesToAssetsWithdraw as the numerator of the share/asset exchange rate.
|
||||
* Returns the effective total of assets backing outstanding shares for the
|
||||
* purposes of a withdrawal, i.e. sfAssetsTotal, discounted by sfLossUnrealized
|
||||
* unless waived. This is the numerator used by both withdraw conversion
|
||||
* helpers (assetsToSharesWithdraw and sharesToAssetsWithdraw) to compute the
|
||||
* share/asset exchange rate.
|
||||
*
|
||||
* @param view The ledger view, for interest accrued since the last settlement.
|
||||
* @param vault The vault SLE.
|
||||
* @param waive Whether to skip subtracting the unrealized loss.
|
||||
* @param waive Whether to waive (i.e. not subtract) the vault's unrealized
|
||||
* loss.
|
||||
*/
|
||||
[[nodiscard]] Number
|
||||
assetsTotalForWithdrawal(ReadView const& view, SLE::const_ref vault, WaiveUnrealizedLoss waive);
|
||||
assetsTotalForWithdrawal(SLE::const_ref vault, WaiveUnrealizedLoss waive);
|
||||
|
||||
/**
|
||||
* Returns true if debiting `amount` from `total` (the current value of a
|
||||
* vault's sfAssetsTotal or sfAssetsAvailable) would canonicalize to the
|
||||
* same STAmount value. This happens when `amount` is non-zero but too small
|
||||
* to change the stored total at STAmount's precision. Shares would still
|
||||
* move, so the ValidVault invariant would fail after apply; callers use
|
||||
* this to reject the transaction upfront instead.
|
||||
* Returns whether debiting `amount` from `total` — the current value of a
|
||||
* vault's sfAssetsTotal or sfAssetsAvailable field — would canonicalize back
|
||||
* to the exact same STAmount value it started at. This happens when a
|
||||
* genuinely non-zero debit is dust relative to a `total` large enough to
|
||||
* exceed STAmount's significant-digit precision: the shares still move, but
|
||||
* the stored total doesn't change, which otherwise trips the ValidVault
|
||||
* invariant after the fact instead of failing cleanly upfront.
|
||||
*
|
||||
* @param asset The vault's underlying asset, used to canonicalize both
|
||||
* sides the same way the ledger will when the field is stored.
|
||||
* @param asset The vault's underlying asset, used to canonicalize both sides
|
||||
* the same way the ledger will when the field is stored.
|
||||
* @param total The field's current value.
|
||||
* @param amount The amount to debit. Zero always returns false; that case
|
||||
* is rejected separately.
|
||||
* @param amount The amount to debit. A value of zero always returns false;
|
||||
* that case is rejected separately and unconditionally.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount);
|
||||
@@ -228,7 +106,6 @@ debitIsNonZeroDust(Asset const& asset, Number const& total, Number const& amount
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& assets,
|
||||
@@ -250,7 +127,6 @@ assetsToSharesWithdraw(
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& shares,
|
||||
@@ -362,40 +238,4 @@ getVaultPhase(
|
||||
std::optional<std::uint32_t> subscriptionDate,
|
||||
std::optional<std::uint32_t> redemptionDate);
|
||||
|
||||
/**
|
||||
* Controls whether checkVaultDomain reports an expired credential as an
|
||||
* error. A caller that deletes expired credentials later, in doApply, passes
|
||||
* Yes and treats the subject as authorized; a caller with no such cleanup
|
||||
* step must keep the error.
|
||||
*/
|
||||
enum class SuppressExpired : bool { No = false, Yes = true };
|
||||
|
||||
/**
|
||||
* Checks that subject belongs to the permissioned domain governing a vault's
|
||||
* shares.
|
||||
*
|
||||
* The domain is read from the share issuance rather than from the vault. Vault
|
||||
* shares are issued by the vault's pseudo-account, which cannot grant an
|
||||
* authorization explicitly, so domain membership is the only route to being
|
||||
* authorized: a vault with no domain set has no authorized participants at
|
||||
* all, and every subject fails with tecNO_AUTH.
|
||||
*
|
||||
* Which accounts to check, and whether to check at all, is left to the caller.
|
||||
* This says nothing about vault privacy or about the roles of the accounts.
|
||||
*
|
||||
* @param view The ledger view.
|
||||
* @param issuance The MPTokenIssuance SLE for the vault's shares.
|
||||
* @param subject The account whose domain membership is checked.
|
||||
* @param suppressExpired Whether an expired credential counts as authorized.
|
||||
*
|
||||
* @return tesSUCCESS if the subject is a domain member, otherwise the reason
|
||||
* it is not.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkVaultDomain(
|
||||
ReadView const& view,
|
||||
SLE::const_ref issuance,
|
||||
AccountID const& subject,
|
||||
SuppressExpired suppressExpired);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/nodestore/Database.h>
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -54,6 +55,12 @@ public:
|
||||
*/
|
||||
virtual void
|
||||
setRotationInFlight(bool inFlight) = 0;
|
||||
virtual bool
|
||||
isRotationInFlight() const = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual std::uint64_t
|
||||
getAndResetDuplicationCount() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -72,19 +72,28 @@ public:
|
||||
|
||||
void
|
||||
setRotationInFlight(bool inFlight) override;
|
||||
bool
|
||||
isRotationInFlight() const override;
|
||||
|
||||
[[nodiscard]]
|
||||
std::uint64_t
|
||||
getAndResetDuplicationCount() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Backend> writableBackend_;
|
||||
std::shared_ptr<Backend> archiveBackend_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
// True between SHAMapStore starting the cache-freshen phase and the
|
||||
// completion of rotate(). While true, archive hits on ordinary
|
||||
// (duplicate == false) fetches are copied forward into the writable
|
||||
// backend; copyForwardCount_ tallies them per rotation for the
|
||||
// Set to true during the entire SHAMapStore rotation process.
|
||||
// While true, archive hits on ordinary (duplicate == false)
|
||||
// fetches are copied forward into the writable backend.
|
||||
// copyForwardCount_ tallies them per rotation for the
|
||||
// summary line logged at swap.
|
||||
std::atomic<bool> rotationInFlight_{false};
|
||||
std::atomic<std::uint64_t> copyForwardCount_{0};
|
||||
// Duplication count tracks the number of nodes that are directly duplicated because they're in
|
||||
// the target ledger or rescued from a cache.
|
||||
std::atomic<std::uint64_t> duplicationCount_{0};
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
|
||||
|
||||
@@ -29,78 +29,6 @@ constexpr std::uint32_t kAuctionSlotIntervalDuration =
|
||||
constexpr std::uint16_t kVoteMaxSlots = 8;
|
||||
constexpr std::uint32_t kVoteWeightScaleFactor = 100000;
|
||||
|
||||
// Curve type identifiers
|
||||
enum CurveType : std::uint8_t {
|
||||
CtConstantProduct = 0,
|
||||
CtConcentratedLiquidity = 1,
|
||||
CtStableSwap = 2,
|
||||
CtBinned = 3,
|
||||
};
|
||||
|
||||
inline constexpr CurveType protocolCurveTypes[] = {
|
||||
CtConstantProduct,
|
||||
CtConcentratedLiquidity,
|
||||
CtStableSwap,
|
||||
CtBinned,
|
||||
};
|
||||
|
||||
// Fee tier definitions for concentrated liquidity
|
||||
enum FeeTier : std::uint8_t {
|
||||
FtStable = 0, // 1 bp, tick spacing 1
|
||||
FtLow = 1, // 5 bp, tick spacing 10
|
||||
FtMedium = 2, // 30 bp, tick spacing 60
|
||||
FtHigh = 3, // 100 bp, tick spacing 200
|
||||
};
|
||||
|
||||
inline constexpr std::uint16_t feeTierToFee[] = {1, 5, 30, 100};
|
||||
inline constexpr std::int32_t feeTierToTickSpacing[] = {1, 10, 60, 200};
|
||||
inline constexpr std::uint8_t feeTierCount = 4;
|
||||
|
||||
// Tick bounds
|
||||
inline constexpr std::int32_t minTick = -887272;
|
||||
inline constexpr std::int32_t maxTick = 887272;
|
||||
|
||||
// Offset-binary scaling applied wherever a tick is hashed or bit-packed
|
||||
// to keep arithmetic in unsigned domain (avoids signed-division surprises
|
||||
// near zero). Used by:
|
||||
// - keylet::ammTick (offset-encoded into the low 64 keylet bits)
|
||||
// - keylet::ammTickBitmapWord (wordIndex = (tick + offset) >> 8)
|
||||
// - Validthe AMM bitmap-consistency invariant
|
||||
// One constant, one source of fragility — DO NOT duplicate inline.
|
||||
inline constexpr std::uint32_t kTickBitmapOffset =
|
||||
static_cast<std::uint32_t>(-static_cast<std::int64_t>(minTick));
|
||||
|
||||
// StableSwap limits
|
||||
inline constexpr std::uint32_t minAmplification = 1;
|
||||
inline constexpr std::uint32_t maxAmplification = 5000;
|
||||
inline constexpr std::uint32_t maxAmpChangePct = 10;
|
||||
inline constexpr std::uint32_t ampRampDuration = 86400;
|
||||
|
||||
// Binned-curve limits. Bin step in basis points; bin price grows as
|
||||
// (1 + binStep/10000)^binID. Range bound keeps state size finite and
|
||||
// price range close to v3's effective range at default tick spacing.
|
||||
inline constexpr std::int32_t minBinID = -221818;
|
||||
inline constexpr std::int32_t maxBinID = 221818;
|
||||
inline constexpr std::uint16_t validBinSteps[] = {1, 5, 10, 25, 100};
|
||||
inline constexpr std::uint8_t binStepCount = 5;
|
||||
|
||||
// Newton's method convergence
|
||||
inline constexpr int newtonMaxIterations = 256;
|
||||
|
||||
// Concentrated-liquidity per-swap tick-crossing cap. Bounds the
|
||||
// per-swap work done by the curve's iterative tick traversal (each
|
||||
// crossing does one SHAMap lookup for the next initialised tick + one
|
||||
// SLE read). With FtStable's tickSpacing=1, 1000 crossings = ~10%
|
||||
// price range; with FtMedium's tickSpacing=60, 1000 crossings spans
|
||||
// the equivalent of a ~4x price move — both comfortably larger than
|
||||
// any reasonable swap requires. Hitting the cap produces a silent
|
||||
// partial fill: the curve returns the output realized over the first
|
||||
// `maxTickCrossings` boundaries, and the caller infers the cap from
|
||||
// the (smaller-than-requested) result. Uniswap v3 has no protocol
|
||||
// cap (only block gas); we cap here because XRPL has no metered
|
||||
// execution and the cap is the only fairness bound on per-swap work.
|
||||
inline constexpr int maxTickCrossings = 1000;
|
||||
|
||||
class STObject;
|
||||
class STAmount;
|
||||
class Rules;
|
||||
@@ -109,20 +37,13 @@ class Rules;
|
||||
* Calculate Liquidity Provider Token (LPT) Currency.
|
||||
*/
|
||||
Currency
|
||||
ammLPTCurrency(
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
std::uint8_t curveType = CtConstantProduct);
|
||||
ammLPTCurrency(Asset const& asset1, Asset const& asset2);
|
||||
|
||||
/**
|
||||
* Calculate LPT Issue from AMM asset pair.
|
||||
*/
|
||||
Issue
|
||||
ammLPTIssue(
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
AccountID const& ammAccountID,
|
||||
std::uint8_t curveType = CtConstantProduct);
|
||||
ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID);
|
||||
|
||||
/**
|
||||
* Validate the amount.
|
||||
|
||||
@@ -133,7 +133,7 @@ private:
|
||||
using id_hash_type = boost::base_from_member<std::hash<xrpl::MPTID>, 0>;
|
||||
|
||||
public:
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
|
||||
using value_type = std::size_t;
|
||||
using argument_type = xrpl::MPTIssue;
|
||||
@@ -160,7 +160,7 @@ private:
|
||||
mptissue_hasher mMptissueHasher_;
|
||||
|
||||
public:
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
|
||||
value_type
|
||||
operator()(argument_type const& asset) const
|
||||
@@ -227,7 +227,7 @@ struct hash<xrpl::Issue> : std::hash<xrpl::Issue>
|
||||
template <>
|
||||
struct hash<xrpl::MPTIssue> : std::hash<xrpl::MPTIssue>
|
||||
{
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::MPTIssue>;
|
||||
};
|
||||
@@ -235,7 +235,7 @@ struct hash<xrpl::MPTIssue> : std::hash<xrpl::MPTIssue>
|
||||
template <>
|
||||
struct hash<xrpl::Asset> : std::hash<xrpl::Asset>
|
||||
{
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<xrpl::Asset>;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -434,88 +433,4 @@ verifyConvertBackProof(
|
||||
uint64_t amount,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for a BallotCastVote transaction.
|
||||
*
|
||||
* Binds the cast's range proof to this specific transaction, preventing
|
||||
* proof reuse across ballots or accounts.
|
||||
*
|
||||
* @param account The voter's account ID.
|
||||
* @param ballotID The target ballot's ledger index.
|
||||
* @param sequence The transaction sequence number or ticket number.
|
||||
* @return A 256-bit context hash unique to this cast.
|
||||
*/
|
||||
uint256
|
||||
getBallotCastContextHash(AccountID const& account, uint256 const& ballotID, std::uint32_t sequence);
|
||||
|
||||
/**
|
||||
* @brief Generates the context hash for a BallotFinalize transaction.
|
||||
*
|
||||
* Binds each per-option decryption-correctness proof to this specific
|
||||
* finalize transaction.
|
||||
*
|
||||
* @param account The tally authority's account ID.
|
||||
* @param ballotID The target ballot's ledger index.
|
||||
* @param sequence The transaction sequence number or ticket number.
|
||||
* @return A 256-bit context hash unique to this finalize.
|
||||
*/
|
||||
uint256
|
||||
getBallotFinalizeContextHash(
|
||||
AccountID const& account,
|
||||
uint256 const& ballotID,
|
||||
std::uint32_t sequence);
|
||||
|
||||
/**
|
||||
* @brief Verifies an aggregated Bulletproof range proof over ballot option
|
||||
* commitments.
|
||||
*
|
||||
* Proves that every one of the N per-option values committed in
|
||||
* @p commitments lies in the non-negative range, so a vote cannot subtract
|
||||
* weight from a disliked option. Thin wrapper over
|
||||
* mpt_verify_aggregated_bulletproof.
|
||||
*
|
||||
* @param proof The serialized aggregated Bulletproof.
|
||||
* @param commitments One 33-byte Pedersen commitment per option.
|
||||
* @param contextHash The 256-bit context hash binding the proof.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyBallotRangeProof(
|
||||
Slice const& proof,
|
||||
std::vector<Slice> const& commitments,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Verifies the ciphertext-commitment linkage for one ballot option.
|
||||
*
|
||||
* Proves that the option's ElGamal ciphertext(s) encrypt the same value that
|
||||
* its Pedersen commitment commits to, and that every mirror ciphertext (tally,
|
||||
* optional auditor, optional voter) encrypts that same value under shared
|
||||
* randomness. Combined with the aggregated range proof over the commitment,
|
||||
* this pins the tally update to a non-negative value the voter cannot forge —
|
||||
* the verifiable-encryption guarantee a vote needs because it encrypts under
|
||||
* the tally key, which the voter does not own.
|
||||
*
|
||||
* Wraps secp256k1_compact_standard_verify. The balance-linkage terms of that
|
||||
* relation are neutralized with a canonical witness (sk_A = 1, rho_b = 1), so
|
||||
* pk_A = G, PC_b = H and B1 = B2 = G are reconstructed identically here and by
|
||||
* the prover, and only the 192-byte proof is carried on the wire.
|
||||
*
|
||||
* @param pubKeys The n mirror public keys, tally key first (33 bytes each).
|
||||
* @param c1 The shared ElGamal C1 component (33 bytes).
|
||||
* @param c2PerKey The n ElGamal C2 components, one per mirror key (33 bytes).
|
||||
* @param commitment The option's Pedersen commitment PC_m (33 bytes).
|
||||
* @param proof The 192-byte compact sigma linkage proof.
|
||||
* @param contextHash The 256-bit context hash binding the proof.
|
||||
* @return tesSUCCESS if the linkage holds, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyBallotVoteLinkage(
|
||||
std::vector<Slice> const& pubKeys,
|
||||
Slice const& c1,
|
||||
std::vector<Slice> const& c2PerKey,
|
||||
Slice const& commitment,
|
||||
Slice const& proof,
|
||||
uint256 const& contextHash);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/TxSettings.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace xrpl {
|
||||
/**
|
||||
* We have both transaction type emitables and granular type emitables.
|
||||
* Since we will reuse the TransactionFormats to parse the Transaction
|
||||
* Emitables, only the GranularEmitableType is defined here. To prevent
|
||||
* conflicts with TxType, the GranularEmitableType is always set to a value
|
||||
* greater than the maximum value of uint16.
|
||||
*/
|
||||
enum GranularEmitableType : std::uint32_t {
|
||||
#pragma push_macro("EMITABLE")
|
||||
#undef EMITABLE
|
||||
|
||||
#define EMITABLE(type, txType, value) type = value,
|
||||
|
||||
#include <xrpl/protocol/detail/emitable.macro>
|
||||
|
||||
#undef EMITABLE
|
||||
#pragma pop_macro("EMITABLE")
|
||||
};
|
||||
|
||||
class Emitable
|
||||
{
|
||||
private:
|
||||
Emitable();
|
||||
|
||||
std::unordered_map<std::uint16_t, Emittance> emitableTx_;
|
||||
|
||||
std::unordered_map<std::string, GranularEmitableType> granularEmitableMap_;
|
||||
|
||||
std::unordered_map<GranularEmitableType, std::string> granularNameMap_;
|
||||
|
||||
std::unordered_map<GranularEmitableType, TxType> granularTxTypeMap_;
|
||||
|
||||
public:
|
||||
static Emitable const&
|
||||
getInstance();
|
||||
|
||||
Emitable(Emitable const&) = delete;
|
||||
Emitable&
|
||||
operator=(Emitable const&) = delete;
|
||||
|
||||
std::optional<std::string>
|
||||
getEmitableName(std::uint32_t const value) const;
|
||||
|
||||
std::optional<std::uint32_t>
|
||||
getGranularValue(std::string const& name) const;
|
||||
|
||||
std::optional<std::string>
|
||||
getGranularName(GranularEmitableType const& value) const;
|
||||
|
||||
std::optional<TxType>
|
||||
getGranularTxType(GranularEmitableType const& gpType) const;
|
||||
|
||||
bool
|
||||
isEmitable(std::uint32_t const& emitableValue) const;
|
||||
|
||||
// for tx level emitable, emitable value is equal to tx type plus one
|
||||
uint32_t
|
||||
txToEmitableType(TxType const& type) const;
|
||||
|
||||
// tx type value is emitable value minus one
|
||||
TxType
|
||||
emitableToTxType(uint32_t const& value) const;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -10,15 +10,6 @@ namespace xrpl {
|
||||
// This was the reference fee units used in the old fee calculation.
|
||||
inline constexpr std::uint32_t kFeeUnitsDeprecated = 10;
|
||||
|
||||
// Number of micro-drops in one drop.
|
||||
constexpr std::uint32_t microDropsPerDrop{1'000'000};
|
||||
|
||||
/**
|
||||
* Maximum Feature Extension fee settings.
|
||||
*/
|
||||
inline constexpr std::uint32_t kMaxGasLimit{2'000'000};
|
||||
inline constexpr std::uint32_t kMaxBytecodeSizeLimit{200'000};
|
||||
|
||||
/**
|
||||
* Reflects the fee settings for a particular ledger.
|
||||
*
|
||||
@@ -42,21 +33,6 @@ struct Fees
|
||||
*/
|
||||
XRPAmount increment{0};
|
||||
|
||||
/**
|
||||
* @brief Gas limit for Feature Extensions (instructions).
|
||||
*/
|
||||
std::uint32_t gasLimit{0};
|
||||
|
||||
/**
|
||||
* @brief Bytecode size limit for Feature Extensions (bytes).
|
||||
*/
|
||||
std::uint32_t bytecodeSizeLimit{0};
|
||||
|
||||
/**
|
||||
* @brief Price of WASM gas (micro-drops).
|
||||
*/
|
||||
std::uint32_t gasPrice{0};
|
||||
|
||||
explicit Fees() = default;
|
||||
Fees(Fees const&) = default;
|
||||
Fees&
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/TxSettings.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* How an account's firewall treats a transaction of the given type.
|
||||
*
|
||||
* The classification is declared per transaction in transactions.macro. A type
|
||||
* the switch does not name, which means a deprecated one, is allowed.
|
||||
*/
|
||||
[[nodiscard]] FirewallAction
|
||||
firewallAction(TxType txType) noexcept;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -92,26 +92,6 @@ enum class HashPrefix : std::uint32_t {
|
||||
* Batch
|
||||
*/
|
||||
Batch = detail::makeHashPrefix('B', 'C', 'H'),
|
||||
|
||||
/**
|
||||
* inner transaction to sign as the counterparty
|
||||
*/
|
||||
CounterpartyTxSign = detail::makeHashPrefix('C', 'P', 'T'),
|
||||
|
||||
/**
|
||||
* inner transaction to multi-sign as the counterparty
|
||||
*/
|
||||
CounterpartyTxMultiSign = detail::makeHashPrefix('C', 'P', 'M'),
|
||||
|
||||
/**
|
||||
* inner transaction to sign as the sponsor
|
||||
*/
|
||||
SponsorTxSign = detail::makeHashPrefix('S', 'P', 'N'),
|
||||
|
||||
/**
|
||||
* inner transaction to multi-sign as the sponsor
|
||||
*/
|
||||
SponsorTxMultiSign = detail::makeHashPrefix('S', 'P', 'M'),
|
||||
};
|
||||
|
||||
template <class Hasher>
|
||||
|
||||
@@ -15,43 +15,13 @@
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <boost/endian/conversion.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SeqProxy;
|
||||
|
||||
// Structured-key helpers: read/write the low 64 bits of a uint256 keylet
|
||||
// in big-endian byte order. Used by every AMM keylet scheme that
|
||||
// embeds an ordered subkey (tick index, bitmap-word index, bin ID) into
|
||||
// the low 64 bits so SHAMap range walks visit entries in subkey order.
|
||||
//
|
||||
// We type-pun via std::memcpy rather than a reinterpret_cast through
|
||||
// uint64_t* — the latter violates strict aliasing and has no alignment
|
||||
// guarantee on base_uint's underlying byte storage. memcpy of an
|
||||
// 8-byte value compiles to a single load/store on x86_64 / ARM64 under
|
||||
// any optimization level, so the safer idiom is free at runtime.
|
||||
inline void
|
||||
setLow64BE(uint256& key, std::uint64_t value) noexcept
|
||||
{
|
||||
auto const be = boost::endian::native_to_big(value);
|
||||
std::memcpy(key.end() - sizeof(std::uint64_t), &be, sizeof(std::uint64_t));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::uint64_t
|
||||
getLow64BE(uint256 const& key) noexcept
|
||||
{
|
||||
std::uint64_t be;
|
||||
std::memcpy(&be, key.end() - sizeof(std::uint64_t), sizeof(std::uint64_t));
|
||||
return boost::endian::big_to_native(be);
|
||||
}
|
||||
/**
|
||||
* Keylet computation functions.
|
||||
*
|
||||
@@ -136,9 +106,7 @@ book(Book const& b);
|
||||
* BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line
|
||||
* between them.
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
|
||||
|
||||
@@ -147,16 +115,12 @@ trustLine(AccountID const& id, Issue const& issue) noexcept
|
||||
{
|
||||
return trustLine(id, issue.account, issue.currency);
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* An offer from an account
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
offer(AccountID const& id, SeqProxy const& seq) noexcept;
|
||||
|
||||
@@ -165,9 +129,7 @@ offer(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltOFFER, key};
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* The initial directory page for a specific quality
|
||||
@@ -184,9 +146,7 @@ next(Keylet const& k);
|
||||
/**
|
||||
* A ticket belonging to an account
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
ticket(AccountID const& id, SeqProxy const& ticketSeq);
|
||||
|
||||
@@ -195,9 +155,7 @@ ticket(uint256 const& key)
|
||||
{
|
||||
return {ltTICKET, key};
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* A SignerList
|
||||
@@ -211,18 +169,10 @@ signerList(AccountID const& account) noexcept;
|
||||
Keylet
|
||||
sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept;
|
||||
|
||||
/**
|
||||
* An account's beneficiary designation. One per account.
|
||||
*/
|
||||
Keylet
|
||||
beneficiary(AccountID const& account) noexcept;
|
||||
|
||||
/**
|
||||
* A Check
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
check(AccountID const& id, SeqProxy const& seq) noexcept;
|
||||
|
||||
@@ -231,16 +181,12 @@ check(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltCHECK, key};
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* A DepositPreauth
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept;
|
||||
|
||||
@@ -254,9 +200,7 @@ depositPreauth(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltDEPOSIT_PREAUTH, key};
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -275,9 +219,7 @@ ownerDir(AccountID const& id) noexcept;
|
||||
/**
|
||||
* A page in a directory
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
Keylet
|
||||
page(uint256 const& root, std::uint64_t const index = 0) noexcept;
|
||||
|
||||
@@ -287,9 +229,7 @@ page(Keylet const& root, std::uint64_t const index = 0) noexcept
|
||||
XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::keylet::page : valid root type");
|
||||
return page(root.key, index);
|
||||
}
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* An escrow entry
|
||||
@@ -297,12 +237,6 @@ page(Keylet const& root, std::uint64_t const index = 0) noexcept
|
||||
Keylet
|
||||
escrow(AccountID const& src, SeqProxy const& seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
escrow(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltESCROW, key};
|
||||
}
|
||||
|
||||
/**
|
||||
* A PaymentChannel
|
||||
*/
|
||||
@@ -317,9 +251,7 @@ payChannel(AccountID const& src, AccountID const& dst, SeqProxy const& seq) noex
|
||||
* 160-bit AccountID, followed by a 96-bit value that determines which NFT
|
||||
* tokens are candidates for that page.
|
||||
*/
|
||||
/**
|
||||
* @{.
|
||||
*/
|
||||
/** @{ */
|
||||
/**
|
||||
* A keylet for the owner's first possible NFT page.
|
||||
*/
|
||||
@@ -334,9 +266,7 @@ nftokenPageMax(AccountID const& owner);
|
||||
|
||||
Keylet
|
||||
nftokenPage(Keylet const& k, uint256 const& token);
|
||||
/**
|
||||
* @}.
|
||||
*/
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* An offer from an account to buy or sell an NFT
|
||||
@@ -366,7 +296,7 @@ nftSells(uint256 const& id) noexcept;
|
||||
* AMM entry
|
||||
*/
|
||||
Keylet
|
||||
amm(Asset const& issue1, Asset const& issue2, std::uint8_t curveType = 0) noexcept;
|
||||
amm(Asset const& issue1, Asset const& issue2) noexcept;
|
||||
|
||||
Keylet
|
||||
amm(uint256 const& amm) noexcept;
|
||||
@@ -412,15 +342,6 @@ mptokenIssuance(uint256 const& issuanceKey)
|
||||
return {ltMPTOKEN_ISSUANCE, issuanceKey};
|
||||
}
|
||||
|
||||
Keylet
|
||||
tokenIssuance(AccountID const& issuer, Currency const& currency) noexcept;
|
||||
|
||||
inline Keylet
|
||||
tokenIssuance(uint256 const& key)
|
||||
{
|
||||
return {ltTOKEN_ISSUANCE, key};
|
||||
}
|
||||
|
||||
Keylet
|
||||
mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept;
|
||||
|
||||
@@ -445,45 +366,6 @@ vault(uint256 const& vaultKey)
|
||||
Keylet
|
||||
loanBroker(AccountID const& owner, SeqProxy const& seq) noexcept;
|
||||
|
||||
/**
|
||||
* A repurchase agreement, keyed by the seller and the creating sequence.
|
||||
*/
|
||||
Keylet
|
||||
repo(AccountID const& seller, SeqProxy const& seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
repo(uint256 const& repoID)
|
||||
{
|
||||
return {ltREPO, repoID};
|
||||
}
|
||||
|
||||
/**
|
||||
* A firewall, keyed by the account it protects.
|
||||
*/
|
||||
Keylet
|
||||
firewall(AccountID const& account) noexcept;
|
||||
|
||||
inline Keylet
|
||||
firewall(uint256 const& firewallID)
|
||||
{
|
||||
return {ltFIREWALL, firewallID};
|
||||
}
|
||||
|
||||
/**
|
||||
* A withdraw preauthorization, keyed by owner, authorized account and tag.
|
||||
*/
|
||||
Keylet
|
||||
withdrawPreauth(
|
||||
AccountID const& owner,
|
||||
AccountID const& preauthorized,
|
||||
std::uint32_t dtag) noexcept;
|
||||
|
||||
inline Keylet
|
||||
withdrawPreauth(uint256 const& key)
|
||||
{
|
||||
return {ltWITHDRAW_PREAUTH, key};
|
||||
}
|
||||
|
||||
inline Keylet
|
||||
loanBroker(uint256 const& key)
|
||||
{
|
||||
@@ -499,174 +381,11 @@ loan(uint256 const& key)
|
||||
return {ltLOAN, key};
|
||||
}
|
||||
|
||||
Keylet
|
||||
couponSchedule(uint192 const& mptIssuanceID) noexcept;
|
||||
|
||||
inline Keylet
|
||||
couponSchedule(uint256 const& scheduleKey)
|
||||
{
|
||||
return {ltCOUPON_SCHEDULE, scheduleKey};
|
||||
}
|
||||
|
||||
Keylet
|
||||
permissionedDomain(AccountID const& account, SeqProxy const& seq) noexcept;
|
||||
|
||||
Keylet
|
||||
permissionedDomain(uint256 const& domainID) noexcept;
|
||||
|
||||
Keylet
|
||||
contractSource(uint256 const& contractHash) noexcept;
|
||||
|
||||
Keylet
|
||||
contract(uint256 const& contractHash, AccountID const& owner, std::uint32_t seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
contract(uint256 const& contractID)
|
||||
{
|
||||
return {ltCONTRACT, contractID};
|
||||
}
|
||||
|
||||
Keylet
|
||||
contractData(AccountID const& owner, AccountID const& contractAccount) noexcept;
|
||||
|
||||
Keylet
|
||||
passkeyList(AccountID const& account) noexcept;
|
||||
|
||||
/**
|
||||
* A ballot owned by `owner`, keyed by the creating transaction sequence.
|
||||
*/
|
||||
Keylet
|
||||
ballot(AccountID const& owner, std::uint32_t seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
ballot(uint256 const& ballotID)
|
||||
{
|
||||
return {ltBALLOT, ballotID};
|
||||
}
|
||||
|
||||
/**
|
||||
* A voter's cast on the ballot identified by `ballotID`.
|
||||
*/
|
||||
Keylet
|
||||
ballotVote(uint256 const& ballotID, AccountID const& voter) noexcept;
|
||||
|
||||
inline Keylet
|
||||
ballotVote(uint256 const& key)
|
||||
{
|
||||
return {ltBALLOT_VOTE, key};
|
||||
}
|
||||
/**
|
||||
* A concentrated liquidity AMM position.
|
||||
*/
|
||||
Keylet
|
||||
ammPosition(uint256 const& ammID, AccountID const& owner, std::uint32_t seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
ammPosition(uint256 const& key)
|
||||
{
|
||||
return {ltAMM_POSITION, key};
|
||||
}
|
||||
|
||||
/**
|
||||
* A concentrated liquidity AMM tick.
|
||||
* Uses structured (non-hashed) keys for ordered SHAMap traversal.
|
||||
* High 192 bits: pool scope (from ammID hash).
|
||||
* Low 64 bits: encoded tick index (offset binary, big-endian).
|
||||
*/
|
||||
Keylet
|
||||
ammTick(uint256 const& ammID, std::int32_t tickIndex) noexcept;
|
||||
|
||||
inline Keylet
|
||||
ammTick(uint256 const& key)
|
||||
{
|
||||
return {ltAMM_TICK, key};
|
||||
}
|
||||
|
||||
/**
|
||||
* Base key for a CL pool's tick range (low 64 bits zeroed).
|
||||
*/
|
||||
Keylet
|
||||
ammTickBase(uint256 const& ammID) noexcept;
|
||||
|
||||
/**
|
||||
* End key for a CL pool's tick range (low 64 bits all 1s).
|
||||
*/
|
||||
Keylet
|
||||
ammTickEnd(uint256 const& ammID) noexcept;
|
||||
|
||||
/**
|
||||
* A 256-tick presence bitmap window for a CL pool.
|
||||
* Keylet structure mirrors `ammTick`: high 192 bits derive from a pool-scoped
|
||||
* hash, low 64 bits encode the word index (big-endian) so range walks via
|
||||
* SHAMap succ/pred yield the next-higher / next-lower word.
|
||||
*/
|
||||
Keylet
|
||||
ammTickBitmapWord(uint256 const& ammID, std::uint16_t wordIndex) noexcept;
|
||||
|
||||
inline Keylet
|
||||
ammTickBitmapWord(uint256 const& key)
|
||||
{
|
||||
return {ltAMM_TICK_BITMAP, key};
|
||||
}
|
||||
|
||||
/**
|
||||
* Base key for a CL pool's tick-bitmap range (low 64 bits zeroed).
|
||||
*/
|
||||
Keylet
|
||||
ammTickBitmapBase(uint256 const& ammID) noexcept;
|
||||
|
||||
/**
|
||||
* End key for a CL pool's tick-bitmap range (low 64 bits all 1s).
|
||||
*/
|
||||
Keylet
|
||||
ammTickBitmapEnd(uint256 const& ammID) noexcept;
|
||||
|
||||
/**
|
||||
* A single bin within a CtBinned AMM pool. Bins are keyed by signed
|
||||
* bin ID, offset-encoded into the low 64 bits of the keylet so SHAMap
|
||||
* range walks yield consecutive bins in price order.
|
||||
*/
|
||||
Keylet
|
||||
ammBin(uint256 const& ammID, std::int32_t binID) noexcept;
|
||||
|
||||
/**
|
||||
* Lookup a bin SLE by its raw key (used by transactors that have a
|
||||
* stored issuance / bin reference).
|
||||
*/
|
||||
Keylet
|
||||
ammBin(uint256 const& key) noexcept;
|
||||
|
||||
/**
|
||||
* Base / end keys for a binned AMM's bin-SLE range. Bins for the
|
||||
* same AMM are contiguous in SHAMap order (high 192 bits are an
|
||||
* ammID-scoped hash; low 64 bits offset-encode the bin ID), so
|
||||
* `view.succ(bin_at(binID).key, ammBinEnd(ammID).key)` jumps to the
|
||||
* next populated bin in O(log n) regardless of gap size.
|
||||
*/
|
||||
Keylet
|
||||
ammBinBase(uint256 const& ammID) noexcept;
|
||||
|
||||
Keylet
|
||||
ammBinEnd(uint256 const& ammID) noexcept;
|
||||
|
||||
/**
|
||||
* A single LP's holding record in a single bin. Phase 5 will replace
|
||||
* this with a fungible MPT issuance per bin.
|
||||
*/
|
||||
Keylet
|
||||
ammBinHolding(uint256 const& ammID, AccountID const& owner, std::int32_t binID) noexcept;
|
||||
|
||||
Keylet
|
||||
ammBinHolding(uint256 const& key) noexcept;
|
||||
|
||||
Keylet
|
||||
subscription(AccountID const& account, AccountID const& dest, std::uint32_t seq) noexcept;
|
||||
|
||||
inline Keylet
|
||||
subscription(uint256 const& key) noexcept
|
||||
{
|
||||
return {ltSUBSCRIPTION, key};
|
||||
}
|
||||
} // namespace keylet
|
||||
|
||||
// Everything below is deprecated and should be removed in favor of keylets:
|
||||
|
||||
@@ -8,8 +8,6 @@ namespace xrpl {
|
||||
enum class KeyType {
|
||||
Secp256k1 = 0,
|
||||
Ed25519 = 1,
|
||||
Dilithium = 2,
|
||||
P256 = 3,
|
||||
};
|
||||
|
||||
inline std::optional<KeyType>
|
||||
@@ -21,12 +19,6 @@ keyTypeFromString(std::string const& s)
|
||||
if (s == "ed25519")
|
||||
return KeyType::Ed25519;
|
||||
|
||||
if (s == "dilithium")
|
||||
return KeyType::Dilithium;
|
||||
|
||||
if (s == "p256")
|
||||
return KeyType::P256;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -39,12 +31,6 @@ to_string(KeyType type)
|
||||
if (type == KeyType::Ed25519)
|
||||
return "ed25519";
|
||||
|
||||
if (type == KeyType::Dilithium)
|
||||
return "dilithium";
|
||||
|
||||
if (type == KeyType::P256)
|
||||
return "p256";
|
||||
|
||||
return "INVALID";
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,15 @@ enum LedgerEntryType : std::uint16_t {
|
||||
*/
|
||||
ltNICKNAME [[deprecated("This object type is not supported and should not be used.")]] = 0x006e,
|
||||
|
||||
/**
|
||||
* A legacy, deprecated type.
|
||||
*
|
||||
* @deprecated **This object type is not supported and should not be used.**
|
||||
* Support for this type of object was never implemented.
|
||||
* No objects of this type were ever created.
|
||||
*/
|
||||
ltCONTRACT [[deprecated("This object type is not supported and should not be used.")]] = 0x0063,
|
||||
|
||||
/**
|
||||
* A legacy, deprecated type.
|
||||
*
|
||||
@@ -145,8 +154,7 @@ enum LedgerEntryType : std::uint16_t {
|
||||
LEDGER_OBJECT(Offer, \
|
||||
LSF_FLAG(lsfPassive, 0x00010000) \
|
||||
LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \
|
||||
LSF_FLAG(lsfHybrid, 0x00040000) /* True, offer is hybrid. */ \
|
||||
LSF_FLAG(lsfAllOrNone, 0x00080000)) /* True, offer is all-or-none. */ \
|
||||
LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \
|
||||
\
|
||||
LEDGER_OBJECT(RippleState, \
|
||||
LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \
|
||||
@@ -180,8 +188,7 @@ enum LedgerEntryType : std::uint16_t {
|
||||
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
|
||||
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
|
||||
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080) \
|
||||
LSF_FLAG(lsfMPTCouponSchedule, 0x00000100)) \
|
||||
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPToken, \
|
||||
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
|
||||
@@ -201,20 +208,7 @@ enum LedgerEntryType : std::uint16_t {
|
||||
\
|
||||
LEDGER_OBJECT(Sponsorship, \
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) \
|
||||
\
|
||||
LEDGER_OBJECT(TokenIssuance, \
|
||||
LSF_FLAG(lsfTokenLocked, 0x00000001) /* True, per-currency global freeze */ \
|
||||
LSF_FLAG(lsfTokenCannotLock, 0x00000002) /* True, issuer renounced the per-currency lock */ \
|
||||
LSF_FLAG(lsfTokenWrapped, 0x00000004) /* True, owned by a pseudo-account (blackholed issuer) */ \
|
||||
LSF_FLAG(lsfTokenVerifiedSupply, 0x00000008)) /* True, IssuedAmount reflects verified legacy supply */ \
|
||||
\
|
||||
LEDGER_OBJECT(Ballot, \
|
||||
LSF_FLAG(lsfBallotFinalized, 0x00000001) /* True, results have been published */ \
|
||||
LSF_FLAG(lsfVoterRecoverable, 0x00000002)) /* True, casts carry a voter self-recovery vector */ \
|
||||
\
|
||||
LEDGER_OBJECT(Subscription, \
|
||||
LSF_FLAG(lsfSingleUse, 0x00010000)) /* True, delete on first successful claim */
|
||||
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000))
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -175,17 +174,4 @@ mulRatio(MPTAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundU
|
||||
return MPTAmount(r.convert_to<MPTAmount::value_type>());
|
||||
}
|
||||
|
||||
inline std::optional<MPTAmount>
|
||||
tryMulRatio(MPTAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
|
||||
{
|
||||
try
|
||||
{
|
||||
return mulRatio(amt, num, den, roundUp);
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace std {
|
||||
template <>
|
||||
struct hash<xrpl::MPTID> : xrpl::MPTID::hasher
|
||||
{
|
||||
hash() = default;
|
||||
explicit hash() = default;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
19
include/xrpl/protocol/NFTSyntheticSerializer.h
Normal file
19
include/xrpl/protocol/NFTSyntheticSerializer.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::rpc {
|
||||
|
||||
/**
|
||||
* Adds common synthetic fields to transaction-related JSON responses
|
||||
*/
|
||||
/** @{ */
|
||||
void
|
||||
insertNFTSyntheticInJson(json::Value&, std::shared_ptr<STTx const> const&, TxMeta const&);
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -1,20 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
inline void
|
||||
@@ -25,70 +15,4 @@ serializePayChanAuthorization(Serializer& msg, uint256 const& key, XRPAmount con
|
||||
msg.add64(amt.drops());
|
||||
}
|
||||
|
||||
inline void
|
||||
serializePayChanAuthorization(
|
||||
Serializer& msg,
|
||||
uint256 const& key,
|
||||
IOUAmount const& amt,
|
||||
Currency const& cur,
|
||||
AccountID const& iss)
|
||||
{
|
||||
msg.add32(HashPrefix::PaymentChannelClaim);
|
||||
msg.addBitString(key);
|
||||
if (amt == beast::kZero)
|
||||
{
|
||||
msg.add64(STAmount::kIssuedCurrency);
|
||||
}
|
||||
else if (amt.signum() == -1)
|
||||
{ // 512 = not native; the sign is encoded by omitting the 256 flag, so
|
||||
// the mantissa must be serialized as its absolute value
|
||||
msg.add64(
|
||||
static_cast<std::uint64_t>(-amt.mantissa()) |
|
||||
(static_cast<std::uint64_t>(amt.exponent() + 512 + 97) << (64 - 10)));
|
||||
}
|
||||
else
|
||||
{ // 256 = positive
|
||||
msg.add64(
|
||||
amt.mantissa() |
|
||||
(static_cast<std::uint64_t>(amt.exponent() + 512 + 256 + 97) << (64 - 10)));
|
||||
}
|
||||
msg.addBitString(cur);
|
||||
msg.addBitString(iss);
|
||||
}
|
||||
|
||||
inline void
|
||||
serializePayChanAuthorization(
|
||||
Serializer& msg,
|
||||
uint256 const& key,
|
||||
MPTAmount const& amt,
|
||||
MPTID const& mptID,
|
||||
AccountID const& iss)
|
||||
{
|
||||
msg.add32(HashPrefix::PaymentChannelClaim);
|
||||
msg.addBitString(key);
|
||||
msg.add64(amt.value());
|
||||
msg.addBitString(mptID);
|
||||
msg.addBitString(iss);
|
||||
}
|
||||
|
||||
inline void
|
||||
serializePayChanAuthorization(Serializer& msg, uint256 const& key, STAmount const& amt)
|
||||
{
|
||||
if (amt.native())
|
||||
{
|
||||
serializePayChanAuthorization(msg, key, amt.xrp());
|
||||
}
|
||||
else if (amt.holds<Issue>())
|
||||
{
|
||||
serializePayChanAuthorization(
|
||||
msg, key, amt.iou(), amt.get<Issue>().currency, amt.get<Issue>().account);
|
||||
}
|
||||
else if (amt.holds<MPTIssue>())
|
||||
{
|
||||
auto const& mpt = amt.get<MPTIssue>();
|
||||
auto const& mptID = mpt.getMptID();
|
||||
serializePayChanAuthorization(msg, key, amt.mpt(), mptID, amt.getIssuer());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -141,13 +140,6 @@ tenthBipsOfValue(T value, TenthBips<TBips> bips)
|
||||
return value * bips.value() / kTenthBipsPerUnity.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* The longest inactivity period a beneficiary designation may require, ten
|
||||
* years in seconds. Long enough for the intended use and short enough that the
|
||||
* value still means something.
|
||||
*/
|
||||
constexpr std::uint32_t kMaxBeneficiaryTimeLock = 10 * 365 * 24 * 60 * 60;
|
||||
|
||||
namespace lending {
|
||||
/**
|
||||
* The maximum management fee rate allowed by a loan broker in 1/10 bips.
|
||||
@@ -309,29 +301,11 @@ static_assert(Number::kMaxRep >= kMaxMpTokenAmount);
|
||||
*/
|
||||
constexpr std::size_t kMaxDataPayloadLength = 256;
|
||||
|
||||
/**
|
||||
* The maximum length of a structured-data Schema
|
||||
*/
|
||||
constexpr std::size_t kMaxSchemaLength = 256;
|
||||
|
||||
/**
|
||||
* Vault withdrawal policies
|
||||
*/
|
||||
constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1;
|
||||
|
||||
/**
|
||||
* Vault interest recognition methods.
|
||||
*
|
||||
* Legacy recognizes a loan's whole-life interest at origination and is the
|
||||
* implicit method for vaults created before featureLendingProtocolV1_1. Cash
|
||||
* recognizes interest as it is collected; accrual recognizes it continuously
|
||||
* as it is earned. Fixed at VaultCreate — changing it would reprice every
|
||||
* outstanding share in a single step.
|
||||
*/
|
||||
constexpr std::uint8_t kVaultAccountingLegacy = 0;
|
||||
constexpr std::uint8_t kVaultAccountingCash = 1;
|
||||
constexpr std::uint8_t kVaultAccountingAccrual = 2;
|
||||
|
||||
/**
|
||||
* Default IOU scale factor for a Vault
|
||||
*/
|
||||
@@ -341,13 +315,7 @@ constexpr std::uint8_t kVaultDefaultIouScale = 6;
|
||||
* 1 IOU can be always converted to shares.
|
||||
* 10^19 > maxMPTokenAmount (2^64-1) > 10^18
|
||||
*/
|
||||
constexpr std::uint8_t kVaultMaximumIouScale =
|
||||
18; /** Largest deposit or redemption fee a vault may charge, in 1/10 bips.
|
||||
|
||||
Matches kMaxTransferFee: half of what is moved is the most any fee may
|
||||
retain.
|
||||
*/
|
||||
constexpr std::uint32_t kMaxVaultFee = 50'000;
|
||||
constexpr std::uint8_t kVaultMaximumIouScale = 18;
|
||||
|
||||
/**
|
||||
* Vault ledger-entry schema versions. Assigned to newly created
|
||||
@@ -361,16 +329,12 @@ enum class VaultVersion : uint8_t {
|
||||
};
|
||||
|
||||
/**
|
||||
* Vault kind. Distinguishes closed-ended and rolling vaults from the default
|
||||
* open-ended kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
|
||||
*
|
||||
* A rolling vault deals in a window that reopens every sfDealingInterval
|
||||
* seconds and stays open for sfDealingWindow of them.
|
||||
* Vault kind. Distinguishes closed-ended vaults from the default open-ended
|
||||
* kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
|
||||
*/
|
||||
enum class VaultKind : std::uint8_t {
|
||||
OpenEnded = 0,
|
||||
ClosedEnded = 1,
|
||||
Rolling = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -384,24 +348,13 @@ enum class VaultPhase : std::uint8_t {
|
||||
Redemption,
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimum gap between a closed-ended loan's final scheduled payment and the
|
||||
* vault's RedemptionDate. LoanSet rejects a schedule whose final payment is
|
||||
* fewer than this many seconds before RedemptionDate.
|
||||
*/
|
||||
constexpr std::uint32_t kLoanRedemptionBuffer = std::chrono::seconds{60}.count();
|
||||
|
||||
/**
|
||||
* Bounds on the length of a closed-ended vault's Investment phase
|
||||
* (RedemptionDate - SubscriptionDate). At vault creation the gap must satisfy
|
||||
* kMinInvestmentPeriod <= gap < kMaxInvestmentPeriod.
|
||||
*
|
||||
* 180s is enough to originate a loan that uses the minimum payment interval
|
||||
* and kLoanRedemptionBuffer after StartDate, which is strictly after
|
||||
* SubscriptionDate. The interval and buffer need not be equal; only their
|
||||
* sum plus one second must fit in this floor.
|
||||
*/
|
||||
constexpr std::uint32_t kMinInvestmentPeriod = std::chrono::seconds{180}.count();
|
||||
constexpr std::uint32_t kMinInvestmentPeriod =
|
||||
std::chrono::seconds{std::chrono::minutes{1}}.count();
|
||||
// This is 946708560 seconds which 30 x 365.2425 days (the average length of a Gregorian year).
|
||||
constexpr std::uint32_t kMaxInvestmentPeriod = std::chrono::seconds{std::chrono::years{30}}.count();
|
||||
|
||||
@@ -411,30 +364,6 @@ constexpr std::uint32_t kMaxInvestmentPeriod = std::chrono::seconds{std::chrono:
|
||||
*/
|
||||
constexpr std::uint8_t kMaxAssetCheckDepth = 5;
|
||||
|
||||
/**
|
||||
* Maximum length of a Data field in Escrow object that can be updated by WASM code.
|
||||
*/
|
||||
constexpr std::size_t kMaxWasmDataLength = 1 * 1024; // 1KB
|
||||
|
||||
/**
|
||||
* Maximum amount of data transfer across hostfunction<->wasm border.
|
||||
*/
|
||||
constexpr std::size_t kWasmTransferLimit = 1 << 20; // 1MB
|
||||
|
||||
/**
|
||||
* Maximum MaximumAmount of a TokenIssuance, in base units (10^15). Bounded
|
||||
* to what Number/STAmount arithmetic represents exactly, so the supply-cap
|
||||
* comparison can never be affected by mantissa rounding.
|
||||
*/
|
||||
constexpr std::uint64_t kMaxTokenIssuanceAmount = 1'000'000'000'000'000ull;
|
||||
static_assert(kMaxTokenIssuanceAmount <= kMaxMpTokenAmount);
|
||||
|
||||
/**
|
||||
* Maximum TokenScale of a TokenIssuance: 10^scale must stay in exact int64
|
||||
* range. 10^19 > 2^63-1 > 10^18
|
||||
*/
|
||||
constexpr std::uint8_t kMaxTokenIssuanceScale = 18;
|
||||
|
||||
/**
|
||||
* A ledger index.
|
||||
*/
|
||||
@@ -604,11 +533,6 @@ constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_
|
||||
*/
|
||||
constexpr std::uint32_t kConfidentialFeeMultiplier = 9;
|
||||
|
||||
/**
|
||||
* Maximum value a confidential MPT key epoch may reach.
|
||||
*/
|
||||
constexpr std::uint32_t kMaxKeyEpoch = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
/**
|
||||
* Compressed EC point prefix for even y-coordinate
|
||||
*/
|
||||
|
||||
@@ -36,12 +36,10 @@ namespace xrpl {
|
||||
* information needed to determine the cryptosystem
|
||||
* parameters used is stored inside the key.
|
||||
*
|
||||
* As of this writing three systems are supported:
|
||||
* As of this writing two systems are supported:
|
||||
*
|
||||
* secp256k1
|
||||
* ed25519
|
||||
* dilithium
|
||||
* p256
|
||||
*
|
||||
* secp256k1 public keys consist of a 33 byte
|
||||
* compressed public key, with the lead byte equal
|
||||
@@ -50,19 +48,14 @@ namespace xrpl {
|
||||
* The ed25519 public keys consist of a 1 byte
|
||||
* prefix constant 0xED, followed by 32 bytes of
|
||||
* public key data.
|
||||
*
|
||||
* The dilithium public keys will have their own specific format.
|
||||
*/
|
||||
class PublicKey
|
||||
{
|
||||
protected:
|
||||
// Minimum / standard public key size (secp256k1, ed25519).
|
||||
// All the constructed public keys are valid, non-empty and contain 33
|
||||
// bytes of data.
|
||||
static constexpr std::size_t kSize = 33;
|
||||
// Buffer sized for the largest supported key (dilithium = 1312 bytes;
|
||||
// uncompressed p256 = 65 bytes). Actual length is tracked in size_.
|
||||
static constexpr std::size_t kMaxSize = 1312;
|
||||
std::uint8_t buf_[kMaxSize]{};
|
||||
std::size_t size_ = 0;
|
||||
std::uint8_t buf_[kSize]{}; // should be large enough
|
||||
|
||||
public:
|
||||
using const_iterator = std::uint8_t const*;
|
||||
@@ -88,10 +81,10 @@ public:
|
||||
return buf_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t
|
||||
size() const noexcept
|
||||
static std::size_t
|
||||
size() noexcept
|
||||
{
|
||||
return size_;
|
||||
return kSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator
|
||||
@@ -109,19 +102,19 @@ public:
|
||||
[[nodiscard]] const_iterator
|
||||
end() const noexcept
|
||||
{
|
||||
return buf_ + size_;
|
||||
return buf_ + kSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator
|
||||
cend() const noexcept
|
||||
{
|
||||
return buf_ + size_;
|
||||
return buf_ + kSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] Slice
|
||||
slice() const noexcept
|
||||
{
|
||||
return {buf_, size_};
|
||||
return {buf_, kSize};
|
||||
}
|
||||
|
||||
operator Slice() const noexcept
|
||||
@@ -139,7 +132,7 @@ operator<<(std::ostream& os, PublicKey const& pk);
|
||||
inline bool
|
||||
operator==(PublicKey const& lhs, PublicKey const& rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
|
||||
return std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
||||
@@ -34,9 +34,6 @@ class STNumber;
|
||||
class STXChainBridge;
|
||||
class STVector256;
|
||||
class STCurrency;
|
||||
class STData;
|
||||
class STDataType;
|
||||
class STJson;
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming)
|
||||
#pragma push_macro("XMACRO")
|
||||
@@ -76,9 +73,6 @@ class STJson;
|
||||
STYPE(STI_ISSUE, 24) \
|
||||
STYPE(STI_XCHAIN_BRIDGE, 25) \
|
||||
STYPE(STI_CURRENCY, 26) \
|
||||
STYPE(STI_DATA, 27) \
|
||||
STYPE(STI_DATATYPE, 28) \
|
||||
STYPE(STI_JSON, 29) \
|
||||
\
|
||||
/* high-level types */ \
|
||||
/* cannot be serialized inside other types */ \
|
||||
@@ -365,9 +359,6 @@ using SF_NUMBER = TypedField<STNumber>;
|
||||
using SF_VL = TypedField<STBlob>;
|
||||
using SF_VECTOR256 = TypedField<STVector256>;
|
||||
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
|
||||
using SF_DATA = TypedField<STData>;
|
||||
using SF_DATATYPE = TypedField<STDataType>;
|
||||
using SF_JSON = TypedField<STJson>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAccount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STBase.h>
|
||||
#include <xrpl/protocol/STBitString.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/detail/STVar.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class STData final : public STBase
|
||||
{
|
||||
private:
|
||||
using data_type = detail::STVar;
|
||||
std::uint16_t inner_type_;
|
||||
data_type data_;
|
||||
bool default_{true};
|
||||
|
||||
public:
|
||||
using value_type = STData; // Although not directly holding a single value
|
||||
|
||||
STData(SField const& n);
|
||||
STData(SField const& n, unsigned char);
|
||||
STData(SField const& n, std::uint16_t);
|
||||
STData(SField const& n, std::uint32_t);
|
||||
STData(SField const& n, std::uint64_t);
|
||||
STData(SField const& n, uint128 const&);
|
||||
STData(SField const& n, uint160 const&);
|
||||
STData(SField const& n, uint192 const&);
|
||||
STData(SField const& n, uint256 const&);
|
||||
STData(SField const& n, Blob const&);
|
||||
STData(SField const& n, Slice const&);
|
||||
STData(SField const& n, AccountID const&);
|
||||
STData(SField const& n, STAmount const&);
|
||||
STData(SField const& n, STIssue const&);
|
||||
STData(SField const& n, STCurrency const&);
|
||||
STData(SField const& n, STNumber const&);
|
||||
|
||||
STData(SerialIter& sit, SField const& name);
|
||||
|
||||
std::size_t
|
||||
size() const;
|
||||
|
||||
SerializedTypeID
|
||||
getSType() const override;
|
||||
|
||||
std::string
|
||||
getInnerTypeString() const;
|
||||
|
||||
std::string
|
||||
getText() const override;
|
||||
|
||||
json::Value getJson(JsonOptions) const override;
|
||||
|
||||
void
|
||||
add(Serializer& s) const override;
|
||||
|
||||
bool
|
||||
isEquivalent(STBase const& t) const override;
|
||||
|
||||
bool
|
||||
isDefault() const override;
|
||||
|
||||
SerializedTypeID
|
||||
getInnerSType() const noexcept;
|
||||
|
||||
STBase*
|
||||
makeFieldPresent();
|
||||
|
||||
void
|
||||
setFieldU8(unsigned char);
|
||||
void
|
||||
setFieldU16(std::uint16_t);
|
||||
void
|
||||
setFieldU32(std::uint32_t);
|
||||
void
|
||||
setFieldU64(std::uint64_t);
|
||||
void
|
||||
setFieldH128(uint128 const&);
|
||||
void
|
||||
setFieldH160(uint160 const&);
|
||||
void
|
||||
setFieldH192(uint192 const&);
|
||||
void
|
||||
setFieldH256(uint256 const&);
|
||||
void
|
||||
setFieldVL(Blob const&);
|
||||
void
|
||||
setFieldVL(Slice const&);
|
||||
void
|
||||
setAccountID(AccountID const&);
|
||||
void
|
||||
setFieldAmount(STAmount const&);
|
||||
void
|
||||
setIssue(STIssue const&);
|
||||
void
|
||||
setCurrency(STCurrency const&);
|
||||
void
|
||||
setFieldNumber(STNumber const&);
|
||||
|
||||
unsigned char
|
||||
getFieldU8() const;
|
||||
std::uint16_t
|
||||
getFieldU16() const;
|
||||
std::uint32_t
|
||||
getFieldU32() const;
|
||||
std::uint64_t
|
||||
getFieldU64() const;
|
||||
uint128
|
||||
getFieldH128() const;
|
||||
uint160
|
||||
getFieldH160() const;
|
||||
uint192
|
||||
getFieldH192() const;
|
||||
uint256
|
||||
getFieldH256() const;
|
||||
Blob
|
||||
getFieldVL() const;
|
||||
AccountID
|
||||
getAccountID() const;
|
||||
STAmount const&
|
||||
getFieldAmount() const;
|
||||
STIssue
|
||||
getFieldIssue() const;
|
||||
STCurrency
|
||||
getFieldCurrency() const;
|
||||
STNumber
|
||||
getFieldNumber() const;
|
||||
|
||||
private:
|
||||
STBase*
|
||||
copy(std::size_t n, void* buf) const override;
|
||||
STBase*
|
||||
move(std::size_t n, void* buf) override;
|
||||
|
||||
friend class detail::STVar;
|
||||
|
||||
// Implementation for getting (most) fields that return by value.
|
||||
//
|
||||
// The remove_cv and remove_reference are necessitated by the STBitString
|
||||
// types. Their value() returns by const ref. We return those types
|
||||
// by value.
|
||||
template <
|
||||
typename T,
|
||||
typename V = typename std::remove_cv<
|
||||
typename std::remove_reference<decltype(std::declval<T>().value())>::type>::type>
|
||||
V
|
||||
getFieldByValue() const;
|
||||
|
||||
// Implementations for getting (most) fields that return by const reference.
|
||||
//
|
||||
// If an absent optional field is deserialized we don't have anything
|
||||
// obvious to return. So we insist on having the call provide an
|
||||
// 'empty' value we return in that circumstance.
|
||||
template <typename T, typename V>
|
||||
V const&
|
||||
getFieldByConstRef(V const& empty) const;
|
||||
|
||||
// Implementation for setting most fields with a setValue() method.
|
||||
template <typename T, typename V>
|
||||
void
|
||||
setFieldUsingSetValue(V value);
|
||||
|
||||
// Implementation for setting fields using assignment
|
||||
template <typename T>
|
||||
void
|
||||
setFieldUsingAssignment(T const& value);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline SerializedTypeID
|
||||
STData::getInnerSType() const noexcept
|
||||
{
|
||||
return static_cast<SerializedTypeID>(inner_type_);
|
||||
}
|
||||
|
||||
template <typename T, typename V>
|
||||
V
|
||||
STData::getFieldByValue() const
|
||||
{
|
||||
STBase const* rf = &data_.get();
|
||||
|
||||
// if (!rf)
|
||||
// throwFieldNotFound(getFName());
|
||||
|
||||
SerializedTypeID const id = rf->getSType();
|
||||
|
||||
if (id == STI_NOTPRESENT)
|
||||
Throw<std::runtime_error>("Field not present");
|
||||
|
||||
T const* cf = dynamic_cast<T const*>(rf);
|
||||
|
||||
if (!cf)
|
||||
Throw<std::runtime_error>("Wrong field type");
|
||||
|
||||
return cf->value();
|
||||
}
|
||||
|
||||
// Implementations for getting (most) fields that return by const reference.
|
||||
//
|
||||
// If an absent optional field is deserialized we don't have anything
|
||||
// obvious to return. So we insist on having the call provide an
|
||||
// 'empty' value we return in that circumstance.
|
||||
template <typename T, typename V>
|
||||
V const&
|
||||
STData::getFieldByConstRef(V const& empty) const
|
||||
{
|
||||
STBase const* rf = &data_.get();
|
||||
|
||||
// if (!rf)
|
||||
// throwFieldNotFound(field);
|
||||
|
||||
SerializedTypeID const id = rf->getSType();
|
||||
|
||||
if (id == STI_NOTPRESENT)
|
||||
return empty; // optional field not present
|
||||
|
||||
T const* cf = dynamic_cast<T const*>(rf);
|
||||
|
||||
if (!cf)
|
||||
Throw<std::runtime_error>("Wrong field type");
|
||||
|
||||
return *cf;
|
||||
}
|
||||
|
||||
// Implementation for setting most fields with a setValue() method.
|
||||
template <typename T, typename V>
|
||||
void
|
||||
STData::setFieldUsingSetValue(V value)
|
||||
{
|
||||
static_assert(!std::is_lvalue_reference<V>::value, "");
|
||||
|
||||
STBase* rf = &data_.get();
|
||||
|
||||
// if (!rf)
|
||||
// throwFieldNotFound(field);
|
||||
|
||||
if (rf->getSType() == STI_NOTPRESENT)
|
||||
rf = makeFieldPresent();
|
||||
|
||||
T* cf = dynamic_cast<T*>(rf);
|
||||
|
||||
if (!cf)
|
||||
Throw<std::runtime_error>("Wrong field type");
|
||||
|
||||
cf->setValue(std::move(value));
|
||||
}
|
||||
|
||||
// Implementation for setting fields using assignment
|
||||
template <typename T>
|
||||
void
|
||||
STData::setFieldUsingAssignment(T const& value)
|
||||
{
|
||||
STBase* rf = &data_.get();
|
||||
|
||||
// if (!rf)
|
||||
// throwFieldNotFound(field);
|
||||
|
||||
// if (rf->getSType() == STI_NOTPRESENT)
|
||||
// rf = makeFieldPresent(field);
|
||||
|
||||
T* cf = dynamic_cast<T*>(rf);
|
||||
|
||||
if (!cf)
|
||||
Throw<std::runtime_error>("Wrong field type");
|
||||
|
||||
(*cf) = value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Creation
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
STData
|
||||
dataFromJson(SField const& field, json::Value const& value);
|
||||
|
||||
} // namespace xrpl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user