mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-24 22:20:19 +00:00
Compare commits
38 Commits
alphanet
...
g-ripple/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e221267af | ||
|
|
1d7669f528 | ||
|
|
30640a626f | ||
|
|
5a5ad8673a | ||
|
|
8f4e9c25d8 | ||
|
|
d8870162eb | ||
|
|
8b1a2282c3 | ||
|
|
0219c01b33 | ||
|
|
00606bec1a | ||
|
|
2bc17c3e73 | ||
|
|
0229c294a9 | ||
|
|
184fe173fb | ||
|
|
16f7b263fd | ||
|
|
f6b51f0b8b | ||
|
|
07b36871c7 | ||
|
|
638ae08eeb | ||
|
|
24be48ee84 | ||
|
|
e20f448a71 | ||
|
|
f6c80fef68 | ||
|
|
a0c12420b5 | ||
|
|
1245254cac | ||
|
|
b60636169a | ||
|
|
d6022fbc4d | ||
|
|
295b74da1c | ||
|
|
faa2bf583f | ||
|
|
da260fa42b | ||
|
|
53788b193d | ||
|
|
227f1b4d9c | ||
|
|
26b66957ec | ||
|
|
54e62a621f | ||
|
|
53628b70c0 | ||
|
|
6fec2c11bf | ||
|
|
04108a030c | ||
|
|
551a19b10d | ||
|
|
b0a940a383 | ||
|
|
e302e4eeed | ||
|
|
1a4a40ebb8 | ||
|
|
7f55dd390c |
@@ -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
|
||||
@@ -67,15 +64,13 @@ words:
|
||||
- blindings
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- bthomee
|
||||
- Britto
|
||||
- Btrfs
|
||||
- Buildx
|
||||
- canonicality
|
||||
- cdylib
|
||||
- canonicalised
|
||||
- canonicality
|
||||
- cctools
|
||||
- CGNAT
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
@@ -120,7 +115,7 @@ words:
|
||||
- dsymutil
|
||||
- dxrpl
|
||||
- elgamal
|
||||
- emittance
|
||||
- enabled
|
||||
- enablerepo
|
||||
- endmacro
|
||||
- envrc
|
||||
@@ -147,6 +142,7 @@ words:
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
- Injectivity
|
||||
- insuf
|
||||
- insuff
|
||||
- invasively
|
||||
@@ -183,11 +179,11 @@ words:
|
||||
- mathbunnyru
|
||||
- mcmodel
|
||||
- MEMORYSTATUSEX
|
||||
- MPTAMM
|
||||
- MPTDEX
|
||||
- Merkle
|
||||
- misprediction
|
||||
- mispricing
|
||||
- missingok
|
||||
- MPTAMM
|
||||
- mptbalance
|
||||
- MPTDEX
|
||||
- mptflags
|
||||
@@ -262,8 +258,8 @@ words:
|
||||
- replayer
|
||||
- repodata
|
||||
- repomd
|
||||
- rerandomization
|
||||
- rerandomize
|
||||
- rerandomization
|
||||
- rerandomized
|
||||
- rerandomizes
|
||||
- rerere
|
||||
@@ -286,8 +282,8 @@ words:
|
||||
- rustup
|
||||
- sahyadri
|
||||
- Satoshi
|
||||
- Schnorr
|
||||
- scons
|
||||
- Schnorr
|
||||
- secp
|
||||
- sendq
|
||||
- seqit
|
||||
@@ -312,9 +308,7 @@ words:
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
- stjson
|
||||
- stnum
|
||||
- stnumber
|
||||
- stobj
|
||||
- stobject
|
||||
- stpath
|
||||
@@ -341,8 +335,8 @@ words:
|
||||
- txn
|
||||
- txns
|
||||
- txs
|
||||
- UBSAN
|
||||
- ubsan
|
||||
- UBSAN
|
||||
- ufdio
|
||||
- umant
|
||||
- unacquired
|
||||
@@ -354,9 +348,7 @@ words:
|
||||
- unfindable
|
||||
- unflatten
|
||||
- unfund
|
||||
- ungated
|
||||
- unimpair
|
||||
- unmetered
|
||||
- unroutable
|
||||
- unscalable
|
||||
- unserviced
|
||||
@@ -382,11 +374,9 @@ words:
|
||||
- writeme
|
||||
- wsrch
|
||||
- wthread
|
||||
- Xahau
|
||||
- xbridge
|
||||
- xchain
|
||||
- xcrun
|
||||
- xfloat
|
||||
- ximinez
|
||||
- XMACRO
|
||||
- xored
|
||||
@@ -397,3 +387,5 @@ words:
|
||||
- xxhash
|
||||
- xxhasher
|
||||
- zstdio
|
||||
- CGNAT
|
||||
- ungated
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1,2 +0,0 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
@@ -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
|
||||
@@ -83,7 +82,6 @@ test.app > xrpl.tx
|
||||
test.basics > test.jtx
|
||||
test.basics > xrpl.basics
|
||||
test.basics > xrpl.core
|
||||
test.basics > xrpld.rpc
|
||||
test.basics > xrpl.json
|
||||
test.basics > xrpl.protocol
|
||||
test.beast > xrpl.basics
|
||||
@@ -114,6 +112,7 @@ test.jtx > xrpl.config
|
||||
test.jtx > xrpl.core
|
||||
test.jtx > xrpld.app
|
||||
test.jtx > xrpld.core
|
||||
test.jtx > xrpld.overlay
|
||||
test.jtx > xrpld.rpc
|
||||
test.jtx > xrpl.json
|
||||
test.jtx > xrpl.ledger
|
||||
@@ -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
|
||||
@@ -288,10 +286,10 @@ xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.config
|
||||
xrpld.perflog > xrpl.core
|
||||
xrpld.perflog > xrpld.app
|
||||
xrpld.perflog > xrpld.rpc
|
||||
xrpld.perflog > xrpl.json
|
||||
xrpld.perflog > xrpl.nodestore
|
||||
xrpld.perflog > xrpl.protocol
|
||||
xrpld.perflog > xrpl.server
|
||||
xrpld.rpc > xrpl.basics
|
||||
xrpld.rpc > xrpl.config
|
||||
xrpld.rpc > xrpl.core
|
||||
|
||||
50
.github/scripts/strategy-matrix/generate.py
vendored
50
.github/scripts/strategy-matrix/generate.py
vendored
@@ -15,6 +15,14 @@ _BASE_CMAKE_ARGS = [
|
||||
"-Drust=ON",
|
||||
]
|
||||
|
||||
# The package formats a config can be packaged as, each with its own
|
||||
# install-test job in reusable-package.yml.
|
||||
PACKAGE_TYPES = ("deb", "rpm")
|
||||
|
||||
# The package name a variant suffixes, as build_pkg.py's BASE_NAME spells it:
|
||||
# the two have to agree, or the artifact globs miss what was built.
|
||||
BASE_NAME = "xrpld"
|
||||
|
||||
# Maps sanitizer names (as used in cmake) to short config-name suffixes.
|
||||
_SANITIZER_SUFFIX: dict[str, str] = {
|
||||
"address": "asan",
|
||||
@@ -62,10 +70,20 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
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
|
||||
type: str # 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
|
||||
# A flavour of the package, named xrpld-<variant>, for a config whose
|
||||
# binaries are not the plain release build. A variant needs no counterpart
|
||||
# in the other format.
|
||||
variant: str = ""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.type in PACKAGE_TYPES, (
|
||||
f"unsupported package type {self.type!r}: "
|
||||
f"use one of {', '.join(PACKAGE_TYPES)}."
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -81,8 +99,6 @@ class LinuxConfig:
|
||||
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):
|
||||
@@ -170,7 +186,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
|
||||
@@ -181,6 +196,8 @@ class PackagingEntry:
|
||||
validator_keys_artifact_name: str
|
||||
image: str
|
||||
package_type: str # "deb" or "rpm"; drives the format-specific steps
|
||||
package_variant: str # passed to build_pkg.py --variant; empty for xrpld
|
||||
package_name: str # the name it builds under, which the artifact globs use
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -236,7 +253,6 @@ def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||
architecture=arch_info,
|
||||
sanitizers=sanitizer,
|
||||
compiler=compiler,
|
||||
force_supported=cfg.force_supported,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -271,12 +287,32 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
||||
validator_keys_artifact_name=f"validator-keys-{name}",
|
||||
image=cfg.package.image,
|
||||
package_type=cfg.package.type,
|
||||
package_variant=cfg.package.variant,
|
||||
package_name=(
|
||||
f"{BASE_NAME}-{cfg.package.variant}"
|
||||
if cfg.package.variant
|
||||
else BASE_NAME
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def package_names_by_type(entries: list[PackagingEntry]) -> dict[str, list[str]]:
|
||||
"""The names of the packages in 'entries', keyed by format.
|
||||
|
||||
Derived from the packaging matrix rather than listed again, so the packages
|
||||
the install-test jobs look for are the packages that were built.
|
||||
"""
|
||||
return {
|
||||
package_type: sorted(
|
||||
{e.package_name for e in entries if e.package_type == package_type}
|
||||
)
|
||||
for package_type in PACKAGE_TYPES
|
||||
}
|
||||
|
||||
|
||||
def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]:
|
||||
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
|
||||
|
||||
@@ -345,6 +381,10 @@ if __name__ == "__main__":
|
||||
|
||||
if args.packaging:
|
||||
matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json"))
|
||||
# One list per format, so each install-test job installs the packages its
|
||||
# own format produced.
|
||||
for package_type, names in package_names_by_type(matrix).items():
|
||||
print(f"{package_type}_package_names={json.dumps(names)}")
|
||||
else:
|
||||
if args.config in ("linux", None):
|
||||
matrix += expand_linux_matrix(
|
||||
|
||||
31
.github/scripts/strategy-matrix/linux.json
vendored
31
.github/scripts/strategy-matrix/linux.json
vendored
@@ -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,21 +62,9 @@
|
||||
"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"],
|
||||
@@ -85,8 +76,22 @@
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10"
|
||||
}
|
||||
},
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"suffix": "assert",
|
||||
"extra_cmake_args": "-Dvalidator_keys=ON -Dassert=ON",
|
||||
"package": {
|
||||
"type": "deb",
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-49cdc10",
|
||||
"variant": "assert"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"rhel": [
|
||||
{
|
||||
"compiler": ["gcc"],
|
||||
|
||||
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 }}
|
||||
7
.github/workflows/on-pr.yml
vendored
7
.github/workflows/on-pr.yml
vendored
@@ -85,6 +85,7 @@ jobs:
|
||||
.github/workflows/reusable-build-test.yml
|
||||
.github/workflows/reusable-check-autogen.yml
|
||||
.github/workflows/reusable-clang-tidy.yml
|
||||
.github/workflows/reusable-package-test-install.yml
|
||||
.github/workflows/reusable-package.yml
|
||||
.github/workflows/reusable-rust.yml
|
||||
.github/workflows/reusable-strategy-matrix.yml
|
||||
@@ -189,6 +190,12 @@ jobs:
|
||||
# matrix (i.e. not yet labeled "Ready to merge" or "Full CI build").
|
||||
if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }}
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
# A pull request builds packages to prove they still build, and publishes
|
||||
# nothing. Stated rather than left to the input's default, so that changing
|
||||
# that default cannot start publishing from pull requests. No secrets are
|
||||
# passed either, which is the second reason a publish here cannot succeed.
|
||||
publish: false
|
||||
|
||||
upload-recipe:
|
||||
needs:
|
||||
|
||||
1
.github/workflows/on-trigger.yml
vendored
1
.github/workflows/on-trigger.yml
vendored
@@ -23,6 +23,7 @@ on:
|
||||
- ".github/workflows/reusable-build-test.yml"
|
||||
- ".github/workflows/reusable-check-autogen.yml"
|
||||
- ".github/workflows/reusable-clang-tidy.yml"
|
||||
- ".github/workflows/reusable-package-test-install.yml"
|
||||
- ".github/workflows/reusable-package.yml"
|
||||
- ".github/workflows/reusable-rust.yml"
|
||||
- ".github/workflows/reusable-strategy-matrix.yml"
|
||||
|
||||
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,20 +128,6 @@ 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
|
||||
with:
|
||||
@@ -458,7 +439,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
|
||||
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 }}
|
||||
|
||||
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# Install one package format on every distro family it targets, one job per
|
||||
# package name and image, and run the binaries there. Called once per format by
|
||||
# reusable-package.yml, which owns the names and the image lists.
|
||||
name: Install packages
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package_type:
|
||||
description: 'The package format to install ("deb" or "rpm").'
|
||||
required: true
|
||||
type: string
|
||||
package_names:
|
||||
description: "JSON array of package names built for this format."
|
||||
required: true
|
||||
type: string
|
||||
images:
|
||||
description: "JSON array of container images to install in."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
PACKAGE_DIR: packages
|
||||
|
||||
jobs:
|
||||
install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package_name: ${{ fromJson(inputs.package_names) }}
|
||||
image: ${{ fromJson(inputs.images) }}
|
||||
name: "${{ matrix.package_name }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# Every package lands in one directory; the step below picks its own,
|
||||
# which keeps this 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_NAME: ${{ matrix.package_name }}
|
||||
PACKAGE_TYPE: ${{ inputs.package_type }}
|
||||
run: |
|
||||
# The version follows the name, separated by '_' in a DEB and '-' in an
|
||||
# RPM. Requiring a digit after it is what keeps 'xrpld' from picking up
|
||||
# another package, such as 'xrpld-assert'.
|
||||
pattern="${PACKAGE_NAME}[_-][0-9]*.${PACKAGE_TYPE}"
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "${pattern}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no ${pattern} 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: ${{ inputs.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: ${{ inputs.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
|
||||
185
.github/workflows/reusable-package.yml
vendored
185
.github/workflows/reusable-package.yml
vendored
@@ -3,8 +3,10 @@
|
||||
#
|
||||
# - '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
|
||||
# - 'test-install-deb' and 'test-install-rpm' call
|
||||
# reusable-package-test-install.yml to install what was built on a range of
|
||||
# distros and run 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'
|
||||
#
|
||||
@@ -23,7 +25,7 @@ on:
|
||||
description: "The base URL of the Nexus instance hosting the deb and rpm repositories."
|
||||
required: false
|
||||
type: string
|
||||
default: https://packages.xrplf.org
|
||||
default: https://packages-upload.xrplf.org
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
@@ -49,6 +51,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
deb_package_names: ${{ steps.generate.outputs.deb_package_names }}
|
||||
rpm_package_names: ${{ steps.generate.outputs.rpm_package_names }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -107,6 +111,7 @@ jobs:
|
||||
- name: Build package
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
PACKAGE_VARIANT: ${{ matrix.package_variant }}
|
||||
PKG_RELEASE: ${{ steps.release_info.outputs.pkg_release }}
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: |
|
||||
@@ -114,6 +119,7 @@ jobs:
|
||||
--package-type "${PACKAGE_TYPE}" \
|
||||
--build-dir "${BUILD_DIR}" \
|
||||
--pkg-release "${PKG_RELEASE}" \
|
||||
--variant "${PACKAGE_VARIANT}" \
|
||||
--channel "${CHANNEL}"
|
||||
|
||||
# Before the upload, so the artifact, the tested package and the published
|
||||
@@ -125,14 +131,17 @@ jobs:
|
||||
run: ./package/sign_rpm.py --package-dir "${BUILD_DIR}"
|
||||
|
||||
# Split from the debug symbols, which are an order of magnitude larger, so
|
||||
# that test-install downloads only what it installs.
|
||||
# that test-install downloads only what it installs. In the globs below the
|
||||
# version follows the name, separated by '_' in a DEB and '-' in an RPM. A
|
||||
# version starts with a digit and a longer name does not, so that one digit
|
||||
# is what tells 'xrpld-3.4.1-...' from 'xrpld-assert-3.4.1-...'.
|
||||
- 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/${{ matrix.package_name }}_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload debug symbol artifact
|
||||
@@ -140,133 +149,59 @@ jobs:
|
||||
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
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-debuginfo-[0-9]*.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
|
||||
# One call per format, so a variant packaged for one format is installed for
|
||||
# that format alone. The images are every distro family that format targets,
|
||||
# oldest release first, so both ends of the dependency range the packages
|
||||
# declare are exercised.
|
||||
test-install-deb:
|
||||
needs: [generate-matrix, package]
|
||||
name: install deb
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: deb
|
||||
package_names: ${{ needs.generate-matrix.outputs.deb_package_names }}
|
||||
images: |
|
||||
[
|
||||
"debian:11",
|
||||
"debian:12",
|
||||
"debian:13",
|
||||
"ubuntu:20.04",
|
||||
"ubuntu:22.04",
|
||||
"ubuntu:24.04",
|
||||
"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
|
||||
test-install-rpm:
|
||||
needs: [generate-matrix, package]
|
||||
name: install rpm
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: rpm
|
||||
package_names: ${{ needs.generate-matrix.outputs.rpm_package_names }}
|
||||
images: |
|
||||
[
|
||||
"almalinux:9",
|
||||
"almalinux:10",
|
||||
"rockylinux/rockylinux:9",
|
||||
"rockylinux/rockylinux:10",
|
||||
"registry.access.redhat.com/ubi9/ubi",
|
||||
"registry.access.redhat.com/ubi10/ubi"
|
||||
]
|
||||
|
||||
publish:
|
||||
needs: [generate-matrix, package, test-install]
|
||||
needs: [generate-matrix, package, test-install-deb, test-install-rpm]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}"
|
||||
# The name says which of the two this is, because the job runs either way:
|
||||
# with publish false it passes --dry-run and uploads nothing, and a job
|
||||
# called "publish ..." succeeding on a pull request reads like a release.
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
|
||||
2
.github/workflows/reusable-rust.yml
vendored
2
.github/workflows/reusable-rust.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
|
||||
@@ -53,6 +53,11 @@ repos:
|
||||
entry: ./bin/pre-commit/check_doxygen_style.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
- id: fix-gtest-names
|
||||
name: "fix gtest names: CamelCase suite, snake_case test case"
|
||||
entry: ./bin/pre-commit/fix_gtest_names.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8
|
||||
|
||||
@@ -28,10 +28,6 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
|
||||
|
||||
### 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
|
||||
@@ -45,6 +41,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
|
||||
- `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)
|
||||
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
|
||||
|
||||
## XRP Ledger server version 3.3.0
|
||||
|
||||
@@ -66,11 +63,6 @@ This release contains bug fixes only and no API changes.
|
||||
|
||||
### Additions in 3.2.0
|
||||
|
||||
- `ledger_entry`, `account_objects`: Added the `Ballot` and `BallotVote` ledger entry types introduced by the `ConfidentialVoting` amendment. `ledger_entry` accepts a `ballot` request object (`owner` + `seq`) and a `ballot_vote` request object (`ballot_id` + `account`), or a hex object ID for either. `account_objects` returns these entries and accepts them as `type` filters.
|
||||
|
||||
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
|
||||
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
|
||||
|
||||
- `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681))
|
||||
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
||||
- `TRANSACTION_FORMATS`: Describes the fields and their optionality for each transaction type, including common fields shared across all transactions.
|
||||
|
||||
21
BUILD.md
21
BUILD.md
@@ -304,6 +304,7 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
||||
| ---------------- | ------------- | ----------------------------------------------------------------------------- |
|
||||
| `assert` | OFF | Force enabling assertions. |
|
||||
| `coverage` | OFF | Prepare the coverage report. |
|
||||
| `jemalloc` | ON | Use jemalloc instead of the system allocator; disables glibc malloc trimming. |
|
||||
| `rust` | OFF | Build the Rust crates and the C++ code that depends on them. |
|
||||
| `tests` | OFF | Build tests. |
|
||||
| `unity` | OFF | Configure a unity build. |
|
||||
@@ -317,6 +318,26 @@ memory) since they concatenate sources into fewer translation units. Non-unity
|
||||
builds may be faster for incremental builds, and can be helpful for detecting
|
||||
`#include` omissions.
|
||||
|
||||
### Memory allocator
|
||||
|
||||
Conan and CMake use jemalloc by default. In jemalloc builds, `mallocTrim` does
|
||||
not call glibc's `malloc_trim` or collect trim metrics; jemalloc manages its own
|
||||
arenas and memory reclamation.
|
||||
|
||||
To use the system allocator instead (GNU/glibc malloc with the existing
|
||||
`malloc_trim(0)` behavior on Linux/glibc), add `--options '&:jemalloc=False'`
|
||||
to each `conan install` command and `-Djemalloc=OFF` to the CMake configure
|
||||
command. Keep the Conan and CMake options consistent. To switch an existing
|
||||
system-allocator build to jemalloc, rerun Conan with
|
||||
`--options '&:jemalloc=True'` and configure CMake with `-Djemalloc=ON`, since
|
||||
existing build directories may cache the old option value.
|
||||
|
||||
The `sanitizers` Conan profile disables jemalloc when sanitizer instrumentation
|
||||
is enabled so that sanitizers can intercept the system allocator. CMake also
|
||||
defaults to the system allocator when `SANITIZERS` is set; use `-Djemalloc=OFF`
|
||||
if reusing a build directory previously configured with jemalloc. Platforms
|
||||
other than Linux/glibc do not support malloc trimming, regardless of this option.
|
||||
|
||||
### Rust crates
|
||||
|
||||
The Rust crates in `crates/` are only part of the build when `rust` is ON. With
|
||||
|
||||
@@ -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(
|
||||
|
||||
144
bin/pre-commit/fix_gtest_names.py
Executable file
144
bin/pre-commit/fix_gtest_names.py
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Rewrites gtest names to the required style in this project: the suite name is
|
||||
CamelCase, the test-case name is snake_case.
|
||||
|
||||
TEST(SuiteName, test_case_name)
|
||||
|
||||
The gtest `DISABLED_` prefix is kept verbatim on either name.
|
||||
|
||||
Both conversions fold acronyms the way a reader expects:
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`, not
|
||||
`set_and_reset_account_txn_i_d`.
|
||||
|
||||
The first argument of `TEST_F`, `TEST_P`, `TYPED_TEST` and `TYPED_TEST_P` is a
|
||||
fixture class rather than a free identifier, so rewriting it here would leave
|
||||
the class it names behind. Those are reported for a human to rename (clang-tidy
|
||||
checks the class declaration itself, via readability-identifier-naming).
|
||||
|
||||
Usage: ./bin/pre-commit/fix_gtest_names.py <file1> <file2> ...
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
# A test-case definition, `MACRO(SuiteOrFixture, TestName)`, anchored at the
|
||||
# start of a line so that commented-out definitions and project macros that
|
||||
# merely look similar (`TEST_EXPECT(...)`) are left alone. The `\s*` between
|
||||
# arguments allows for a definition clang-format wrapped over several lines.
|
||||
PATTERN = re.compile(
|
||||
r"(?P<head>^[ \t]*(?P<macro>TYPED_TEST_P|TYPED_TEST|TEST_F|TEST_P|TEST)\s*\(\s*)"
|
||||
r"(?P<suite>\w+)(?P<mid>\s*,\s*)(?P<name>\w+)(?P<tail>\s*\))",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
# The macros whose first argument names a fixture class, not a free identifier.
|
||||
FIXTURE_MACROS = ("TEST_F", "TEST_P", "TYPED_TEST", "TYPED_TEST_P")
|
||||
|
||||
DISABLED = "DISABLED_"
|
||||
|
||||
ACRONYM_BOUNDARY = re.compile(r"([A-Z]+)([A-Z][a-z])")
|
||||
WORD_BOUNDARY = re.compile(r"([a-z\d])([A-Z])")
|
||||
|
||||
|
||||
def _split_disabled(name: str) -> tuple[str, str]:
|
||||
"""Splits off gtest's `DISABLED_` prefix, which is kept verbatim."""
|
||||
if name.startswith(DISABLED):
|
||||
return DISABLED, name[len(DISABLED) :]
|
||||
return "", name
|
||||
|
||||
|
||||
def snake_case(name: str) -> str:
|
||||
"""Returns the name in snake_case, leaving acronyms whole.
|
||||
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`,
|
||||
`parseStatRSSkB` -> `parse_stat_rs_sk_b`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
core = ACRONYM_BOUNDARY.sub(r"\1_\2", core)
|
||||
return prefix + WORD_BOUNDARY.sub(r"\1_\2", core).lower()
|
||||
|
||||
|
||||
def camel_case(name: str) -> str:
|
||||
"""Returns the name in CamelCase, capitalizing each underscored word.
|
||||
|
||||
Only the letters that have to change are touched, so acronyms survive: a
|
||||
conversion that went via snake_case would turn `SHAMapTest` into
|
||||
`ShaMapTest`, whereas here it is already CamelCase and stays put.
|
||||
`json_value` -> `JsonValue`, `parseStatRSSkB` -> `ParseStatRSSkB`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
return prefix + "".join(w[:1].upper() + w[1:] for w in core.split("_") if w)
|
||||
|
||||
|
||||
def _corrected(match: re.Match) -> tuple[str, str]:
|
||||
"""Returns the suite and test-case names this definition should end up with."""
|
||||
suite = match["suite"]
|
||||
return (
|
||||
suite if match["macro"] in FIXTURE_MACROS else camel_case(suite),
|
||||
snake_case(match["name"]),
|
||||
)
|
||||
|
||||
|
||||
def fix_source(text: str) -> tuple[str, list[str]]:
|
||||
"""Returns the corrected text and one `line: message` report per bad name."""
|
||||
# gtest joins the suite and test names into one class name, so two test
|
||||
# cases whose joined names agree cannot coexist: `TEST(a, b_c)` and
|
||||
# `TEST(a_b, c)` both define `a_b_c_Test`. A rename that would introduce
|
||||
# such a clash is reported for a human instead of applied.
|
||||
joined = Counter("_".join(_corrected(m)) for m in PATTERN.finditer(text))
|
||||
reports = []
|
||||
|
||||
def rewrite(match: re.Match) -> str:
|
||||
suite, name = match["suite"], match["name"]
|
||||
new_suite, new_name = _corrected(match)
|
||||
line = text.count("\n", 0, match.start()) + 1
|
||||
|
||||
if match["macro"] in FIXTURE_MACROS and camel_case(suite) != suite:
|
||||
reports.append(
|
||||
f"{line}: fixture '{suite}' is not CamelCase: rename the class "
|
||||
f"to '{camel_case(suite)}' by hand"
|
||||
)
|
||||
if (new_suite, new_name) == (suite, name):
|
||||
return match[0]
|
||||
if joined[f"{new_suite}_{new_name}"] > 1:
|
||||
reports.append(
|
||||
f"{line}: cannot rename '{suite}, {name}' to '{new_suite}, "
|
||||
f"{new_name}': another test case already generates that name"
|
||||
)
|
||||
return match[0]
|
||||
if new_suite != suite:
|
||||
reports.append(f"{line}: renamed suite '{suite}' to '{new_suite}'")
|
||||
if new_name != name:
|
||||
reports.append(f"{line}: renamed test case '{name}' to '{new_name}'")
|
||||
return match["head"] + new_suite + match["mid"] + new_name + match["tail"]
|
||||
|
||||
return PATTERN.sub(rewrite, text), reports
|
||||
|
||||
|
||||
def fix_names(path: Path) -> bool:
|
||||
"""Corrects one file's gtest names, reporting each on stdout."""
|
||||
original = path.read_text(encoding="utf-8")
|
||||
fixed, reports = fix_source(original)
|
||||
for report in reports:
|
||||
print(f"{path}:{report}")
|
||||
if fixed != original:
|
||||
path.write_text(fixed, encoding="utf-8")
|
||||
return not reports
|
||||
|
||||
|
||||
def main() -> int:
|
||||
files = [Path(f) for f in sys.argv[1:]]
|
||||
success = True
|
||||
|
||||
for path in files:
|
||||
success &= fix_names(path)
|
||||
|
||||
return 0 if success else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for fix_gtest_names.py.
|
||||
|
||||
Run directly (no test framework needed):
|
||||
./bin/pre-commit/test_fix_gtest_names.py
|
||||
or under pytest:
|
||||
pytest bin/pre-commit/test_fix_gtest_names.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from fix_gtest_names import camel_case, fix_source, snake_case
|
||||
|
||||
|
||||
def dedent(text: str) -> str:
|
||||
"""Removes a fixture's common indentation and its leading newline.
|
||||
|
||||
Lets fixtures be written as indented triple-quoted here-docs while keeping
|
||||
honest 1-based line numbers.
|
||||
"""
|
||||
return textwrap.dedent(text).lstrip("\n")
|
||||
|
||||
|
||||
def fixed(text: str) -> str:
|
||||
return fix_source(dedent(text))[0]
|
||||
|
||||
|
||||
def reports(text: str) -> list[str]:
|
||||
return fix_source(dedent(text))[1]
|
||||
|
||||
|
||||
# --- conversion --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_snake_case_conversion() -> None:
|
||||
assert snake_case("BadInputs") == "bad_inputs"
|
||||
assert snake_case("mulDiv") == "mul_div"
|
||||
assert snake_case("already_snake") == "already_snake"
|
||||
assert snake_case("base64") == "base64"
|
||||
|
||||
|
||||
def test_snake_case_keeps_acronyms_whole() -> None:
|
||||
assert snake_case("SetAndResetAccountTxnID") == "set_and_reset_account_txn_id"
|
||||
assert snake_case("XRPToIOU") == "xrp_to_iou"
|
||||
assert snake_case("STAmountMath") == "st_amount_math"
|
||||
|
||||
|
||||
def test_camel_case_conversion() -> None:
|
||||
assert camel_case("json_value") == "JsonValue"
|
||||
assert camel_case("mulDiv") == "MulDiv"
|
||||
assert camel_case("scope") == "Scope"
|
||||
assert camel_case("base64") == "Base64"
|
||||
|
||||
|
||||
def test_camel_case_leaves_acronyms_alone() -> None:
|
||||
# A snake_case round-trip would give `ShaMapTest` / `ParseStatmRsSkB` here.
|
||||
assert camel_case("SHAMapTest") == "SHAMapTest"
|
||||
assert camel_case("parseStatmRSSkB") == "ParseStatmRSSkB"
|
||||
assert camel_case("XRPAmount") == "XRPAmount"
|
||||
assert camel_case("CSPRNG") == "CSPRNG"
|
||||
|
||||
|
||||
def test_disabled_prefix_preserved() -> None:
|
||||
assert snake_case("DISABLED_FooBar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_foo_bar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_") == "DISABLED_"
|
||||
assert camel_case("DISABLED_foo_bar") == "DISABLED_FooBar"
|
||||
assert camel_case("DISABLED_") == "DISABLED_"
|
||||
|
||||
|
||||
# --- what counts as a test definition ---------------------------------------
|
||||
|
||||
|
||||
def test_all_macros_recognized() -> None:
|
||||
code = """
|
||||
TEST(Suite, oneName)
|
||||
TEST_F(Fixture, twoName)
|
||||
TEST_P(Fixture, threeName)
|
||||
TYPED_TEST(Fixture, fourName)
|
||||
TYPED_TEST_P(Fixture, fiveName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(Suite, one_name)
|
||||
TEST_F(Fixture, two_name)
|
||||
TEST_P(Fixture, three_name)
|
||||
TYPED_TEST(Fixture, four_name)
|
||||
TYPED_TEST_P(Fixture, five_name)
|
||||
""")
|
||||
|
||||
|
||||
def test_conforming_definitions_untouched() -> None:
|
||||
code = """
|
||||
TEST(AccountSet, bad_inputs)
|
||||
TEST_F(MutexMakeTest, default_constructor)
|
||||
TEST(SHAMap, DISABLED_slow_path)
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_lookalikes_ignored() -> None:
|
||||
code = """
|
||||
// TEST(Suite, notATest)
|
||||
TEST_EXPECT(someCall())
|
||||
TEST_EXPECTS(amount == value, amount.getText())
|
||||
INSTANTIATE_TEST_SUITE_P(Prefix, Fixture, testValues());
|
||||
auto x = TEST(Suite, notATest);
|
||||
TYPED_TEST_SUITE(Fixture, MyTypes);
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_indented_and_wrapped_definitions() -> None:
|
||||
code = """
|
||||
namespace ripple {
|
||||
TEST(Suite, indentedName)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrappedName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
namespace ripple {
|
||||
TEST(Suite, indented_name)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrapped_name)
|
||||
""")
|
||||
|
||||
|
||||
# --- rewriting --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_only_the_two_names_are_rewritten() -> None:
|
||||
code = """
|
||||
TEST(mulDiv, mulDiv)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(MulDiv, mul_div)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
def test_suite_name_camel_cased() -> None:
|
||||
code = """
|
||||
TEST(json_value, limits)
|
||||
TEST(scope, ScopeExit)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: renamed suite 'json_value' to 'JsonValue'",
|
||||
"2: renamed suite 'scope' to 'Scope'",
|
||||
"2: renamed test case 'ScopeExit' to 'scope_exit'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(JsonValue, limits)
|
||||
TEST(Scope, scope_exit)
|
||||
""")
|
||||
|
||||
|
||||
def test_fixture_reported_but_not_renamed() -> None:
|
||||
# The first argument names a class, so only a human (or clang-tidy) can
|
||||
# rename it; the test-case name is still fixed.
|
||||
code = """
|
||||
TEST_F(my_fixture, someTest)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: fixture 'my_fixture' is not CamelCase: rename the class to "
|
||||
"'MyFixture' by hand",
|
||||
"1: renamed test case 'someTest' to 'some_test'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST_F(my_fixture, some_test)
|
||||
""")
|
||||
|
||||
|
||||
def test_reports_carry_line_numbers() -> None:
|
||||
code = """
|
||||
#include <foo.h>
|
||||
|
||||
TEST(Suite, firstName)
|
||||
|
||||
TEST(Suite, secondName)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"3: renamed test case 'firstName' to 'first_name'",
|
||||
"5: renamed test case 'secondName' to 'second_name'",
|
||||
]
|
||||
|
||||
|
||||
# --- collisions -------------------------------------------------------------
|
||||
|
||||
|
||||
def test_collision_reported_and_not_applied() -> None:
|
||||
# Both would define `Suite_mul_div_Test`.
|
||||
code = """
|
||||
TEST(Suite, mulDiv)
|
||||
TEST(Suite, mul_div)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: cannot rename 'Suite, mulDiv' to 'Suite, mul_div': another test "
|
||||
"case already generates that name"
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_collision_between_converging_suites() -> None:
|
||||
# Both suites camel-case to `SuiteA`, so both would define
|
||||
# `SuiteA_one_test_Test`.
|
||||
code = """
|
||||
TEST(SuiteA, oneTest)
|
||||
TEST(Suite_a, one_test)
|
||||
"""
|
||||
assert [r.split(":")[1].strip() for r in reports(code)] == [
|
||||
"cannot rename 'SuiteA, oneTest' to 'SuiteA, one_test'",
|
||||
"cannot rename 'Suite_a, one_test' to 'SuiteA, one_test'",
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_same_name_in_different_suites_is_not_a_collision() -> None:
|
||||
code = """
|
||||
TEST(SuiteOne, mulDiv)
|
||||
TEST(SuiteTwo, mulDiv)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(SuiteOne, mul_div)
|
||||
TEST(SuiteTwo, mul_div)
|
||||
""")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
tests = sorted(
|
||||
(name, fn)
|
||||
for name, fn in globals().items()
|
||||
if name.startswith("test_") and callable(fn)
|
||||
)
|
||||
failed = 0
|
||||
for name, fn in tests:
|
||||
try:
|
||||
fn()
|
||||
print(f"PASS {name}")
|
||||
except AssertionError as exc:
|
||||
failed += 1
|
||||
print(f"FAIL {name}: {exc!r}")
|
||||
print(f"\n{len(tests) - failed}/{len(tests)} passed")
|
||||
return 1 if failed 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>
|
||||
|
||||
@@ -44,12 +44,18 @@ else()
|
||||
set(pkg_type rpm)
|
||||
endif()
|
||||
|
||||
# Unquoted below, so an empty value adds no argument at all.
|
||||
set(pkg_variant_option "")
|
||||
if(assert)
|
||||
set(pkg_variant_option --variant=assert)
|
||||
endif()
|
||||
|
||||
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
|
||||
${pkg_variant_option} --channel=UNRELEASED
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS xrpld validator-keys
|
||||
COMMENT "Building Linux ${pkg_type} package"
|
||||
|
||||
@@ -131,7 +131,17 @@ else()
|
||||
set(use_lld OFF CACHE BOOL "try lld linker, clang only" FORCE)
|
||||
endif()
|
||||
|
||||
option(jemalloc "Enables jemalloc for heap profiling" OFF)
|
||||
# Sanitizers need to intercept the system allocator.
|
||||
if(SANITIZERS_ENABLED)
|
||||
set(JEMALLOC_DEFAULT OFF)
|
||||
else()
|
||||
set(JEMALLOC_DEFAULT ON)
|
||||
endif()
|
||||
option(
|
||||
jemalloc
|
||||
"Use jemalloc instead of the system allocator"
|
||||
${JEMALLOC_DEFAULT}
|
||||
)
|
||||
option(werr "treat warnings as errors" OFF)
|
||||
option(
|
||||
local_protobuf
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -98,6 +98,8 @@ tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags"
|
||||
&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{ sanitizers }}", "SANITIZERS_COMPILER_FLAGS": "{{ sanitizer_compiler_flags | join(' ') }}", "SANITIZERS_LINKER_FLAGS": "{{ sanitizer_linker_flags | join(' ') }}"}
|
||||
|
||||
[options]
|
||||
# Let sanitizers intercept the system allocator rather than jemalloc.
|
||||
&:jemalloc=False
|
||||
{% if enable_asan %}
|
||||
# Build Boost.Context with ucontext backend (not fcontext) so that
|
||||
# ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber)
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -53,7 +52,7 @@ class Xrpl(ConanFile):
|
||||
"benchmark": True,
|
||||
"coverage": False,
|
||||
"fPIC": True,
|
||||
"jemalloc": False,
|
||||
"jemalloc": True,
|
||||
"rocksdb": True,
|
||||
"shared": False,
|
||||
"static": True,
|
||||
@@ -150,6 +149,7 @@ class Xrpl(ConanFile):
|
||||
self.requires("xxhash/0.8.3", transitive_headers=True)
|
||||
|
||||
exports_sources = (
|
||||
"bin/default-loader-path.sh",
|
||||
"CMakeLists.txt",
|
||||
"cfg/*",
|
||||
"cmake/*",
|
||||
@@ -225,9 +225,10 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
if self.options.jemalloc:
|
||||
libxrpl.requires.append("jemalloc::jemalloc")
|
||||
if self.options.rocksdb:
|
||||
libxrpl.requires.append("rocksdb::librocksdb")
|
||||
|
||||
@@ -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).
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
`xrpld` is published as DEB and RPM packages for 64-bit x86 Linux.
|
||||
Use APT on Debian-based distributions such as Debian and Ubuntu,
|
||||
and YUM on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux.
|
||||
and DNF on Red Hat-based distributions such as RHEL, AlmaLinux, and Rocky Linux,
|
||||
where `yum` is a symlink to `dnf`.
|
||||
To build from source instead, see [BUILD.md](../BUILD.md).
|
||||
|
||||
## Release channels
|
||||
@@ -81,7 +82,7 @@ wherever it appears in the repository configuration.
|
||||
sudo apt -y install xrpld
|
||||
```
|
||||
|
||||
### With the YUM package manager
|
||||
### With the DNF package manager
|
||||
|
||||
1. Add the XRPL Foundation package-signing key:
|
||||
|
||||
@@ -109,9 +110,23 @@ wherever it appears in the repository configuration.
|
||||
3. Install the `xrpld` package:
|
||||
|
||||
```bash
|
||||
sudo yum install -y xrpld
|
||||
sudo dnf install -y xrpld
|
||||
```
|
||||
|
||||
### Optional: the assert-enabled build
|
||||
|
||||
Every channel also carries `xrpld-assert` as a DEB, the same build with assertions
|
||||
enabled, for diagnosing a problem on a non-production server.
|
||||
It installs the same files as `xrpld` and replaces it, so install one or the other:
|
||||
|
||||
```bash
|
||||
sudo apt -y install xrpld-assert # APT removes xrpld itself
|
||||
```
|
||||
|
||||
Switching stops the service, since it is a removal and an installation rather than an upgrade,
|
||||
and APT starts it again.
|
||||
Install `xrpld` the same way to switch back.
|
||||
|
||||
## The xrpld service
|
||||
|
||||
Both package managers install a systemd unit and enable it, so `xrpld` starts on boot.
|
||||
@@ -121,7 +136,7 @@ Check whether it is already running:
|
||||
systemctl status xrpld.service
|
||||
```
|
||||
|
||||
The APT packages start it immediately as well; the YUM packages do not, so start it yourself:
|
||||
The DEB packages start it immediately as well; the RPM packages do not, so start it yourself:
|
||||
|
||||
```bash
|
||||
sudo systemctl start xrpld.service
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace xrpl {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Allocator interaction note:
|
||||
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
|
||||
// ptmalloc return free heap pages to the OS.
|
||||
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
|
||||
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
|
||||
// on the *active* heap. The call is harmless but may not reclaim memory
|
||||
// because those allocators manage their own arenas.
|
||||
// ptmalloc return free heap pages to the OS, unless built with jemalloc.
|
||||
// - Builds with jemalloc disable trimming and its instrumentation entirely.
|
||||
// - Other linked or preloaded allocators (LD_PRELOAD) are not detected. Calling
|
||||
// glibc's malloc_trim typically has no effect on their heaps because those
|
||||
// allocators manage their own arenas.
|
||||
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
|
||||
// allocations are usually returned to the OS on free regardless of trimming.
|
||||
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
|
||||
@@ -47,18 +47,16 @@ struct MallocTrimReport
|
||||
* @brief Attempt to return freed memory to the operating system.
|
||||
*
|
||||
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
|
||||
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
|
||||
* a different allocator is in use, this function is a no-op and the report will
|
||||
* indicate that trimming is unsupported or had no effect.
|
||||
* free space from ptmalloc arenas back to the kernel. On other platforms, or
|
||||
* when built with jemalloc, this function performs no trimming or instrumentation
|
||||
* and returns a default report with supported=false.
|
||||
*
|
||||
* @param tag Identifier for logging/debugging purposes.
|
||||
* @param journal Journal for diagnostic logging.
|
||||
* @return Report containing before/after metrics and the trim result.
|
||||
*
|
||||
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
|
||||
* calling glibc's malloc_trim may have no effect on the active heap. The
|
||||
* call is harmless but typically does not reclaim memory under those
|
||||
* allocators.
|
||||
* @note Other linked or preloaded allocators are not detected at runtime.
|
||||
* Calling glibc's malloc_trim may have no effect on their heaps.
|
||||
*
|
||||
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
|
||||
* Large allocations satisfied via mmap are usually returned on free
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -162,4 +162,89 @@ toUInt64(std::string const& s);
|
||||
bool
|
||||
isProperlyFormedTomlDomain(std::string_view domain);
|
||||
|
||||
/**
|
||||
* Whether a view can be passed on as a C string.
|
||||
*
|
||||
* A reader given only data() stops at the first null, so the view must reach the
|
||||
* terminating null. The test rebuilds the view from data() and compares: a view
|
||||
* that stops earlier rebuilds longer, and so compares unequal.
|
||||
*
|
||||
* consteval because reading the byte after the view is only defined when @p str
|
||||
* points into storage holding a null at or after its end, such as a string
|
||||
* literal. An unterminated view is then a compile error, not an out-of-bounds
|
||||
* read.
|
||||
*
|
||||
* @param str The view to test.
|
||||
* @return Whether @p str is null-terminated. A view with no data is not.
|
||||
*/
|
||||
consteval bool
|
||||
isNullTerminated(std::string_view str)
|
||||
{
|
||||
if (str.data() == nullptr)
|
||||
return false;
|
||||
|
||||
// Reading past the view is the point, so the usual data() warning does not
|
||||
// apply.
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
|
||||
return std::string_view{str.data()} == str;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that is known to reach its terminating null.
|
||||
*
|
||||
* Converts to std::string_view, so it compares and hashes as one. Unlike a
|
||||
* view, asCString() may be handed to a reader that expects a C string, such
|
||||
* as json::StaticString.
|
||||
*
|
||||
* The only constructor is consteval and rejects a view that stops before the
|
||||
* null, so the property holds by construction and no caller asserts it.
|
||||
*/
|
||||
class NullTerminatedView
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Build a view from one that reaches its terminating null.
|
||||
*
|
||||
* Explicit, so that a plain view cannot become a proof of termination by
|
||||
* accident. The conversion the other way stays implicit.
|
||||
*
|
||||
* @param view The string to hold. Rejected at compile time if it stops
|
||||
* before its terminating null, or has no data.
|
||||
*/
|
||||
explicit consteval NullTerminatedView(std::string_view view)
|
||||
: data_(view.data()), size_(view.size())
|
||||
{
|
||||
if (!isNullTerminated(view))
|
||||
throw "xrpl::NullTerminatedView : view does not reach a null";
|
||||
}
|
||||
|
||||
constexpr
|
||||
operator std::string_view() const noexcept
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a view.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view
|
||||
view() const noexcept
|
||||
{
|
||||
return {data_, size_};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a C string. Never null.
|
||||
*/
|
||||
[[nodiscard]] constexpr char const*
|
||||
asCString() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
char const* data_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -518,7 +518,7 @@ public:
|
||||
* The input must be precisely `2 * bytes` hexadecimal characters
|
||||
* long, with one exception: the value '0'.
|
||||
*
|
||||
* @param sv A null-terminated string of hexadecimal characters
|
||||
* @param sv A string of hexadecimal characters
|
||||
* @return true if the input was parsed properly; false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
@@ -9,7 +10,8 @@
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace beast {
|
||||
class Journal;
|
||||
@@ -67,7 +69,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcStart(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcStart(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log successful finish of RPC call
|
||||
@@ -76,7 +78,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcFinish(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcFinish(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log errored RPC call
|
||||
@@ -85,7 +87,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcError(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcError(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log queued job
|
||||
@@ -150,10 +152,20 @@ public:
|
||||
PerfLog::Setup
|
||||
setupPerfLog(Section const& section, std::filesystem::path const& configDir);
|
||||
|
||||
/**
|
||||
* @param methodNames The RPC methods to count, one counter per name. Reported
|
||||
* as JSON keys that borrow each name and read it as a C string, which is
|
||||
* why the parameter type requires one that reaches its terminating null.
|
||||
* The names must outlive the returned object, which holds views of them.
|
||||
* The range itself need not: it is copied.
|
||||
* Passed in rather than looked up here, so that this layer needs no
|
||||
* knowledge of the dispatch table.
|
||||
*/
|
||||
std::unique_ptr<PerfLog>
|
||||
makePerfLog(
|
||||
PerfLog::Setup const& setup,
|
||||
Application& app,
|
||||
std::span<NullTerminatedView const> methodNames,
|
||||
beast::Journal journal,
|
||||
std::function<void()>&& signalStop);
|
||||
|
||||
@@ -161,7 +173,7 @@ template <typename Func, class Rep, class Period>
|
||||
auto
|
||||
measureDurationAndLog(
|
||||
Func&& func,
|
||||
std::string const& actionDescription,
|
||||
std::string_view actionDescription,
|
||||
std::chrono::duration<Rep, Period> maxDelay,
|
||||
beast::Journal const& journal)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
45
include/xrpl/ledger/entries/AMMEntry.h
Normal file
45
include/xrpl/ledger/entries/AMMEntry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AMMEntry : public SLEBase<ViewT, ltAMM>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltAMM>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AMMEntry(
|
||||
Asset const& issue1,
|
||||
Asset const& issue2,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amm(issue1, issue2), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit AMMEntry(
|
||||
uint256 const& ammID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amm(ammID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AMMEntryR = AMMEntry<ReadView>;
|
||||
using AMMEntryW = AMMEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/AccountRootEntry.h
Normal file
35
include/xrpl/ledger/entries/AccountRootEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltACCOUNT_ROOT>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AccountRootEntry(
|
||||
AccountID const& id,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::account(id), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AccountRootEntryR = AccountRootEntry<ReadView>;
|
||||
using AccountRootEntryW = AccountRootEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/AmendmentsEntry.h
Normal file
33
include/xrpl/ledger/entries/AmendmentsEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AmendmentsEntry : public SLEBase<ViewT, ltAMENDMENTS>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltAMENDMENTS>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AmendmentsEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amendments(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AmendmentsEntryR = AmendmentsEntry<ReadView>;
|
||||
using AmendmentsEntryW = AmendmentsEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/BridgeEntry.h
Normal file
36
include/xrpl/ledger/entries/BridgeEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class BridgeEntry : public SLEBase<ViewT, ltBRIDGE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltBRIDGE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit BridgeEntry(
|
||||
STXChainBridge const& bridge,
|
||||
STXChainBridge::ChainType chainType,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::bridge(bridge, chainType), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using BridgeEntryR = BridgeEntry<ReadView>;
|
||||
using BridgeEntryW = BridgeEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/CheckEntry.h
Normal file
46
include/xrpl/ledger/entries/CheckEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class CheckEntry : public SLEBase<ViewT, ltCHECK>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltCHECK>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit CheckEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::check(id, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CheckEntry(
|
||||
uint256 const& checkID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::check(checkID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using CheckEntryR = CheckEntry<ReadView>;
|
||||
using CheckEntryW = CheckEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
47
include/xrpl/ledger/entries/CredentialEntry.h
Normal file
47
include/xrpl/ledger/entries/CredentialEntry.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class CredentialEntry : public SLEBase<ViewT, ltCREDENTIAL>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltCREDENTIAL>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit CredentialEntry(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Slice const& credType,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::credential(subject, issuer, credType), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CredentialEntry(
|
||||
uint256 const& credentialID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::credential(credentialID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using CredentialEntryR = CredentialEntry<ReadView>;
|
||||
using CredentialEntryW = CredentialEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/DIDEntry.h
Normal file
35
include/xrpl/ledger/entries/DIDEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DIDEntry : public SLEBase<ViewT, ltDID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DIDEntry(
|
||||
AccountID const& account,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::did(account), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DIDEntryR = DIDEntry<ReadView>;
|
||||
using DIDEntryW = DIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/DelegateEntry.h
Normal file
36
include/xrpl/ledger/entries/DelegateEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DelegateEntry : public SLEBase<ViewT, ltDELEGATE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDELEGATE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DelegateEntry(
|
||||
AccountID const& account,
|
||||
AccountID const& authorizedAccount,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::delegate(account, authorizedAccount), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DelegateEntryR = DelegateEntry<ReadView>;
|
||||
using DelegateEntryW = DelegateEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
58
include/xrpl/ledger/entries/DepositPreauthEntry.h
Normal file
58
include/xrpl/ledger/entries/DepositPreauthEntry.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DepositPreauthEntry : public SLEBase<ViewT, ltDEPOSIT_PREAUTH>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDEPOSIT_PREAUTH>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
AccountID const& owner,
|
||||
AccountID const& preauthorized,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(owner, preauthorized), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
AccountID const& owner,
|
||||
std::set<std::pair<AccountID, Slice>> const& authCreds,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(owner, authCreds), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
uint256 const& preauthID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(preauthID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DepositPreauthEntryR = DepositPreauthEntry<ReadView>;
|
||||
using DepositPreauthEntryW = DepositPreauthEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
50
include/xrpl/ledger/entries/DirectoryNodeEntry.h
Normal file
50
include/xrpl/ledger/entries/DirectoryNodeEntry.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DirectoryNodeEntry : public SLEBase<ViewT, ltDIR_NODE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDIR_NODE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DirectoryNodeEntry(
|
||||
AccountID const& id,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ownerDir(id), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a specific page of the directory rooted at @p root.
|
||||
*/
|
||||
explicit DirectoryNodeEntry(
|
||||
uint256 const& root,
|
||||
std::uint64_t index,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::page(root, index), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DirectoryNodeEntryR = DirectoryNodeEntry<ReadView>;
|
||||
using DirectoryNodeEntryW = DirectoryNodeEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/ledger/entries/EscrowEntry.h
Normal file
37
include/xrpl/ledger/entries/EscrowEntry.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class EscrowEntry : public SLEBase<ViewT, ltESCROW>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltESCROW>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit EscrowEntry(
|
||||
AccountID const& src,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::escrow(src, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using EscrowEntryR = EscrowEntry<ReadView>;
|
||||
using EscrowEntryW = EscrowEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/FeeSettingsEntry.h
Normal file
33
include/xrpl/ledger/entries/FeeSettingsEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class FeeSettingsEntry : public SLEBase<ViewT, ltFEE_SETTINGS>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltFEE_SETTINGS>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit FeeSettingsEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::feeSettings(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using FeeSettingsEntryR = FeeSettingsEntry<ReadView>;
|
||||
using FeeSettingsEntryW = FeeSettingsEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/LedgerHashesEntry.h
Normal file
33
include/xrpl/ledger/entries/LedgerHashesEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LedgerHashesEntry : public SLEBase<ViewT, ltLEDGER_HASHES>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLEDGER_HASHES>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LedgerHashesEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::skip(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
||||
using LedgerHashesEntryW = LedgerHashesEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/LoanBrokerEntry.h
Normal file
46
include/xrpl/ledger/entries/LoanBrokerEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LoanBrokerEntry : public SLEBase<ViewT, ltLOAN_BROKER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLOAN_BROKER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LoanBrokerEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loanBroker(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit LoanBrokerEntry(
|
||||
uint256 const& loanBrokerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loanBroker(loanBrokerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LoanBrokerEntryR = LoanBrokerEntry<ReadView>;
|
||||
using LoanBrokerEntryW = LoanBrokerEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
45
include/xrpl/ledger/entries/LoanEntry.h
Normal file
45
include/xrpl/ledger/entries/LoanEntry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LoanEntry : public SLEBase<ViewT, ltLOAN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLOAN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LoanEntry(
|
||||
uint256 const& loanBrokerID,
|
||||
SeqProxy const& loanSeq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loan(loanBrokerID, loanSeq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit LoanEntry(
|
||||
uint256 const& loanID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loan(loanID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LoanEntryR = LoanEntry<ReadView>;
|
||||
using LoanEntryW = LoanEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/ledger/entries/MPTokenEntry.h
Normal file
55
include/xrpl/ledger/entries/MPTokenEntry.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class MPTokenEntry : public SLEBase<ViewT, ltMPTOKEN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltMPTOKEN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit MPTokenEntry(
|
||||
MPTID const& issuanceID,
|
||||
AccountID const& holder,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(issuanceID, holder), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenEntry(
|
||||
uint256 const& issuanceKey,
|
||||
AccountID const& holder,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(issuanceKey, holder), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenEntry(
|
||||
uint256 const& mptokenKey,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(mptokenKey), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using MPTokenEntryR = MPTokenEntry<ReadView>;
|
||||
using MPTokenEntryW = MPTokenEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
56
include/xrpl/ledger/entries/MPTokenIssuanceEntry.h
Normal file
56
include/xrpl/ledger/entries/MPTokenIssuanceEntry.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class MPTokenIssuanceEntry : public SLEBase<ViewT, ltMPTOKEN_ISSUANCE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltMPTOKEN_ISSUANCE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
std::uint32_t seq,
|
||||
AccountID const& issuer,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(makeMptID(seq, issuer)), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
MPTID const& issuanceID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(issuanceID), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
uint256 const& issuanceKey,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(issuanceKey), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using MPTokenIssuanceEntryR = MPTokenIssuanceEntry<ReadView>;
|
||||
using MPTokenIssuanceEntryW = MPTokenIssuanceEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/NFTokenOfferEntry.h
Normal file
46
include/xrpl/ledger/entries/NFTokenOfferEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NFTokenOfferEntry : public SLEBase<ViewT, ltNFTOKEN_OFFER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNFTOKEN_OFFER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NFTokenOfferEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenOffer(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit NFTokenOfferEntry(
|
||||
uint256 const& offerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenOffer(offerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NFTokenOfferEntryR = NFTokenOfferEntry<ReadView>;
|
||||
using NFTokenOfferEntryW = NFTokenOfferEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/ledger/entries/NFTokenPageEntry.h
Normal file
37
include/xrpl/ledger/entries/NFTokenPageEntry.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NFTokenPageEntry : public SLEBase<ViewT, ltNFTOKEN_PAGE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNFTOKEN_PAGE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NFTokenPageEntry(
|
||||
Keylet const& page,
|
||||
uint256 const& token,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenPage(page, token), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NFTokenPageEntryR = NFTokenPageEntry<ReadView>;
|
||||
using NFTokenPageEntryW = NFTokenPageEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/NegativeUNLEntry.h
Normal file
33
include/xrpl/ledger/entries/NegativeUNLEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NegativeUNLEntry : public SLEBase<ViewT, ltNEGATIVE_UNL>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNEGATIVE_UNL>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NegativeUNLEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::negativeUNL(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NegativeUNLEntryR = NegativeUNLEntry<ReadView>;
|
||||
using NegativeUNLEntryW = NegativeUNLEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/OfferEntry.h
Normal file
46
include/xrpl/ledger/entries/OfferEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class OfferEntry : public SLEBase<ViewT, ltOFFER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltOFFER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit OfferEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::offer(id, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit OfferEntry(
|
||||
uint256 const& offerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::offer(offerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using OfferEntryR = OfferEntry<ReadView>;
|
||||
using OfferEntryW = OfferEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/OracleEntry.h
Normal file
38
include/xrpl/ledger/entries/OracleEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class OracleEntry : public SLEBase<ViewT, ltORACLE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltORACLE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit OracleEntry(
|
||||
AccountID const& account,
|
||||
std::uint32_t documentID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::oracle(account, documentID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using OracleEntryR = OracleEntry<ReadView>;
|
||||
using OracleEntryW = OracleEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/PayChannelEntry.h
Normal file
38
include/xrpl/ledger/entries/PayChannelEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class PayChannelEntry : public SLEBase<ViewT, ltPAYCHAN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltPAYCHAN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit PayChannelEntry(
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::payChannel(src, dst, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PayChannelEntryR = PayChannelEntry<ReadView>;
|
||||
using PayChannelEntryW = PayChannelEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/PermissionedDomainEntry.h
Normal file
46
include/xrpl/ledger/entries/PermissionedDomainEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class PermissionedDomainEntry : public SLEBase<ViewT, ltPERMISSIONED_DOMAIN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltPERMISSIONED_DOMAIN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit PermissionedDomainEntry(
|
||||
AccountID const& account,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::permissionedDomain(account, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit PermissionedDomainEntry(
|
||||
uint256 const& domainID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::permissionedDomain(domainID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PermissionedDomainEntryR = PermissionedDomainEntry<ReadView>;
|
||||
using PermissionedDomainEntryW = PermissionedDomainEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
48
include/xrpl/ledger/entries/RippleStateEntry.h
Normal file
48
include/xrpl/ledger/entries/RippleStateEntry.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class RippleStateEntry : public SLEBase<ViewT, ltRIPPLE_STATE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltRIPPLE_STATE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit RippleStateEntry(
|
||||
AccountID const& id0,
|
||||
AccountID const& id1,
|
||||
Currency const& currency,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::trustLine(id0, id1, currency), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit RippleStateEntry(
|
||||
AccountID const& id,
|
||||
Issue const& issue,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::trustLine(id, issue), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using RippleStateEntryR = RippleStateEntry<ReadView>;
|
||||
using RippleStateEntryW = RippleStateEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
503
include/xrpl/ledger/entries/SLEBase.h
Normal file
503
include/xrpl/ledger/entries/SLEBase.h
Normal file
@@ -0,0 +1,503 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Concept to distinguish read-only vs writable view types
|
||||
template <typename V>
|
||||
concept IsWritableView = std::derived_from<V, ApplyView>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Resolves a keylet for a read-only entry.
|
||||
*
|
||||
* ReadView::read() on an ApplyView returns the underlying ledger's entry
|
||||
* whenever the view is not already tracking one, while peek() installs the
|
||||
* view's own copy and returns that. A read-only entry built with read()
|
||||
* would therefore hold an SLE that goes stale the moment anything peeks the
|
||||
* same key and modifies it. Resolve through peek() whenever the view really is
|
||||
* an ApplyView, so every entry over that view shares one SLE.
|
||||
*
|
||||
* @note The const_cast is what makes reaching ApplyView::peek() possible, and
|
||||
* it is defined behavior only when the view really is a non-const
|
||||
* object that the caller merely observes through a const reference.
|
||||
* That holds for every production view today, but it is not a
|
||||
* guarantee the codebase makes: the unit tests already build
|
||||
* genuinely const ApplyView-derived objects (`Sandbox const` in
|
||||
* Directory_test.cpp and View_test.cpp, `PaymentSandbox const` in
|
||||
* TheoreticalQuality_test.cpp and View_test.cpp). Constructing a
|
||||
* read-only entry over one of those would be undefined behavior, so
|
||||
* do not, until #8069 removes the cast -- by giving ApplyView a
|
||||
* const-qualified peek(), which needs no amendment because
|
||||
* Action::Cache is invisible to apply(), visit() and metadata.
|
||||
*
|
||||
* @note Consequently a "read-only" entry over an ApplyView is not free of
|
||||
* side effects: peek() installs an Action::Cache entry in the apply
|
||||
* state table. That is benign for transaction metadata -- Cache entries
|
||||
* are skipped in ApplyStateTable::apply(), ::visit() and in metadata
|
||||
* generation -- but it does cost one deep SLE copy on first touch.
|
||||
*/
|
||||
inline SLE::const_pointer
|
||||
resolveEntry(ReadView const& view, Keylet const& key)
|
||||
{
|
||||
// Safe only for a view that is not itself a const object -- see the
|
||||
// note above. The entry holds a const reference because it does not
|
||||
// modify the view, not because the view is const.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
||||
if (auto const applyView = dynamic_cast<ApplyView*>(const_cast<ReadView*>(&view)))
|
||||
return applyView->peek(key);
|
||||
return view.read(key);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* View-parameterized base class for all ledger entries.
|
||||
*
|
||||
* SLEBase<ReadView> — read-only: holds shared_ptr<SLE const> + ReadView const&
|
||||
* SLEBase<ApplyView> — writable: holds shared_ptr<SLE> + ApplyView& + Keylet,
|
||||
* plus insert/update/erase operations
|
||||
*
|
||||
* Write-only members are gated by `requires` clauses, providing compile-time
|
||||
* guarantees that read-only entries cannot mutate state.
|
||||
*
|
||||
* @tparam EntryType the ledger entry type this entry is statically bound to.
|
||||
* Derived per-type entries pass their own type (e.g. ltACCOUNT_ROOT); the
|
||||
* generic ReadOnlySLE / WritableSLE aliases leave it at ltANY, which opts out
|
||||
* of the static type check. Binding the type here is what keeps an entry for
|
||||
* one entry type from being constructed or converted from another -- see the
|
||||
* converting constructor below.
|
||||
*
|
||||
* Derived classes should provide domain-specific accessors that hide
|
||||
* implementation details of the underlying ledger entry format.
|
||||
*/
|
||||
template <typename ViewT, LedgerEntryType EntryType = ltANY>
|
||||
class SLEBase
|
||||
{
|
||||
public:
|
||||
static constexpr bool kIsWritable = IsWritableView<ViewT>;
|
||||
|
||||
// The ledger entry type this entry is bound to, and whether that binding
|
||||
// is meaningful (ltANY means "any type", i.e. no static check).
|
||||
static constexpr LedgerEntryType kEntryType = EntryType;
|
||||
static constexpr bool kIsTyped = (EntryType != ltANY);
|
||||
|
||||
// SLE pointer type: mutable for writable views, const for read-only
|
||||
using SlePtrType = std::conditional_t<kIsWritable, SLE::pointer, SLE::const_pointer>;
|
||||
|
||||
// View reference type: ApplyView& for writable, ReadView const& for
|
||||
// read-only
|
||||
using ViewRefType = std::conditional_t<kIsWritable, ApplyView&, ReadView const&>;
|
||||
|
||||
// Non-virtual by design: these entries are parameterized on the view and
|
||||
// entry type, never used polymorphically through a base pointer. A vptr
|
||||
// would be 8 bytes of pure overhead on a type meant to be as cheap as the
|
||||
// shared_ptr it wraps. See the static_assert below the class.
|
||||
//
|
||||
// The destructor is public because the ReadOnlySLE / WritableSLE aliases
|
||||
// name this class directly and are used as value types. Since it is not
|
||||
// virtual, never delete a derived entry through an SLEBase*.
|
||||
~SLEBase() = default;
|
||||
|
||||
SLEBase(SLEBase const&)
|
||||
requires(!kIsWritable)
|
||||
= default;
|
||||
SLEBase(SLEBase&&) = default;
|
||||
SLEBase&
|
||||
operator=(SLEBase const&) = delete;
|
||||
SLEBase&
|
||||
operator=(SLEBase&&) = delete;
|
||||
SLEBase() = delete;
|
||||
|
||||
// --- Constructors that adopt/resolve an SLE (public so the ReadOnlySLE /
|
||||
// WritableSLE aliases and the per-type entries can be built directly
|
||||
// from a keylet, or -- read-only only -- from an already-fetched
|
||||
// SLE). ---
|
||||
|
||||
/**
|
||||
* Constructor for read-only context (adopt an already-fetched SLE).
|
||||
*
|
||||
* There is deliberately no writable equivalent: a writable entry needs
|
||||
* a Keylet so that newSLE() can still build an entry when none exists,
|
||||
* and that cannot be recovered from a null SLE.
|
||||
*/
|
||||
explicit SLEBase(
|
||||
SLE::const_pointer sle,
|
||||
ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires(!kIsWritable)
|
||||
: view_(view), sle_(std::move(sle)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || !sle_ || sle_->getType() == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : adopted SLE matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for read-only context (read from view by keylet)
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires(!kIsWritable)
|
||||
: view_(view), sle_(detail::resolveEntry(view, key)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || key.type == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : keylet matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converting constructor: writable → read-only.
|
||||
*
|
||||
* Enables implicit conversion from SLEBase<ApplyView> to
|
||||
* SLEBase<ReadView>, so functions taking ReadOnlySLE const& can accept
|
||||
* WritableSLE.
|
||||
*
|
||||
* Constrained to the same entry type (or to a ltANY target, i.e. widening
|
||||
* a typed entry to a generic ReadOnlySLE). The constraint is load-bearing:
|
||||
* this constructor is inherited into every per-type entry, and unconstrained
|
||||
* it would bind any writable entry that slices to SLEBase, so an OfferEntryW
|
||||
* would convert to an AccountRootEntryR with no cast at the call site.
|
||||
*/
|
||||
template <typename OtherViewT, LedgerEntryType OtherType>
|
||||
SLEBase(SLEBase<OtherViewT, OtherType> const& other)
|
||||
requires(!kIsWritable && IsWritableView<OtherViewT> &&
|
||||
(OtherType == EntryType || EntryType == ltANY))
|
||||
: view_(other.readView()), sle_(other.rawSle()), j_(other.journal())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context (peek from view by keylet)
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ApplyView& view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: view_(view), key_(key), sle_(view_.peek(key)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || key.type == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : keylet matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context, for call sites that hold an
|
||||
* ApplyViewContext (peek from ctx.view by keylet).
|
||||
*
|
||||
* ctx.tx is not retained: this exists purely so transactors can pass the
|
||||
* context they already have instead of spelling out ctx.view. If an entry
|
||||
* ever needs the applying transaction, store it here rather than adding
|
||||
* another overload.
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ApplyViewContext const& ctx,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: SLEBase(key, ctx.view, j)
|
||||
{
|
||||
}
|
||||
|
||||
// --- Common interface (always available) ---
|
||||
|
||||
/**
|
||||
* Returns true if the ledger entry exists
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
exists() const
|
||||
{
|
||||
return sle_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit conversion to bool for convenient existence checking
|
||||
*/
|
||||
explicit
|
||||
operator bool() const
|
||||
{
|
||||
return exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying SLE for read access.
|
||||
*
|
||||
* Prefer operator-> / operator* for field access; this is for the call
|
||||
* sites that need the shared_ptr itself.
|
||||
*/
|
||||
[[nodiscard]] SLE::const_pointer
|
||||
rawSle() const
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ledger entry type of this entry.
|
||||
*
|
||||
* For a per-type entry this is kEntryType, known at compile time and
|
||||
* valid whether or not the entry exists. Only the generic ReadOnlySLE /
|
||||
* WritableSLE aliases have to read it back out of the SLE.
|
||||
*
|
||||
* @throws std::logic_error for a generic (ltANY) entry if exists() is
|
||||
* false.
|
||||
*/
|
||||
[[nodiscard]] LedgerEntryType
|
||||
type() const
|
||||
{
|
||||
if constexpr (kIsTyped)
|
||||
{
|
||||
return kEntryType;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::type : entry does not exist");
|
||||
return sle_->getType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keylet identifying this entry.
|
||||
*
|
||||
* Writable entries keep the keylet they were built from, so it is valid
|
||||
* even before newSLE(). Read-only entries derive it from the SLE, which
|
||||
* must therefore exist.
|
||||
*
|
||||
* @throws std::logic_error for a read-only entry if exists() is false.
|
||||
*/
|
||||
[[nodiscard]] Keylet
|
||||
keylet() const
|
||||
{
|
||||
if constexpr (kIsWritable)
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::keylet : entry does not exist");
|
||||
// Take the type from the SLE, not from kEntryType: the adopt-SLE
|
||||
// constructor's type check is assert-only, so a Release build can
|
||||
// be holding an SLE whose type disagrees with the binding, and the
|
||||
// SLE is the one telling the truth.
|
||||
return Keylet(sle_->getType(), sle_->key());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ledger key of this entry.
|
||||
*
|
||||
* @throws std::logic_error same as keylet(): for read-only entries,
|
||||
* if exists() is false.
|
||||
*/
|
||||
[[nodiscard]] uint256
|
||||
key() const
|
||||
{
|
||||
return keylet().key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the read view (always available; ApplyView inherits ReadView)
|
||||
*/
|
||||
[[nodiscard]] ReadView const&
|
||||
readView() const
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Const dereference operators (always available)
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
STLedgerEntry const*
|
||||
operator->() const
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry const&
|
||||
operator*() const
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator* : entry does not exist");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
// --- Writable interface (compile-time gated) ---
|
||||
//
|
||||
// Everything that hands out mutable access (or mutates) is non-const, so
|
||||
// that a `FooEntryW const&` is as inert as a `FooEntryR`. Use readView()
|
||||
// when a const entry only needs to inspect the view.
|
||||
|
||||
/**
|
||||
* Returns the underlying SLE for write access.
|
||||
*
|
||||
* Prefer operator-> / operator* for field access; this is for the call
|
||||
* sites that need the shared_ptr itself.
|
||||
*/
|
||||
[[nodiscard]] SlePtrType const&
|
||||
mutableRawSle()
|
||||
requires kIsWritable
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the apply view for write operations
|
||||
*/
|
||||
[[nodiscard]] ApplyView&
|
||||
applyView()
|
||||
requires kIsWritable
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable dereference operators
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
STLedgerEntry*
|
||||
operator->()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry&
|
||||
operator*()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator* : entry does not exist");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the entry into the view.
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
insert()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::insert : entry does not exist");
|
||||
view_.insert(sle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the entry from the view.
|
||||
*
|
||||
* Drops the SLE afterwards, so the entry reports !exists() and any
|
||||
* further use throws here rather than either throwing from deep inside
|
||||
* ApplyStateTable or -- worse -- silently succeeding. For an
|
||||
* entry that already existed, ApplyStateTable::erase keeps holding this
|
||||
* exact SLE and builds the DeletedNode's FinalFields from it, so a write
|
||||
* through the entry after erase() would land in transaction metadata
|
||||
* with no diagnostic at all.
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
erase()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::erase : entry does not exist");
|
||||
view_.erase(sle_);
|
||||
sle_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
update()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::update : entry does not exist");
|
||||
view_.update(sle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws std::logic_error if exists() is true: newSLE() would otherwise
|
||||
* silently discard the SLE already held.
|
||||
*/
|
||||
void
|
||||
newSLE()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::newSLE : entry already exists");
|
||||
sle_ = std::make_shared<SLE>(key_);
|
||||
}
|
||||
|
||||
[[nodiscard]] beast::Journal
|
||||
journal() const
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
protected:
|
||||
ViewRefType view_;
|
||||
|
||||
// Keylet is only meaningful for writable views, which need it to build an
|
||||
// SLE that does not exist yet; read-only entries derive it from the SLE.
|
||||
struct Empty
|
||||
{
|
||||
};
|
||||
|
||||
// No default member initializer: Keylet is not default-constructible, so
|
||||
// every writable constructor must initialize key_ explicitly.
|
||||
[[no_unique_address]]
|
||||
std::conditional_t<kIsWritable, Keylet, Empty> key_;
|
||||
|
||||
SlePtrType sle_{};
|
||||
beast::Journal j_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic (any-entry-type) SLE entries.
|
||||
*
|
||||
* Use these when the concrete ledger entry type is not known at a given site;
|
||||
* otherwise prefer the per-type entries (e.g. AccountRootEntry.h), which
|
||||
* additionally enforce the entry type at compile time.
|
||||
*
|
||||
* SLE::const_pointer / SLE::const_ref -> ReadOnlySLE
|
||||
* SLE::pointer / SLE::ref -> WritableSLE
|
||||
*/
|
||||
using ReadOnlySLE = SLEBase<ReadView>;
|
||||
using WritableSLE = SLEBase<ApplyView>;
|
||||
|
||||
static_assert(
|
||||
!std::is_polymorphic_v<ReadOnlySLE> && !std::is_polymorphic_v<WritableSLE>,
|
||||
"SLEBase must stay a thin value type; it must not acquire a vtable");
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/SignerListEntry.h
Normal file
35
include/xrpl/ledger/entries/SignerListEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class SignerListEntry : public SLEBase<ViewT, ltSIGNER_LIST>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltSIGNER_LIST>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit SignerListEntry(
|
||||
AccountID const& account,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::signerList(account), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using SignerListEntryR = SignerListEntry<ReadView>;
|
||||
using SignerListEntryW = SignerListEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/SponsorshipEntry.h
Normal file
36
include/xrpl/ledger/entries/SponsorshipEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class SponsorshipEntry : public SLEBase<ViewT, ltSPONSORSHIP>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltSPONSORSHIP>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit SponsorshipEntry(
|
||||
AccountID const& sponsor,
|
||||
AccountID const& sponsee,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::sponsorship(sponsor, sponsee), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using SponsorshipEntryR = SponsorshipEntry<ReadView>;
|
||||
using SponsorshipEntryW = SponsorshipEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/TicketEntry.h
Normal file
46
include/xrpl/ledger/entries/TicketEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class TicketEntry : public SLEBase<ViewT, ltTICKET>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltTICKET>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit TicketEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& ticketSeq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ticket(id, ticketSeq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TicketEntry(
|
||||
uint256 const& ticketID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ticket(ticketID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using TicketEntryR = TicketEntry<ReadView>;
|
||||
using TicketEntryW = TicketEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/VaultEntry.h
Normal file
46
include/xrpl/ledger/entries/VaultEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class VaultEntry : public SLEBase<ViewT, ltVAULT>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltVAULT>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit VaultEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::vault(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit VaultEntry(
|
||||
uint256 const& vaultID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::vault(vaultID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using VaultEntryR = VaultEntry<ReadView>;
|
||||
using VaultEntryW = VaultEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h
Normal file
38
include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class XChainOwnedClaimIDEntry : public SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit XChainOwnedClaimIDEntry(
|
||||
STXChainBridge const& bridge,
|
||||
std::uint64_t seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::xChainClaimID(bridge, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using XChainOwnedClaimIDEntryR = XChainOwnedClaimIDEntry<ReadView>;
|
||||
using XChainOwnedClaimIDEntryW = XChainOwnedClaimIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class XChainOwnedCreateAccountClaimIDEntry
|
||||
: public SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit XChainOwnedCreateAccountClaimIDEntry(
|
||||
STXChainBridge const& bridge,
|
||||
std::uint64_t seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::xChainCreateAccountClaimID(bridge, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using XChainOwnedCreateAccountClaimIDEntryR = XChainOwnedCreateAccountClaimIDEntry<ReadView>;
|
||||
using XChainOwnedCreateAccountClaimIDEntryW = XChainOwnedCreateAccountClaimIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -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
|
||||
@@ -338,17 +338,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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -356,9 +338,9 @@ struct AccountingDeltas
|
||||
Number debtTotalDelta;
|
||||
};
|
||||
|
||||
// Whole-life (pre-LendingProtocolV1_1) recognition model: interest is
|
||||
// recognized into AssetsTotal/DebtTotal up front, at origination.
|
||||
namespace accrual {
|
||||
// Instant interest recognition (pre-LendingProtocolV1_1): interest is
|
||||
// recognized into AssetsTotal/DebtTotal immediately, at origination.
|
||||
namespace instant_recognition {
|
||||
|
||||
// LoanSet origination: what's added to Vault.AssetsTotal and LoanBroker.DebtTotal
|
||||
AccountingDeltas
|
||||
@@ -380,7 +362,7 @@ loanVaultExposure(SLE::const_ref loanSle);
|
||||
AccountingDeltas
|
||||
loanPaymentDeltas(LoanPaymentParts const& parts);
|
||||
|
||||
} // namespace accrual
|
||||
} // namespace instant_recognition
|
||||
|
||||
// Cash-basis (LendingProtocolV1_1) recognition model: AssetsTotal/DebtTotal
|
||||
// are principal-only, interest is recognized only as it's actually paid.
|
||||
@@ -399,7 +381,7 @@ loanPaymentDeltas(LoanPaymentParts const& parts);
|
||||
|
||||
// Public dispatchers: pick cash_basis:: if featureLendingProtocolV1_1 is
|
||||
// enabled AND the Vault's LEVersion (VaultHelpers::getVaultVersion) is
|
||||
// VaultVersion::CashBasis, else accrual::. These are the only entry points
|
||||
// VaultVersion::CashBasis, else instant_recognition::. These are the only entry points
|
||||
// transactors call.
|
||||
AccountingDeltas
|
||||
loanOriginationDeltas(
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -392,8 +391,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,7 +1,6 @@
|
||||
#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>
|
||||
@@ -17,111 +16,20 @@
|
||||
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,11 +43,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);
|
||||
sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Adjusts a requested asset change (`delta`) to match the decimal scale of the
|
||||
@@ -187,12 +91,11 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
|
||||
* unrealized loss is waived. Used by assetsToSharesWithdraw and
|
||||
* sharesToAssetsWithdraw as the numerator of 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.
|
||||
*/
|
||||
[[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
|
||||
@@ -228,7 +131,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 +152,6 @@ assetsToSharesWithdraw(
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
ReadView const& view,
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
STAmount const& shares,
|
||||
@@ -271,8 +172,8 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref
|
||||
|
||||
/**
|
||||
* Resolves a Vault's LEVersion, the single point every accounting touch
|
||||
* point should call to determine which recognition model (accrual vs.
|
||||
* cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
|
||||
* point should call to determine which recognition model (instant interest
|
||||
* recognition vs. cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
|
||||
* activated never have sfLEVersion set, which resolves here to
|
||||
* VaultVersion::Legacy.
|
||||
*
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
void
|
||||
importDatabase(Database& source) override
|
||||
{
|
||||
importInternal(*backend_.get(), source);
|
||||
importInternal(*backend_, source);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -26,7 +26,8 @@ struct Config
|
||||
/**
|
||||
* The largest number of public peer slots to allow.
|
||||
* This includes both inbound and outbound, but does not include
|
||||
* fixed peers.
|
||||
* fixed peers. A configuration built by `makeConfig` always holds
|
||||
* `maxPeers == inPeers + outPeers`.
|
||||
*/
|
||||
std::size_t maxPeers{tuning::kDefaultMaxPeers};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user