Compare commits

..

16 Commits

Author SHA1 Message Date
Bart
4a4fded2eb chore: Bump version to 3.4.0 2026-09-16 18:23:54 +02:00
yinyiqian1
c8e767afa4 fix: Reject PaymentBurn payments that cross zero balance 2026-09-16 18:23:28 +02:00
Pratik Mankawde
00eeb0a005 fix: Reject variable-length prefixes the encoder cannot write 2026-09-16 18:23:28 +02:00
Vito Tumas
a18839d92d fix: Relax MPT authorize cap for LoanSet and VaultWithdraw 2026-09-16 18:23:28 +02:00
Gregory Tsipenyuk
8c594c7ed9 fix: Skip CheckCash limit waiver for the issuer 2026-09-16 18:22:39 +02:00
Ayaz Salikhov
ebd810b184 build: Add missing script to conan package 2026-09-16 18:22:26 +02:00
Jingchen
3e4e56d6bb fix: Make calculateBaseFee exception-safe 2026-09-16 18:22:26 +02:00
Ayaz Salikhov
76da5d4475 build: Fix test installation on debian:11 due to EOL 2026-09-16 18:22:26 +02:00
Ayaz Salikhov
c0d0fd0d97 build: Add assert-enabled builds and packages 2026-09-16 18:22:26 +02:00
Timothy Banks
9aebb5ebea fix: Cap TMTransactions list size and charge fee for undeserializable transactions 2026-09-16 18:22:26 +02:00
Vito Tumas
796f2f8f1e fix: Relax Loan Invariants to allow zero-principal LoanPay transaction 2026-09-16 18:22:26 +02:00
Timothy Banks
b190f2b14f test: Add ProtocolMessage harness for testing TMPing 2026-09-16 18:22:25 +02:00
Timothy Banks
6099940c2c fix: Unbounded Database Seek via TMGetLedger 2026-09-04 15:32:36 +01:00
Ed Hennis
0db7b766e6 fix: Trim unknown fields when parsing incoming peer protobuf messages 2026-09-04 10:32:08 +00:00
Mayukha Vadari
ea6226b8b9 fix: Prevent simulate from updating the orderbook db 2026-09-04 06:17:43 -04:00
Timothy Banks
eae0a35415 fix: Use a hardened hash on the STPathElement 2026-09-03 17:06:27 -04:00
757 changed files with 4759 additions and 163653 deletions

View File

@@ -7,8 +7,6 @@ ignorePaths:
- cmake/**
- LICENSE.md
- .clang-tidy
- src/test/app/wasm_fixtures/**/*.wat
- src/test/app/wasm_fixtures/*.c
- nix/check-tools/*.txt # generated, and full of Nix store hashes
language: en
allowCompoundWords: true # TODO (#6334)
@@ -45,7 +43,6 @@ suggestWords:
- synched->synced
- synch->sync
words:
- cusip
- abempty
- AMMID
- AMMMPT
@@ -71,11 +68,8 @@ words:
- Btrfs
- Buildx
- canonicality
- cdylib
- canonicalised
- canonicality
- cctools
- CGNAT
- changespq
- checkme
- choco
@@ -120,7 +114,7 @@ words:
- dsymutil
- dxrpl
- elgamal
- emittance
- enabled
- enablerepo
- endmacro
- envrc
@@ -147,6 +141,7 @@ words:
- hwrap
- ifndef
- inequation
- Injectivity
- insuf
- insuff
- invasively
@@ -183,11 +178,11 @@ words:
- mathbunnyru
- mcmodel
- MEMORYSTATUSEX
- MPTAMM
- MPTDEX
- Merkle
- misprediction
- mispricing
- missingok
- MPTAMM
- mptbalance
- MPTDEX
- mptflags
@@ -262,8 +257,8 @@ words:
- replayer
- repodata
- repomd
- rerandomization
- rerandomize
- rerandomization
- rerandomized
- rerandomizes
- rerere
@@ -286,8 +281,8 @@ words:
- rustup
- sahyadri
- Satoshi
- Schnorr
- scons
- Schnorr
- secp
- sendq
- seqit
@@ -312,9 +307,7 @@ words:
- statsd
- STATSDCOLLECTOR
- stissue
- stjson
- stnum
- stnumber
- stobj
- stobject
- stpath
@@ -341,8 +334,8 @@ words:
- txn
- txns
- txs
- UBSAN
- ubsan
- UBSAN
- ufdio
- umant
- unacquired
@@ -354,9 +347,7 @@ words:
- unfindable
- unflatten
- unfund
- ungated
- unimpair
- unmetered
- unroutable
- unscalable
- unserviced
@@ -382,11 +373,9 @@ words:
- writeme
- wsrch
- wthread
- Xahau
- xbridge
- xchain
- xcrun
- xfloat
- ximinez
- XMACRO
- xored
@@ -397,3 +386,5 @@ words:
- xxhash
- xxhasher
- zstdio
- CGNAT
- ungated

2
.github/CODEOWNERS vendored
View File

@@ -1,2 +0,0 @@
# Allow anyone to review any change by default.
*

View File

@@ -19,7 +19,6 @@ libxrpl.ledger > xrpl.json
libxrpl.ledger > xrpl.ledger
libxrpl.ledger > xrpl.nodestore
libxrpl.ledger > xrpl.protocol
libxrpl.ledger > xrpl.server
libxrpl.ledger > xrpl.shamap
libxrpl.net > xrpl.basics
libxrpl.net > xrpl.net
@@ -207,7 +206,6 @@ xrpl.core > xrpl.json
xrpl.core > xrpl.protocol
xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics
xrpl.ledger > xrpl.core
xrpl.ledger > xrpl.json
xrpl.ledger > xrpl.nodestore
xrpl.ledger > xrpl.protocol

View File

@@ -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(

View File

@@ -1,5 +1,5 @@
{
"image_tag": "sha-060957e",
"image_tag": "sha-473fe44",
"configs": {
"ubuntu": [
{
@@ -17,6 +17,7 @@
"minimal": true,
"benchmark": true
},
{
"compiler": ["gcc"],
"build_type": ["Release"],
@@ -29,6 +30,7 @@
"arch": ["arm64"],
"minimal": false
},
{
"compiler": ["gcc", "clang"],
"build_type": ["Debug", "Release"],
@@ -36,6 +38,7 @@
"minimal": false,
"sanitizers": ["address", "undefinedbehavior"]
},
{
"compiler": ["clang"],
"build_type": ["Debug"],
@@ -59,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"],

View File

@@ -5,13 +5,15 @@ on:
branches:
- develop
paths:
- ".github/workflows/build-nix-images.yml"
- "flake.nix"
- "flake.lock"
- "rust-toolchain.toml"
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "!nix/check-tools/*.txt"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"
pull_request:
@@ -23,7 +25,7 @@ on:
- "nix/**"
- "!nix/docker/README.md"
- "!nix/devshell.nix"
- "!nix/check-tools/**"
- "!nix/check-tools/*.txt"
- "bin/check-tools.sh"
- "bin/default-loader-path.sh"
- "bin/install-sanitizer-libs.sh"

View File

@@ -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 }}

View File

@@ -34,7 +34,7 @@ permissions:
jobs:
audit:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
permissions:
contents: read
# Needed to open an issue on scheduled failures.

View File

@@ -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

View File

@@ -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"

View File

@@ -41,7 +41,7 @@ env:
jobs:
build:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -91,4 +91,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0

View File

@@ -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:

View File

@@ -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 }}

View File

@@ -34,7 +34,7 @@ jobs:
needs: [determine-files]
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-473fe44"
permissions:
contents: read
issues: write

View 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

View File

@@ -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'
#
@@ -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,129 +149,52 @@ 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) }}

View File

@@ -1,9 +1,8 @@
# Clippy, coverage and documentation for the Rust crates in crates/. Each runs
# as an independent job on a GitHub-hosted runner, but inside the same container
# image used to build the crates in the C++/Corrosion path, so the toolchain
# (and therefore the lints and the cargo cache) matches what production builds
# use. Coverage is the exception: it needs the nightly rustc that honours
# #[coverage(off)], which the image carries alongside the pinned stable.
# (and therefore the lints, coverage instrumentation and the cargo cache) matches
# what production builds use.
#
# Rust unit tests are deliberately NOT run here. They run as part of the C++
# build (reusable-build-test-config.yml), which already compiles the crates on a
@@ -28,7 +27,7 @@ permissions:
jobs:
clippy:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -41,14 +40,11 @@ jobs:
coverage:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Use the nightly Rust toolchain
run: rust-nightly path >>"${GITHUB_PATH}"
- name: Use cargo artifacts cache
uses: ./.github/actions/cargo-cache
@@ -70,7 +66,7 @@ jobs:
doc:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

View File

@@ -40,7 +40,7 @@ defaults:
jobs:
upload:
runs-on: ubuntu-latest
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
env:
REMOTE_NAME: ${{ inputs.remote_name }}
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}

View File

@@ -70,11 +70,6 @@ repos:
language: system
types: [rust]
pass_filenames: false # rustfmt formats the whole workspace
- id: check-coverage-attrs
name: check Rust coverage attributes
entry: ./bin/pre-commit/check_rust_coverage_attrs.py
language: python
files: ^crates/.*\.rs$
- repo: https://github.com/BlankSpruce/gersemi-pre-commit
rev: e98930bdc210d3387007f9252d8c1694ea7e410f # frozen: 0.27.7

View File

@@ -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
@@ -66,11 +62,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.

View File

@@ -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(

View File

@@ -158,7 +158,6 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
check cargo-nextest cargo nextest --version
check clippy-driver
check rust-analyzer
check rust-nightly rust-nightly run rustc --version
check rustc
check rustfmt
fi

View File

@@ -1,149 +0,0 @@
#!/usr/bin/env python3
"""
Check that Rust unit tests stay out of the coverage report.
cargo-llvm-cov instruments the test code along with everything else, so a test
module that is not excluded counts its own body as covered and inflates the
reported number. Excluding it takes two attributes:
* every `#[cfg(test)]` module carries
`#[cfg_attr(coverage_nightly, coverage(off))]`;
* every crate root (lib.rs, main.rs) carries
`#![cfg_attr(coverage_nightly, feature(coverage_attribute))]`, which the
attribute above needs in order to compile.
Both are inert outside the coverage job: cargo-llvm-cov defines
`coverage_nightly` only when it runs on a nightly toolchain.
The crate-root gate is checked even in a crate that has no tests yet, because
that is what lets the first test module added later carry the attribute without
a build failure. Missing it is a hard error, so it cannot go unnoticed; a
missing `coverage(off)` fails open, which is why this check exists.
Matching is on exact attribute text, which works because `cargo fmt` runs over
the whole workspace in the hook ahead of this one: rustfmt puts every attribute
on its own line and normalizes what is inside it, turning `#[cfg( test )]`
and `#[cfg(test,)]` alike into `#[cfg(test)]`. So there is nothing here that
parses Rust. The price is that a cfg this file does not spell out literally --
`all(test, ...)`, `any(test, ...)`, `not(test)` -- is reported rather than
classified, on the grounds that guessing at coverage semantics is how a check
like this ends up quietly wrong.
Usage: ./bin/pre-commit/check_rust_coverage_attrs.py <file1> <file2> ...
Exit status is non-zero if any violation is found.
"""
import re
import sys
from dataclasses import dataclass
from pathlib import Path
CRATE_ROOTS = {"lib.rs", "main.rs"}
FEATURE_ATTR = "#![cfg_attr(coverage_nightly, feature(coverage_attribute))]"
COVERAGE_OFF_ATTR = "#[cfg_attr(coverage_nightly, coverage(off))]"
CFG_TEST_ATTR = "#[cfg(test)]"
# Any other cfg that mentions `test`. String literals are blanked before this
# runs, so `feature = "test"` does not read as the `test` cfg.
RE_CFG_MENTIONS_TEST = re.compile(r"^#\[cfg\(.*\btest\b.*\)\]$")
RE_STRING = re.compile(r'"(?:[^"\\]|\\.)*"')
RE_MOD = re.compile(r"^(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)")
@dataclass(frozen=True)
class Finding:
line: int
label: str
message: str
def _check_module(attrs: list[str], line: int, name: str) -> list[Finding]:
"""Findings for one module, given the attributes attached to it."""
if COVERAGE_OFF_ATTR in attrs:
return [] # excluded from coverage; which cfg gates it does not matter
if CFG_TEST_ATTR in attrs:
return [
Finding(
line,
"missing-coverage-off",
f"`mod {name}` is #[cfg(test)] but not excluded from coverage; "
f"add {COVERAGE_OFF_ATTR}",
)
]
unclassified = [
attr for attr in attrs if RE_CFG_MENTIONS_TEST.match(RE_STRING.sub('""', attr))
]
if unclassified:
return [
Finding(
line,
"unclassified-cfg",
f"`mod {name}` is gated on {unclassified[0]}, which this check "
f"cannot tell apart from a module that ships in the library; "
f"add {COVERAGE_OFF_ATTR} if it is test-only, or teach this "
f"check the cfg if it is not",
)
]
return []
def _check_test_modules(lines: list[str]) -> list[Finding]:
"""Findings for every test module that is not excluded from coverage."""
findings: list[Finding] = []
attrs: list[str] = []
attrs_line = 0
for number, raw in enumerate(lines, start=1):
stripped = raw.strip()
# Blank lines and comments are allowed between an attribute and its item.
if not stripped or stripped.startswith("//"):
continue
if stripped.startswith("#["):
if not attrs:
attrs_line = number
attrs.append(stripped)
continue
module = RE_MOD.match(stripped)
if module is not None and attrs:
findings += _check_module(attrs, attrs_line, module.group(1))
attrs = []
return findings
def _check_crate_root(name: str, lines: list[str]) -> list[Finding]:
"""A finding if a crate root is missing the coverage_attribute feature gate."""
if name not in CRATE_ROOTS:
return []
if any(line.strip() == FEATURE_ATTR for line in lines):
return []
return [
Finding(
1,
"missing-feature-gate",
f"crate root is missing {FEATURE_ATTR}",
)
]
def check_source(name: str, text: str) -> list[Finding]:
"""Findings for one file's contents; `name` is its base name (lib.rs, ...)."""
lines = text.splitlines()
return _check_crate_root(name, lines) + _check_test_modules(lines)
def check_file(path: Path) -> list[Finding]:
return check_source(path.name, path.read_text(encoding="utf-8"))
def main() -> int:
total = 0
for path in (Path(name) for name in sys.argv[1:]):
for finding in check_file(path):
total += 1
print(f"{path}:{finding.line}: {finding.label}: {finding.message}")
return 1 if total else 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -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>

View File

@@ -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"

View File

@@ -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)

View File

@@ -61,8 +61,6 @@ SETTING_DEFAULTS = {
"delegable": "Delegation::NotDelegable",
"amendment": "uint256{}",
"privileges": "Privilege::NoPriv",
"emittance": "Emittance::Emitable",
"firewall": "FirewallAction::Allow",
}

View File

@@ -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",

View File

@@ -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",
]
@@ -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,7 +225,6 @@ class Xrpl(ConanFile):
"soci::soci",
"secp256k1::secp256k1",
"sqlite3::sqlite",
"wasmi::wasmi",
"xxhash::xxhash",
"zlib::zlib",
]

View File

@@ -8,9 +8,6 @@ cxx = { version = "1.0.198", features = ["c++20"] }
[workspace.package]
edition = "2024"
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(coverage)', 'cfg(coverage_nightly)' ] }
[profile.release]
opt-level = 3
overflow-checks = true

View File

@@ -8,6 +8,3 @@ crate-type = ["staticlib"]
[dependencies]
cxx.workspace = true
[lints]
workspace = true

View File

@@ -1,5 +1,3 @@
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#[cxx::bridge(namespace = "rs::hello_world")]
mod ffi {
extern "Rust" {
@@ -10,14 +8,3 @@ mod ffi {
pub fn hello_world() -> String {
"hello_world".to_string()
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn hello_world_returns_hello_world() {
assert_eq!(hello_world(), "hello_world")
}
}

View File

@@ -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"]

View File

@@ -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).

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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";

View File

@@ -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&

View File

@@ -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();

View File

@@ -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

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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,

View File

@@ -92,7 +92,7 @@ public:
void
importDatabase(Database& source) override
{
importInternal(*backend_.get(), source);
importInternal(*backend_, source);
}
void

View File

@@ -29,78 +29,6 @@ constexpr std::uint32_t kAuctionSlotIntervalDuration =
constexpr std::uint16_t kVoteMaxSlots = 8;
constexpr std::uint32_t kVoteWeightScaleFactor = 100000;
// Curve type identifiers
enum CurveType : std::uint8_t {
CtConstantProduct = 0,
CtConcentratedLiquidity = 1,
CtStableSwap = 2,
CtBinned = 3,
};
inline constexpr CurveType protocolCurveTypes[] = {
CtConstantProduct,
CtConcentratedLiquidity,
CtStableSwap,
CtBinned,
};
// Fee tier definitions for concentrated liquidity
enum FeeTier : std::uint8_t {
FtStable = 0, // 1 bp, tick spacing 1
FtLow = 1, // 5 bp, tick spacing 10
FtMedium = 2, // 30 bp, tick spacing 60
FtHigh = 3, // 100 bp, tick spacing 200
};
inline constexpr std::uint16_t feeTierToFee[] = {1, 5, 30, 100};
inline constexpr std::int32_t feeTierToTickSpacing[] = {1, 10, 60, 200};
inline constexpr std::uint8_t feeTierCount = 4;
// Tick bounds
inline constexpr std::int32_t minTick = -887272;
inline constexpr std::int32_t maxTick = 887272;
// Offset-binary scaling applied wherever a tick is hashed or bit-packed
// to keep arithmetic in unsigned domain (avoids signed-division surprises
// near zero). Used by:
// - keylet::ammTick (offset-encoded into the low 64 keylet bits)
// - keylet::ammTickBitmapWord (wordIndex = (tick + offset) >> 8)
// - Validthe AMM bitmap-consistency invariant
// One constant, one source of fragility — DO NOT duplicate inline.
inline constexpr std::uint32_t kTickBitmapOffset =
static_cast<std::uint32_t>(-static_cast<std::int64_t>(minTick));
// StableSwap limits
inline constexpr std::uint32_t minAmplification = 1;
inline constexpr std::uint32_t maxAmplification = 5000;
inline constexpr std::uint32_t maxAmpChangePct = 10;
inline constexpr std::uint32_t ampRampDuration = 86400;
// Binned-curve limits. Bin step in basis points; bin price grows as
// (1 + binStep/10000)^binID. Range bound keeps state size finite and
// price range close to v3's effective range at default tick spacing.
inline constexpr std::int32_t minBinID = -221818;
inline constexpr std::int32_t maxBinID = 221818;
inline constexpr std::uint16_t validBinSteps[] = {1, 5, 10, 25, 100};
inline constexpr std::uint8_t binStepCount = 5;
// Newton's method convergence
inline constexpr int newtonMaxIterations = 256;
// Concentrated-liquidity per-swap tick-crossing cap. Bounds the
// per-swap work done by the curve's iterative tick traversal (each
// crossing does one SHAMap lookup for the next initialised tick + one
// SLE read). With FtStable's tickSpacing=1, 1000 crossings = ~10%
// price range; with FtMedium's tickSpacing=60, 1000 crossings spans
// the equivalent of a ~4x price move — both comfortably larger than
// any reasonable swap requires. Hitting the cap produces a silent
// partial fill: the curve returns the output realized over the first
// `maxTickCrossings` boundaries, and the caller infers the cap from
// the (smaller-than-requested) result. Uniswap v3 has no protocol
// cap (only block gas); we cap here because XRPL has no metered
// execution and the cap is the only fairness bound on per-swap work.
inline constexpr int maxTickCrossings = 1000;
class STObject;
class STAmount;
class Rules;
@@ -109,20 +37,13 @@ class Rules;
* Calculate Liquidity Provider Token (LPT) Currency.
*/
Currency
ammLPTCurrency(
Asset const& asset1,
Asset const& asset2,
std::uint8_t curveType = CtConstantProduct);
ammLPTCurrency(Asset const& asset1, Asset const& asset2);
/**
* Calculate LPT Issue from AMM asset pair.
*/
Issue
ammLPTIssue(
Asset const& asset1,
Asset const& asset2,
AccountID const& ammAccountID,
std::uint8_t curveType = CtConstantProduct);
ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID);
/**
* Validate the amount.

View File

@@ -16,7 +16,6 @@
#include <cstdint>
#include <limits>
#include <optional>
#include <vector>
namespace xrpl {
@@ -434,88 +433,4 @@ verifyConvertBackProof(
uint64_t amount,
uint256 const& contextHash);
/**
* @brief Generates the context hash for a BallotCastVote transaction.
*
* Binds the cast's range proof to this specific transaction, preventing
* proof reuse across ballots or accounts.
*
* @param account The voter's account ID.
* @param ballotID The target ballot's ledger index.
* @param sequence The transaction sequence number or ticket number.
* @return A 256-bit context hash unique to this cast.
*/
uint256
getBallotCastContextHash(AccountID const& account, uint256 const& ballotID, std::uint32_t sequence);
/**
* @brief Generates the context hash for a BallotFinalize transaction.
*
* Binds each per-option decryption-correctness proof to this specific
* finalize transaction.
*
* @param account The tally authority's account ID.
* @param ballotID The target ballot's ledger index.
* @param sequence The transaction sequence number or ticket number.
* @return A 256-bit context hash unique to this finalize.
*/
uint256
getBallotFinalizeContextHash(
AccountID const& account,
uint256 const& ballotID,
std::uint32_t sequence);
/**
* @brief Verifies an aggregated Bulletproof range proof over ballot option
* commitments.
*
* Proves that every one of the N per-option values committed in
* @p commitments lies in the non-negative range, so a vote cannot subtract
* weight from a disliked option. Thin wrapper over
* mpt_verify_aggregated_bulletproof.
*
* @param proof The serialized aggregated Bulletproof.
* @param commitments One 33-byte Pedersen commitment per option.
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
*/
TER
verifyBallotRangeProof(
Slice const& proof,
std::vector<Slice> const& commitments,
uint256 const& contextHash);
/**
* @brief Verifies the ciphertext-commitment linkage for one ballot option.
*
* Proves that the option's ElGamal ciphertext(s) encrypt the same value that
* its Pedersen commitment commits to, and that every mirror ciphertext (tally,
* optional auditor, optional voter) encrypts that same value under shared
* randomness. Combined with the aggregated range proof over the commitment,
* this pins the tally update to a non-negative value the voter cannot forge —
* the verifiable-encryption guarantee a vote needs because it encrypts under
* the tally key, which the voter does not own.
*
* Wraps secp256k1_compact_standard_verify. The balance-linkage terms of that
* relation are neutralized with a canonical witness (sk_A = 1, rho_b = 1), so
* pk_A = G, PC_b = H and B1 = B2 = G are reconstructed identically here and by
* the prover, and only the 192-byte proof is carried on the wire.
*
* @param pubKeys The n mirror public keys, tally key first (33 bytes each).
* @param c1 The shared ElGamal C1 component (33 bytes).
* @param c2PerKey The n ElGamal C2 components, one per mirror key (33 bytes).
* @param commitment The option's Pedersen commitment PC_m (33 bytes).
* @param proof The 192-byte compact sigma linkage proof.
* @param contextHash The 256-bit context hash binding the proof.
* @return tesSUCCESS if the linkage holds, or an error code otherwise.
*/
TER
verifyBallotVoteLinkage(
std::vector<Slice> const& pubKeys,
Slice const& c1,
std::vector<Slice> const& c2PerKey,
Slice const& commitment,
Slice const& proof,
uint256 const& contextHash);
} // namespace xrpl

View File

@@ -1,77 +0,0 @@
#pragma once
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/TxSettings.h>
#include <optional>
#include <string>
#include <unordered_map>
namespace xrpl {
/**
* We have both transaction type emitables and granular type emitables.
* Since we will reuse the TransactionFormats to parse the Transaction
* Emitables, only the GranularEmitableType is defined here. To prevent
* conflicts with TxType, the GranularEmitableType is always set to a value
* greater than the maximum value of uint16.
*/
enum GranularEmitableType : std::uint32_t {
#pragma push_macro("EMITABLE")
#undef EMITABLE
#define EMITABLE(type, txType, value) type = value,
#include <xrpl/protocol/detail/emitable.macro>
#undef EMITABLE
#pragma pop_macro("EMITABLE")
};
class Emitable
{
private:
Emitable();
std::unordered_map<std::uint16_t, Emittance> emitableTx_;
std::unordered_map<std::string, GranularEmitableType> granularEmitableMap_;
std::unordered_map<GranularEmitableType, std::string> granularNameMap_;
std::unordered_map<GranularEmitableType, TxType> granularTxTypeMap_;
public:
static Emitable const&
getInstance();
Emitable(Emitable const&) = delete;
Emitable&
operator=(Emitable const&) = delete;
std::optional<std::string>
getEmitableName(std::uint32_t const value) const;
std::optional<std::uint32_t>
getGranularValue(std::string const& name) const;
std::optional<std::string>
getGranularName(GranularEmitableType const& value) const;
std::optional<TxType>
getGranularTxType(GranularEmitableType const& gpType) const;
bool
isEmitable(std::uint32_t const& emitableValue) const;
// for tx level emitable, emitable value is equal to tx type plus one
uint32_t
txToEmitableType(TxType const& type) const;
// tx type value is emitable value minus one
TxType
emitableToTxType(uint32_t const& value) const;
};
} // namespace xrpl

View File

@@ -10,15 +10,6 @@ namespace xrpl {
// This was the reference fee units used in the old fee calculation.
inline constexpr std::uint32_t kFeeUnitsDeprecated = 10;
// Number of micro-drops in one drop.
constexpr std::uint32_t microDropsPerDrop{1'000'000};
/**
* Maximum Feature Extension fee settings.
*/
inline constexpr std::uint32_t kMaxGasLimit{2'000'000};
inline constexpr std::uint32_t kMaxBytecodeSizeLimit{200'000};
/**
* Reflects the fee settings for a particular ledger.
*
@@ -42,21 +33,6 @@ struct Fees
*/
XRPAmount increment{0};
/**
* @brief Gas limit for Feature Extensions (instructions).
*/
std::uint32_t gasLimit{0};
/**
* @brief Bytecode size limit for Feature Extensions (bytes).
*/
std::uint32_t bytecodeSizeLimit{0};
/**
* @brief Price of WASM gas (micro-drops).
*/
std::uint32_t gasPrice{0};
explicit Fees() = default;
Fees(Fees const&) = default;
Fees&

View File

@@ -1,17 +0,0 @@
#pragma once
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/TxSettings.h>
namespace xrpl {
/**
* How an account's firewall treats a transaction of the given type.
*
* The classification is declared per transaction in transactions.macro. A type
* the switch does not name, which means a deprecated one, is allowed.
*/
[[nodiscard]] FirewallAction
firewallAction(TxType txType) noexcept;
} // namespace xrpl

View File

@@ -15,43 +15,13 @@
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/UintTypes.h>
#include <boost/endian/conversion.hpp>
#include <array>
#include <cstdint>
#include <cstring>
#include <functional>
#include <set>
#include <utility>
namespace xrpl {
class SeqProxy;
// Structured-key helpers: read/write the low 64 bits of a uint256 keylet
// in big-endian byte order. Used by every AMM keylet scheme that
// embeds an ordered subkey (tick index, bitmap-word index, bin ID) into
// the low 64 bits so SHAMap range walks visit entries in subkey order.
//
// We type-pun via std::memcpy rather than a reinterpret_cast through
// uint64_t* — the latter violates strict aliasing and has no alignment
// guarantee on base_uint's underlying byte storage. memcpy of an
// 8-byte value compiles to a single load/store on x86_64 / ARM64 under
// any optimization level, so the safer idiom is free at runtime.
inline void
setLow64BE(uint256& key, std::uint64_t value) noexcept
{
auto const be = boost::endian::native_to_big(value);
std::memcpy(key.end() - sizeof(std::uint64_t), &be, sizeof(std::uint64_t));
}
[[nodiscard]] inline std::uint64_t
getLow64BE(uint256 const& key) noexcept
{
std::uint64_t be;
std::memcpy(&be, key.end() - sizeof(std::uint64_t), sizeof(std::uint64_t));
return boost::endian::big_to_native(be);
}
/**
* Keylet computation functions.
*
@@ -136,9 +106,7 @@ book(Book const& b);
* BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line
* between them.
*/
/**
* @{.
*/
/** @{ */
Keylet
trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept;
@@ -147,16 +115,12 @@ trustLine(AccountID const& id, Issue const& issue) noexcept
{
return trustLine(id, issue.account, issue.currency);
}
/**
* @}.
*/
/** @} */
/**
* An offer from an account
*/
/**
* @{.
*/
/** @{ */
Keylet
offer(AccountID const& id, SeqProxy const& seq) noexcept;
@@ -165,9 +129,7 @@ offer(uint256 const& key) noexcept
{
return {ltOFFER, key};
}
/**
* @}.
*/
/** @} */
/**
* The initial directory page for a specific quality
@@ -184,9 +146,7 @@ next(Keylet const& k);
/**
* A ticket belonging to an account
*/
/**
* @{.
*/
/** @{ */
Keylet
ticket(AccountID const& id, SeqProxy const& ticketSeq);
@@ -195,9 +155,7 @@ ticket(uint256 const& key)
{
return {ltTICKET, key};
}
/**
* @}.
*/
/** @} */
/**
* A SignerList
@@ -211,18 +169,10 @@ signerList(AccountID const& account) noexcept;
Keylet
sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept;
/**
* An account's beneficiary designation. One per account.
*/
Keylet
beneficiary(AccountID const& account) noexcept;
/**
* A Check
*/
/**
* @{.
*/
/** @{ */
Keylet
check(AccountID const& id, SeqProxy const& seq) noexcept;
@@ -231,16 +181,12 @@ check(uint256 const& key) noexcept
{
return {ltCHECK, key};
}
/**
* @}.
*/
/** @} */
/**
* A DepositPreauth
*/
/**
* @{.
*/
/** @{ */
Keylet
depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept;
@@ -254,9 +200,7 @@ depositPreauth(uint256 const& key) noexcept
{
return {ltDEPOSIT_PREAUTH, key};
}
/**
* @}.
*/
/** @} */
//------------------------------------------------------------------------------
@@ -275,9 +219,7 @@ ownerDir(AccountID const& id) noexcept;
/**
* A page in a directory
*/
/**
* @{.
*/
/** @{ */
Keylet
page(uint256 const& root, std::uint64_t const index = 0) noexcept;
@@ -287,9 +229,7 @@ page(Keylet const& root, std::uint64_t const index = 0) noexcept
XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::keylet::page : valid root type");
return page(root.key, index);
}
/**
* @}.
*/
/** @} */
/**
* An escrow entry
@@ -297,12 +237,6 @@ page(Keylet const& root, std::uint64_t const index = 0) noexcept
Keylet
escrow(AccountID const& src, SeqProxy const& seq) noexcept;
inline Keylet
escrow(uint256 const& key) noexcept
{
return {ltESCROW, key};
}
/**
* A PaymentChannel
*/
@@ -317,9 +251,7 @@ payChannel(AccountID const& src, AccountID const& dst, SeqProxy const& seq) noex
* 160-bit AccountID, followed by a 96-bit value that determines which NFT
* tokens are candidates for that page.
*/
/**
* @{.
*/
/** @{ */
/**
* A keylet for the owner's first possible NFT page.
*/
@@ -334,9 +266,7 @@ nftokenPageMax(AccountID const& owner);
Keylet
nftokenPage(Keylet const& k, uint256 const& token);
/**
* @}.
*/
/** @} */
/**
* An offer from an account to buy or sell an NFT
@@ -366,7 +296,7 @@ nftSells(uint256 const& id) noexcept;
* AMM entry
*/
Keylet
amm(Asset const& issue1, Asset const& issue2, std::uint8_t curveType = 0) noexcept;
amm(Asset const& issue1, Asset const& issue2) noexcept;
Keylet
amm(uint256 const& amm) noexcept;
@@ -412,15 +342,6 @@ mptokenIssuance(uint256 const& issuanceKey)
return {ltMPTOKEN_ISSUANCE, issuanceKey};
}
Keylet
tokenIssuance(AccountID const& issuer, Currency const& currency) noexcept;
inline Keylet
tokenIssuance(uint256 const& key)
{
return {ltTOKEN_ISSUANCE, key};
}
Keylet
mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept;
@@ -445,45 +366,6 @@ vault(uint256 const& vaultKey)
Keylet
loanBroker(AccountID const& owner, SeqProxy const& seq) noexcept;
/**
* A repurchase agreement, keyed by the seller and the creating sequence.
*/
Keylet
repo(AccountID const& seller, SeqProxy const& seq) noexcept;
inline Keylet
repo(uint256 const& repoID)
{
return {ltREPO, repoID};
}
/**
* A firewall, keyed by the account it protects.
*/
Keylet
firewall(AccountID const& account) noexcept;
inline Keylet
firewall(uint256 const& firewallID)
{
return {ltFIREWALL, firewallID};
}
/**
* A withdraw preauthorization, keyed by owner, authorized account and tag.
*/
Keylet
withdrawPreauth(
AccountID const& owner,
AccountID const& preauthorized,
std::uint32_t dtag) noexcept;
inline Keylet
withdrawPreauth(uint256 const& key)
{
return {ltWITHDRAW_PREAUTH, key};
}
inline Keylet
loanBroker(uint256 const& key)
{
@@ -499,174 +381,11 @@ loan(uint256 const& key)
return {ltLOAN, key};
}
Keylet
couponSchedule(uint192 const& mptIssuanceID) noexcept;
inline Keylet
couponSchedule(uint256 const& scheduleKey)
{
return {ltCOUPON_SCHEDULE, scheduleKey};
}
Keylet
permissionedDomain(AccountID const& account, SeqProxy const& seq) noexcept;
Keylet
permissionedDomain(uint256 const& domainID) noexcept;
Keylet
contractSource(uint256 const& contractHash) noexcept;
Keylet
contract(uint256 const& contractHash, AccountID const& owner, std::uint32_t seq) noexcept;
inline Keylet
contract(uint256 const& contractID)
{
return {ltCONTRACT, contractID};
}
Keylet
contractData(AccountID const& owner, AccountID const& contractAccount) noexcept;
Keylet
passkeyList(AccountID const& account) noexcept;
/**
* A ballot owned by `owner`, keyed by the creating transaction sequence.
*/
Keylet
ballot(AccountID const& owner, std::uint32_t seq) noexcept;
inline Keylet
ballot(uint256 const& ballotID)
{
return {ltBALLOT, ballotID};
}
/**
* A voter's cast on the ballot identified by `ballotID`.
*/
Keylet
ballotVote(uint256 const& ballotID, AccountID const& voter) noexcept;
inline Keylet
ballotVote(uint256 const& key)
{
return {ltBALLOT_VOTE, key};
}
/**
* A concentrated liquidity AMM position.
*/
Keylet
ammPosition(uint256 const& ammID, AccountID const& owner, std::uint32_t seq) noexcept;
inline Keylet
ammPosition(uint256 const& key)
{
return {ltAMM_POSITION, key};
}
/**
* A concentrated liquidity AMM tick.
* Uses structured (non-hashed) keys for ordered SHAMap traversal.
* High 192 bits: pool scope (from ammID hash).
* Low 64 bits: encoded tick index (offset binary, big-endian).
*/
Keylet
ammTick(uint256 const& ammID, std::int32_t tickIndex) noexcept;
inline Keylet
ammTick(uint256 const& key)
{
return {ltAMM_TICK, key};
}
/**
* Base key for a CL pool's tick range (low 64 bits zeroed).
*/
Keylet
ammTickBase(uint256 const& ammID) noexcept;
/**
* End key for a CL pool's tick range (low 64 bits all 1s).
*/
Keylet
ammTickEnd(uint256 const& ammID) noexcept;
/**
* A 256-tick presence bitmap window for a CL pool.
* Keylet structure mirrors `ammTick`: high 192 bits derive from a pool-scoped
* hash, low 64 bits encode the word index (big-endian) so range walks via
* SHAMap succ/pred yield the next-higher / next-lower word.
*/
Keylet
ammTickBitmapWord(uint256 const& ammID, std::uint16_t wordIndex) noexcept;
inline Keylet
ammTickBitmapWord(uint256 const& key)
{
return {ltAMM_TICK_BITMAP, key};
}
/**
* Base key for a CL pool's tick-bitmap range (low 64 bits zeroed).
*/
Keylet
ammTickBitmapBase(uint256 const& ammID) noexcept;
/**
* End key for a CL pool's tick-bitmap range (low 64 bits all 1s).
*/
Keylet
ammTickBitmapEnd(uint256 const& ammID) noexcept;
/**
* A single bin within a CtBinned AMM pool. Bins are keyed by signed
* bin ID, offset-encoded into the low 64 bits of the keylet so SHAMap
* range walks yield consecutive bins in price order.
*/
Keylet
ammBin(uint256 const& ammID, std::int32_t binID) noexcept;
/**
* Lookup a bin SLE by its raw key (used by transactors that have a
* stored issuance / bin reference).
*/
Keylet
ammBin(uint256 const& key) noexcept;
/**
* Base / end keys for a binned AMM's bin-SLE range. Bins for the
* same AMM are contiguous in SHAMap order (high 192 bits are an
* ammID-scoped hash; low 64 bits offset-encode the bin ID), so
* `view.succ(bin_at(binID).key, ammBinEnd(ammID).key)` jumps to the
* next populated bin in O(log n) regardless of gap size.
*/
Keylet
ammBinBase(uint256 const& ammID) noexcept;
Keylet
ammBinEnd(uint256 const& ammID) noexcept;
/**
* A single LP's holding record in a single bin. Phase 5 will replace
* this with a fungible MPT issuance per bin.
*/
Keylet
ammBinHolding(uint256 const& ammID, AccountID const& owner, std::int32_t binID) noexcept;
Keylet
ammBinHolding(uint256 const& key) noexcept;
Keylet
subscription(AccountID const& account, AccountID const& dest, std::uint32_t seq) noexcept;
inline Keylet
subscription(uint256 const& key) noexcept
{
return {ltSUBSCRIPTION, key};
}
} // namespace keylet
// Everything below is deprecated and should be removed in favor of keylets:

View File

@@ -8,8 +8,6 @@ namespace xrpl {
enum class KeyType {
Secp256k1 = 0,
Ed25519 = 1,
Dilithium = 2,
P256 = 3,
};
inline std::optional<KeyType>
@@ -21,12 +19,6 @@ keyTypeFromString(std::string const& s)
if (s == "ed25519")
return KeyType::Ed25519;
if (s == "dilithium")
return KeyType::Dilithium;
if (s == "p256")
return KeyType::P256;
return {};
}
@@ -39,12 +31,6 @@ to_string(KeyType type)
if (type == KeyType::Ed25519)
return "ed25519";
if (type == KeyType::Dilithium)
return "dilithium";
if (type == KeyType::P256)
return "p256";
return "INVALID";
}

View File

@@ -85,6 +85,15 @@ enum LedgerEntryType : std::uint16_t {
*/
ltNICKNAME [[deprecated("This object type is not supported and should not be used.")]] = 0x006e,
/**
* A legacy, deprecated type.
*
* @deprecated **This object type is not supported and should not be used.**
* Support for this type of object was never implemented.
* No objects of this type were ever created.
*/
ltCONTRACT [[deprecated("This object type is not supported and should not be used.")]] = 0x0063,
/**
* A legacy, deprecated type.
*
@@ -145,8 +154,7 @@ enum LedgerEntryType : std::uint16_t {
LEDGER_OBJECT(Offer, \
LSF_FLAG(lsfPassive, 0x00010000) \
LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \
LSF_FLAG(lsfHybrid, 0x00040000) /* True, offer is hybrid. */ \
LSF_FLAG(lsfAllOrNone, 0x00080000)) /* True, offer is all-or-none. */ \
LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \
\
LEDGER_OBJECT(RippleState, \
LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \
@@ -180,8 +188,7 @@ enum LedgerEntryType : std::uint16_t {
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
LSF_FLAG(lsfMPTCanClawback, 0x00000040) \
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080) \
LSF_FLAG(lsfMPTCouponSchedule, 0x00000100)) \
LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \
\
LEDGER_OBJECT(MPToken, \
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
@@ -201,20 +208,7 @@ enum LedgerEntryType : std::uint16_t {
\
LEDGER_OBJECT(Sponsorship, \
LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) \
\
LEDGER_OBJECT(TokenIssuance, \
LSF_FLAG(lsfTokenLocked, 0x00000001) /* True, per-currency global freeze */ \
LSF_FLAG(lsfTokenCannotLock, 0x00000002) /* True, issuer renounced the per-currency lock */ \
LSF_FLAG(lsfTokenWrapped, 0x00000004) /* True, owned by a pseudo-account (blackholed issuer) */ \
LSF_FLAG(lsfTokenVerifiedSupply, 0x00000008)) /* True, IssuedAmount reflects verified legacy supply */ \
\
LEDGER_OBJECT(Ballot, \
LSF_FLAG(lsfBallotFinalized, 0x00000001) /* True, results have been published */ \
LSF_FLAG(lsfVoterRecoverable, 0x00000002)) /* True, casts carry a voter self-recovery vector */ \
\
LEDGER_OBJECT(Subscription, \
LSF_FLAG(lsfSingleUse, 0x00010000)) /* True, delete on first successful claim */
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000))
// clang-format on

View File

@@ -5,9 +5,11 @@
#include <xrpl/protocol/Concepts.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <variant>
namespace xrpl {
@@ -121,9 +123,32 @@ operator==(PathAsset const& lhs, PathAsset const& rhs)
template <typename Hasher>
void
hash_append(Hasher& h, PathAsset const& pathAsset)
hash_append(Hasher& h, PathAsset const& pathAsset) noexcept
{
std::visit([&]<ValidPathAsset T>(T const& e) { hash_append(h, e); }, pathAsset.value());
using beast::hash_append;
using Variant = std::remove_cvref_t<decltype(pathAsset.value())>;
static_assert(
std::variant_size_v<Variant> < 0xFFu,
"PathAsset's discriminant must fit in a byte, leaving 0xFF reserved.");
// std::visit is not noexcept: it throws bad_variant_access when the variant
// is valueless_by_exception.
if (pathAsset.value().valueless_by_exception()) [[unlikely]]
{
hash_append(h, static_cast<std::uint8_t>(0xFFu));
return;
}
hash_append(h, static_cast<std::uint8_t>(pathAsset.value().index()));
std::visit(
[&]<ValidPathAsset T>(T const& e) noexcept {
static_assert(
noexcept(hash_append(h, e)),
"Every PathAsset alternative must be nothrow-hashable.");
hash_append(h, e);
},
pathAsset.value());
}
inline bool

View File

@@ -1,20 +1,10 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/MPTAmount.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
namespace xrpl {
inline void
@@ -25,70 +15,4 @@ serializePayChanAuthorization(Serializer& msg, uint256 const& key, XRPAmount con
msg.add64(amt.drops());
}
inline void
serializePayChanAuthorization(
Serializer& msg,
uint256 const& key,
IOUAmount const& amt,
Currency const& cur,
AccountID const& iss)
{
msg.add32(HashPrefix::PaymentChannelClaim);
msg.addBitString(key);
if (amt == beast::kZero)
{
msg.add64(STAmount::kIssuedCurrency);
}
else if (amt.signum() == -1)
{ // 512 = not native; the sign is encoded by omitting the 256 flag, so
// the mantissa must be serialized as its absolute value
msg.add64(
static_cast<std::uint64_t>(-amt.mantissa()) |
(static_cast<std::uint64_t>(amt.exponent() + 512 + 97) << (64 - 10)));
}
else
{ // 256 = positive
msg.add64(
amt.mantissa() |
(static_cast<std::uint64_t>(amt.exponent() + 512 + 256 + 97) << (64 - 10)));
}
msg.addBitString(cur);
msg.addBitString(iss);
}
inline void
serializePayChanAuthorization(
Serializer& msg,
uint256 const& key,
MPTAmount const& amt,
MPTID const& mptID,
AccountID const& iss)
{
msg.add32(HashPrefix::PaymentChannelClaim);
msg.addBitString(key);
msg.add64(amt.value());
msg.addBitString(mptID);
msg.addBitString(iss);
}
inline void
serializePayChanAuthorization(Serializer& msg, uint256 const& key, STAmount const& amt)
{
if (amt.native())
{
serializePayChanAuthorization(msg, key, amt.xrp());
}
else if (amt.holds<Issue>())
{
serializePayChanAuthorization(
msg, key, amt.iou(), amt.get<Issue>().currency, amt.get<Issue>().account);
}
else if (amt.holds<MPTIssue>())
{
auto const& mpt = amt.get<MPTIssue>();
auto const& mptID = mpt.getMptID();
serializePayChanAuthorization(msg, key, amt.mpt(), mptID, amt.getIssuer());
}
}
} // namespace xrpl

View File

@@ -12,7 +12,6 @@
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <limits>
namespace xrpl {
@@ -141,13 +140,6 @@ tenthBipsOfValue(T value, TenthBips<TBips> bips)
return value * bips.value() / kTenthBipsPerUnity.value();
}
/**
* The longest inactivity period a beneficiary designation may require, ten
* years in seconds. Long enough for the intended use and short enough that the
* value still means something.
*/
constexpr std::uint32_t kMaxBeneficiaryTimeLock = 10 * 365 * 24 * 60 * 60;
namespace lending {
/**
* The maximum management fee rate allowed by a loan broker in 1/10 bips.
@@ -309,29 +301,11 @@ static_assert(Number::kMaxRep >= kMaxMpTokenAmount);
*/
constexpr std::size_t kMaxDataPayloadLength = 256;
/**
* The maximum length of a structured-data Schema
*/
constexpr std::size_t kMaxSchemaLength = 256;
/**
* Vault withdrawal policies
*/
constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1;
/**
* Vault interest recognition methods.
*
* Legacy recognizes a loan's whole-life interest at origination and is the
* implicit method for vaults created before featureLendingProtocolV1_1. Cash
* recognizes interest as it is collected; accrual recognizes it continuously
* as it is earned. Fixed at VaultCreate — changing it would reprice every
* outstanding share in a single step.
*/
constexpr std::uint8_t kVaultAccountingLegacy = 0;
constexpr std::uint8_t kVaultAccountingCash = 1;
constexpr std::uint8_t kVaultAccountingAccrual = 2;
/**
* Default IOU scale factor for a Vault
*/
@@ -341,13 +315,7 @@ constexpr std::uint8_t kVaultDefaultIouScale = 6;
* 1 IOU can be always converted to shares.
* 10^19 > maxMPTokenAmount (2^64-1) > 10^18
*/
constexpr std::uint8_t kVaultMaximumIouScale =
18; /** Largest deposit or redemption fee a vault may charge, in 1/10 bips.
Matches kMaxTransferFee: half of what is moved is the most any fee may
retain.
*/
constexpr std::uint32_t kMaxVaultFee = 50'000;
constexpr std::uint8_t kVaultMaximumIouScale = 18;
/**
* Vault ledger-entry schema versions. Assigned to newly created
@@ -361,16 +329,12 @@ enum class VaultVersion : uint8_t {
};
/**
* Vault kind. Distinguishes closed-ended and rolling vaults from the default
* open-ended kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
*
* A rolling vault deals in a window that reopens every sfDealingInterval
* seconds and stays open for sfDealingWindow of them.
* Vault kind. Distinguishes closed-ended vaults from the default open-ended
* kind. Persisted as sfVaultKind (UINT8); absent means OpenEnded.
*/
enum class VaultKind : std::uint8_t {
OpenEnded = 0,
ClosedEnded = 1,
Rolling = 2,
};
/**
@@ -411,30 +375,6 @@ constexpr std::uint32_t kMaxInvestmentPeriod = std::chrono::seconds{std::chrono:
*/
constexpr std::uint8_t kMaxAssetCheckDepth = 5;
/**
* Maximum length of a Data field in Escrow object that can be updated by WASM code.
*/
constexpr std::size_t kMaxWasmDataLength = 1 * 1024; // 1KB
/**
* Maximum amount of data transfer across hostfunction<->wasm border.
*/
constexpr std::size_t kWasmTransferLimit = 1 << 20; // 1MB
/**
* Maximum MaximumAmount of a TokenIssuance, in base units (10^15). Bounded
* to what Number/STAmount arithmetic represents exactly, so the supply-cap
* comparison can never be affected by mantissa rounding.
*/
constexpr std::uint64_t kMaxTokenIssuanceAmount = 1'000'000'000'000'000ull;
static_assert(kMaxTokenIssuanceAmount <= kMaxMpTokenAmount);
/**
* Maximum TokenScale of a TokenIssuance: 10^scale must stay in exact int64
* range. 10^19 > 2^63-1 > 10^18
*/
constexpr std::uint8_t kMaxTokenIssuanceScale = 18;
/**
* A ledger index.
*/
@@ -604,11 +544,6 @@ constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_
*/
constexpr std::uint32_t kConfidentialFeeMultiplier = 9;
/**
* Maximum value a confidential MPT key epoch may reach.
*/
constexpr std::uint32_t kMaxKeyEpoch = std::numeric_limits<std::uint32_t>::max();
/**
* Compressed EC point prefix for even y-coordinate
*/

View File

@@ -36,12 +36,10 @@ namespace xrpl {
* information needed to determine the cryptosystem
* parameters used is stored inside the key.
*
* As of this writing three systems are supported:
* As of this writing two systems are supported:
*
* secp256k1
* ed25519
* dilithium
* p256
*
* secp256k1 public keys consist of a 33 byte
* compressed public key, with the lead byte equal
@@ -50,19 +48,14 @@ namespace xrpl {
* The ed25519 public keys consist of a 1 byte
* prefix constant 0xED, followed by 32 bytes of
* public key data.
*
* The dilithium public keys will have their own specific format.
*/
class PublicKey
{
protected:
// Minimum / standard public key size (secp256k1, ed25519).
// All the constructed public keys are valid, non-empty and contain 33
// bytes of data.
static constexpr std::size_t kSize = 33;
// Buffer sized for the largest supported key (dilithium = 1312 bytes;
// uncompressed p256 = 65 bytes). Actual length is tracked in size_.
static constexpr std::size_t kMaxSize = 1312;
std::uint8_t buf_[kMaxSize]{};
std::size_t size_ = 0;
std::uint8_t buf_[kSize]{}; // should be large enough
public:
using const_iterator = std::uint8_t const*;
@@ -88,10 +81,10 @@ public:
return buf_;
}
[[nodiscard]] std::size_t
size() const noexcept
static std::size_t
size() noexcept
{
return size_;
return kSize;
}
[[nodiscard]] const_iterator
@@ -109,19 +102,19 @@ public:
[[nodiscard]] const_iterator
end() const noexcept
{
return buf_ + size_;
return buf_ + kSize;
}
[[nodiscard]] const_iterator
cend() const noexcept
{
return buf_ + size_;
return buf_ + kSize;
}
[[nodiscard]] Slice
slice() const noexcept
{
return {buf_, size_};
return {buf_, kSize};
}
operator Slice() const noexcept
@@ -139,7 +132,7 @@ operator<<(std::ostream& os, PublicKey const& pk);
inline bool
operator==(PublicKey const& lhs, PublicKey const& rhs)
{
return lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
return std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0;
}
inline bool

View File

@@ -34,9 +34,6 @@ class STNumber;
class STXChainBridge;
class STVector256;
class STCurrency;
class STData;
class STDataType;
class STJson;
// NOLINTBEGIN(readability-identifier-naming)
#pragma push_macro("XMACRO")
@@ -76,9 +73,6 @@ class STJson;
STYPE(STI_ISSUE, 24) \
STYPE(STI_XCHAIN_BRIDGE, 25) \
STYPE(STI_CURRENCY, 26) \
STYPE(STI_DATA, 27) \
STYPE(STI_DATATYPE, 28) \
STYPE(STI_JSON, 29) \
\
/* high-level types */ \
/* cannot be serialized inside other types */ \
@@ -365,9 +359,6 @@ using SF_NUMBER = TypedField<STNumber>;
using SF_VL = TypedField<STBlob>;
using SF_VECTOR256 = TypedField<STVector256>;
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
using SF_DATA = TypedField<STData>;
using SF_DATATYPE = TypedField<STDataType>;
using SF_JSON = TypedField<STJson>;
//------------------------------------------------------------------------------

View File

@@ -1,289 +0,0 @@
#pragma once
#include <xrpl/basics/Buffer.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/detail/STVar.h>
#include <cstdint>
#include <string>
#include <vector>
namespace xrpl {
class STData final : public STBase
{
private:
using data_type = detail::STVar;
std::uint16_t inner_type_;
data_type data_;
bool default_{true};
public:
using value_type = STData; // Although not directly holding a single value
STData(SField const& n);
STData(SField const& n, unsigned char);
STData(SField const& n, std::uint16_t);
STData(SField const& n, std::uint32_t);
STData(SField const& n, std::uint64_t);
STData(SField const& n, uint128 const&);
STData(SField const& n, uint160 const&);
STData(SField const& n, uint192 const&);
STData(SField const& n, uint256 const&);
STData(SField const& n, Blob const&);
STData(SField const& n, Slice const&);
STData(SField const& n, AccountID const&);
STData(SField const& n, STAmount const&);
STData(SField const& n, STIssue const&);
STData(SField const& n, STCurrency const&);
STData(SField const& n, STNumber const&);
STData(SerialIter& sit, SField const& name);
std::size_t
size() const;
SerializedTypeID
getSType() const override;
std::string
getInnerTypeString() const;
std::string
getText() const override;
json::Value getJson(JsonOptions) const override;
void
add(Serializer& s) const override;
bool
isEquivalent(STBase const& t) const override;
bool
isDefault() const override;
SerializedTypeID
getInnerSType() const noexcept;
STBase*
makeFieldPresent();
void
setFieldU8(unsigned char);
void
setFieldU16(std::uint16_t);
void
setFieldU32(std::uint32_t);
void
setFieldU64(std::uint64_t);
void
setFieldH128(uint128 const&);
void
setFieldH160(uint160 const&);
void
setFieldH192(uint192 const&);
void
setFieldH256(uint256 const&);
void
setFieldVL(Blob const&);
void
setFieldVL(Slice const&);
void
setAccountID(AccountID const&);
void
setFieldAmount(STAmount const&);
void
setIssue(STIssue const&);
void
setCurrency(STCurrency const&);
void
setFieldNumber(STNumber const&);
unsigned char
getFieldU8() const;
std::uint16_t
getFieldU16() const;
std::uint32_t
getFieldU32() const;
std::uint64_t
getFieldU64() const;
uint128
getFieldH128() const;
uint160
getFieldH160() const;
uint192
getFieldH192() const;
uint256
getFieldH256() const;
Blob
getFieldVL() const;
AccountID
getAccountID() const;
STAmount const&
getFieldAmount() const;
STIssue
getFieldIssue() const;
STCurrency
getFieldCurrency() const;
STNumber
getFieldNumber() const;
private:
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend class detail::STVar;
// Implementation for getting (most) fields that return by value.
//
// The remove_cv and remove_reference are necessitated by the STBitString
// types. Their value() returns by const ref. We return those types
// by value.
template <
typename T,
typename V = typename std::remove_cv<
typename std::remove_reference<decltype(std::declval<T>().value())>::type>::type>
V
getFieldByValue() const;
// Implementations for getting (most) fields that return by const reference.
//
// If an absent optional field is deserialized we don't have anything
// obvious to return. So we insist on having the call provide an
// 'empty' value we return in that circumstance.
template <typename T, typename V>
V const&
getFieldByConstRef(V const& empty) const;
// Implementation for setting most fields with a setValue() method.
template <typename T, typename V>
void
setFieldUsingSetValue(V value);
// Implementation for setting fields using assignment
template <typename T>
void
setFieldUsingAssignment(T const& value);
};
//------------------------------------------------------------------------------
// Implementation
//------------------------------------------------------------------------------
inline SerializedTypeID
STData::getInnerSType() const noexcept
{
return static_cast<SerializedTypeID>(inner_type_);
}
template <typename T, typename V>
V
STData::getFieldByValue() const
{
STBase const* rf = &data_.get();
// if (!rf)
// throwFieldNotFound(getFName());
SerializedTypeID const id = rf->getSType();
if (id == STI_NOTPRESENT)
Throw<std::runtime_error>("Field not present");
T const* cf = dynamic_cast<T const*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
return cf->value();
}
// Implementations for getting (most) fields that return by const reference.
//
// If an absent optional field is deserialized we don't have anything
// obvious to return. So we insist on having the call provide an
// 'empty' value we return in that circumstance.
template <typename T, typename V>
V const&
STData::getFieldByConstRef(V const& empty) const
{
STBase const* rf = &data_.get();
// if (!rf)
// throwFieldNotFound(field);
SerializedTypeID const id = rf->getSType();
if (id == STI_NOTPRESENT)
return empty; // optional field not present
T const* cf = dynamic_cast<T const*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
return *cf;
}
// Implementation for setting most fields with a setValue() method.
template <typename T, typename V>
void
STData::setFieldUsingSetValue(V value)
{
static_assert(!std::is_lvalue_reference<V>::value, "");
STBase* rf = &data_.get();
// if (!rf)
// throwFieldNotFound(field);
if (rf->getSType() == STI_NOTPRESENT)
rf = makeFieldPresent();
T* cf = dynamic_cast<T*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
cf->setValue(std::move(value));
}
// Implementation for setting fields using assignment
template <typename T>
void
STData::setFieldUsingAssignment(T const& value)
{
STBase* rf = &data_.get();
// if (!rf)
// throwFieldNotFound(field);
// if (rf->getSType() == STI_NOTPRESENT)
// rf = makeFieldPresent(field);
T* cf = dynamic_cast<T*>(rf);
if (!cf)
Throw<std::runtime_error>("Wrong field type");
(*cf) = value;
}
//------------------------------------------------------------------------------
//
// Creation
//
//------------------------------------------------------------------------------
STData
dataFromJson(SField const& field, json::Value const& value);
} // namespace xrpl

View File

@@ -1,87 +0,0 @@
#pragma once
#include <xrpl/basics/Buffer.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/detail/STVar.h>
#include <cstdint>
#include <string>
#include <vector>
namespace xrpl {
class STDataType final : public STBase
{
private:
std::uint16_t inner_type_;
bool default_{true};
public:
using value_type = STDataType; // Although not directly holding a single value
STDataType(SField const& n);
STDataType(SField const& n, SerializedTypeID);
STDataType(SerialIter& sit, SField const& name);
SerializedTypeID
getSType() const override;
std::string
getInnerTypeString() const;
std::string
getText() const override;
json::Value getJson(JsonOptions) const override;
void
add(Serializer& s) const override;
bool
isEquivalent(STBase const& t) const override;
bool
isDefault() const override;
void setInnerSType(SerializedTypeID);
SerializedTypeID
getInnerSType() const noexcept;
STBase*
makeFieldPresent();
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend class detail::STVar;
};
//------------------------------------------------------------------------------
// Implementation
//------------------------------------------------------------------------------
inline SerializedTypeID
STDataType::getInnerSType() const noexcept
{
return static_cast<SerializedTypeID>(inner_type_);
}
//------------------------------------------------------------------------------
//
// Creation
//
//------------------------------------------------------------------------------
STDataType
dataTypeFromJson(SField const& field, json::Value const& value);
} // namespace xrpl

View File

@@ -1,193 +0,0 @@
#pragma once
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/Serializer.h>
#include <map>
#include <memory>
#include <string>
#include <variant>
#include <vector>
namespace xrpl {
/**
* STJson: Serialized Type for JSON-like structures (objects or arrays).
*
* Supports two modes:
* - Object: Key-value pairs where keys are VL-encoded strings
* - Array: Ordered list of values
*
* Values are [SType marker][VL-encoded SType serialization].
* Values can be any SType, including nested STJson.
*
* Serialization format: [type_byte][VL_length][data...]
* - type_byte: 0x00 = Object, 0x01 = Array
*/
class STJson : public STBase
{
public:
enum class JsonType : uint8_t { Object = 0x00, Array = 0x01 };
using value_type = STJson;
value_type
value() const
{
return *this;
}
using Key = std::string;
using Value = std::shared_ptr<STBase>;
using Map = std::map<Key, Value>;
using Array = std::vector<Value>;
STJson() = default;
explicit STJson(Map&& map);
explicit STJson(Array&& array);
explicit STJson(SField const& name);
explicit STJson(SerialIter& sit, SField const& name);
SerializedTypeID
getSType() const override;
// Type checking
bool
isArray() const;
bool
isObject() const;
JsonType
getType() const;
// Depth checking (0 = no nesting, 1 = one level of nesting)
int
getDepth() const;
// Parse from binary blob
static std::shared_ptr<STJson>
fromBlob(void const* data, std::size_t size);
// Parse from SerialIter
static std::shared_ptr<STJson>
fromSerialIter(SerialIter& sit);
// Serialize to binary
void
add(Serializer& s) const override;
// JSON representation
json::Value
getJson(JsonOptions options) const override;
bool
isEquivalent(STBase const& t) const override;
bool
isDefault() const override;
// Blob representation
Blob
toBlob() const;
// STJson size
std::size_t
size() const;
// Object accessors (only valid when isObject() == true)
Map const&
getMap() const;
void
setObjectField(Key const& key, Value const& value);
std::optional<STJson::Value>
getObjectField(Key const& key) const;
void
setNestedObjectField(Key const& key, Key const& nestedKey, Value const& value);
std::optional<Value>
getNestedObjectField(Key const& key, Key const& nestedKey) const;
// Array accessors (only valid when isArray() == true)
Array const&
getArray() const;
void
pushArrayElement(Value const& value);
std::optional<Value>
getArrayElement(size_t index) const;
void
setArrayElement(size_t index, Value const& value);
void
setArrayElementField(size_t index, Key const& key, Value const& value);
std::optional<Value>
getArrayElementField(size_t index, Key const& key) const;
size_t
arraySize() const;
// Nested array accessors (for arrays stored in object fields)
void
setNestedArrayElement(Key const& key, size_t index, Value const& value);
void
setNestedArrayElementField(
Key const& key,
size_t index,
Key const& nestedKey,
Value const& value);
std::optional<Value>
getNestedArrayElement(Key const& key, size_t index) const;
std::optional<Value>
getNestedArrayElementField(Key const& key, size_t index, Key const& nestedKey) const;
// Factory for SType value from blob (with SType marker)
static Value
makeValueFromVLWithType(SerialIter& sit);
void
setValue(STJson const& v);
private:
std::variant<Map, Array> data_{Map{}};
bool default_{false};
// Helper: validate nesting depth (max 1 level)
void
validateDepth(Value const& value, int currentDepth) const;
// Helper: parse a single key-value pair from SerialIter
static std::pair<Key, Value>
parsePair(SerialIter& sit);
// Helper: parse array elements from SerialIter
static Array
parseArray(SerialIter& sit, int length);
// Helper: encode a key as VL
static void
addVLKey(Serializer& s, std::string const& str);
// Helper: encode a value as [SType marker][VL]
static void
addVLValue(Serializer& s, std::shared_ptr<STBase> const& value);
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
friend class detail::STVar;
};
} // namespace xrpl

View File

@@ -77,27 +77,9 @@ public:
void
associateAsset(Asset const& a) override;
// Reconstruct via the (mantissa, exponent) ctor so the returned
// Number is normalized to the *current* mantissa scale, not the
// scale that was active when value_ was last set.
//
// STNumber instances are held across transaction boundaries (in
// SLE-cache memory), and the mantissa scale flips per-tx based on
// featureSingleAssetVault / featureLendingProtocol (see
// setCurrentTransactionRules in Rules.cpp). A Number normalized at
// Large scale outside any tx context will fail isnormal() inside a
// Small-scale tx, tripping the assert in operator+=. mantissa() /
// exponent() return the canonical (scale-independent) external
// view; the Normalized{} ctor re-normalizes to current scale.
//
// This costs one extra normalize() per implicit conversion to
// Number — single-digit ns, well below the cost of any caller's
// arithmetic.
operator Number() const
{
if (value_ == Number{})
return value_;
return Number{value_.mantissa(), value_.exponent()};
return value_;
}
private:

View File

@@ -17,7 +17,6 @@
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/STCurrency.h>
#include <xrpl/protocol/STIssue.h>
#include <xrpl/protocol/STJson.h>
#include <xrpl/protocol/STPathSet.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/Serializer.h>
@@ -233,10 +232,6 @@ public:
getFieldI32(SField const& field) const;
[[nodiscard]] AccountID
getAccountID(SField const& field) const;
STData
getFieldData(SField const& field) const;
STDataType
getFieldDataType(SField const& field) const;
[[nodiscard]] Blob
getFieldVL(SField const& field) const;
@@ -255,8 +250,6 @@ public:
getFieldCurrency(SField const& field) const;
[[nodiscard]] STNumber const&
getFieldNumber(SField const& field) const;
STJson const&
getFieldJson(SField const& field) const;
/**
* Get the value of a field.
@@ -370,9 +363,6 @@ public:
void
set(STBase&& v);
void
addFieldFromSlice(SField const& sfield, Slice const& data);
void
setFieldU8(SField const& field, unsigned char);
void
@@ -413,8 +403,6 @@ public:
setFieldArray(SField const& field, STArray const& v);
void
setFieldObject(SField const& field, STObject const& v);
void
setFieldJson(SField const& field, STJson const& v);
template <class Tag>
void

View File

@@ -12,6 +12,8 @@
#include <xrpl/protocol/UintTypes.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
@@ -65,7 +67,7 @@ public:
PathAsset const& asset,
AccountID const& issuer);
[[nodiscard]] auto
[[nodiscard]] std::uint32_t
getNodeType() const;
[[nodiscard]] bool
@@ -109,9 +111,6 @@ public:
[[nodiscard]] bool
isType(Type const& pe) const;
[[nodiscard]] size_t
getHash() const;
bool
operator==(STPathElement const& t) const;
@@ -120,6 +119,17 @@ private:
getHash(STPathElement const& element);
};
template <class Hasher>
void
hash_append(Hasher& h, STPathElement const& e) noexcept
{
using beast::hash_append;
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u);
hash_append(h, e.getAccountID());
hash_append(h, e.getPathAsset());
hash_append(h, e.getIssuerID());
}
class STPath final : public CountedObject<STPath>
{
std::vector<STPathElement> path_;
@@ -176,9 +186,10 @@ template <class Hasher>
void
hash_append(Hasher& h, STPath const& p) noexcept
{
using beast::hash_append;
for (auto const& e : p)
{
beast::hash_append(h, e.getHash());
hash_append(h, e);
}
}
@@ -188,13 +199,39 @@ hash_append(Hasher& h, STPath const& p) noexcept
class STPathSet final : public STBase, public CountedObject<STPathSet>
{
std::vector<STPath> value_;
xrpl::hardened_hash_set<STPath> seenHashes_;
/**
* Deduplication index over `value_`, for pathfinding.
* The use of a std::unique_ptr is intentional as it
* only requires 8 additional bytes of storage for the pointer
* as opposed to 64 bytes with an optional. This keeps the size
* of the STPathSet to within the `STVar::kMaxSize` limit of 72 bytes.
*/
std::unique_ptr<hardened_hash_set<STPath>> seen_;
public:
struct DeduplicationTag
{
};
STPathSet() = default;
/**
* Deduplication tagged constructor.
* Use when you want to ensure that the STPathSet does not contain duplicate paths.
*/
explicit STPathSet(DeduplicationTag);
STPathSet(SField const& n);
STPathSet(SerialIter& sit, SField const& name);
STPathSet(STPathSet const& other);
STPathSet(STPathSet&&) = default;
STPathSet&
operator=(STPathSet const& other);
STPathSet&
operator=(STPathSet&&) = default;
~STPathSet() override = default;
void
add(Serializer& s) const override;
@@ -204,6 +241,16 @@ public:
[[nodiscard]] SerializedTypeID
getSType() const override;
/**
* @brief assembleAdd adds a path to the set by combining a base path and a tail element.
*
* @param base The base path.
* @param tail The tail element.
* @return true if the path was added, false if it was a duplicate and not added.
* @remarks Requires the STPathSet to be constructed with the DeduplicationTag. The return value
* indicates whether the combined path was inserted (true) or rejected as a duplicate (false).
* It is fine for callers to ignore the return value.
*/
bool
assembleAdd(STPath const& base, STPathElement const& tail);
@@ -229,22 +276,61 @@ public:
[[nodiscard]] bool
empty() const;
void
/**
* @brief pushBack adds a path to the set.
*
* @param e The path to add.
* @return true if the path was added, false if it was a duplicate and not added.
* @remarks If the STPathSet was constructed with the DeduplicationTag, then this method will
* check for duplicates and only add the path if it is not already present in the
* set. If the STPathSet was constructed without the DeduplicationTag,
* then this method will always add the path to the set, regardless of duplicates.
* It is fine for callers to ignore the return value.
*/
bool
pushBack(STPath const& e);
/**
* @brief emplaceBack adds a path to the set.
*
* @param args The arguments to construct the path with.
* @return true if the path was added, false if it was a duplicate and not added.
* @remarks If the STPathSet was constructed with the DeduplicationTag, then this method will
* check for duplicates and only add the path if it is not already present in the
* set. If the STPathSet was constructed without the DeduplicationTag,
* then this method will always add the path to the set, regardless of duplicates.
* It is fine for callers to ignore the return value.
* @note The path is constructed before the duplicate check, so on a false
* return the constructed path is discarded and any argument
* forwarded as an rvalue is left in a moved-from state. Use
* pushBack when the caller needs to keep its path on rejection.
*/
template <typename... Args>
void
bool
emplaceBack(Args&&... args);
[[nodiscard]] bool
contains(STPath const& path) const;
private:
STBase*
copy(std::size_t n, void* buf) const override;
STBase*
move(std::size_t n, void* buf) override;
/**
* @brief Append a path via `append`, then register it in the deduplication index.
*
* @param append Invoked with `value_`; must append exactly one path to it.
* @return true if the path was kept, false if it was a duplicate and was rolled back.
* @remarks Appends to the vector before touching the index, so that a failed allocation
* there leaves both containers untouched rather than leaving the index holding
* a path the vector does not. If the index insert reports a duplicate, or
* throws, the append is rolled back so the two containers stay consistent; in
* the throwing case the exception propagates. With no index (constructed
* without the DeduplicationTag) the append is unconditional.
*/
template <typename Append>
bool
appendUnique(Append&& append);
friend class detail::STVar;
};
@@ -336,7 +422,7 @@ inline STPathElement::STPathElement(
hashValue_ = getHash(*this);
}
inline auto
inline std::uint32_t
STPathElement::getNodeType() const
{
return type_;
@@ -545,25 +631,50 @@ STPathSet::empty() const
return value_.empty();
}
inline void
STPathSet::pushBack(STPath const& e)
template <typename Append>
inline bool
STPathSet::appendUnique(Append&& append)
{
value_.push_back(e);
seenHashes_.emplace(value_.back());
}
// Append to the vector first, so that a failed allocation there leaves both
// containers untouched rather than leaving the index holding a path the
// vector does not.
append(value_);
template <typename... Args>
inline void
STPathSet::emplaceBack(Args&&... args)
{
value_.emplace_back(std::forward<Args>(args)...);
seenHashes_.emplace(value_.back());
if (seen_ == nullptr)
{
return true;
}
try
{
if (!seen_->insert(value_.back()).second)
{
// Already present: roll back the append.
value_.pop_back();
return false;
}
}
catch (...)
{
// The index insert failed, so roll back the append to keep the vector
// and the index consistent.
value_.pop_back();
throw;
}
return true;
}
inline bool
STPathSet::contains(STPath const& path) const
STPathSet::pushBack(STPath const& e)
{
return seenHashes_.contains(path);
return appendUnique([&](auto& value) { value.push_back(e); });
}
template <typename... Args>
inline bool
STPathSet::emplaceBack(Args&&... args)
{
return appendUnique([&](auto& value) { value.emplace_back(std::forward<Args>(args)...); });
}
} // namespace xrpl

View File

@@ -124,6 +124,13 @@ public:
[[nodiscard]] NodeID const&
getNodeID() const noexcept;
/**
* Whether this validation carries a good signature.
*
* Reports false if the signature cannot be checked at all, so a caller
* cannot tell that apart from a bad signature. Either way the validation is
* unusable, and the reason is logged. Only a computed answer is remembered.
*/
[[nodiscard]] bool
isValid() const noexcept;
@@ -184,13 +191,7 @@ STValidation::STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, Deseria
, signingPubKey_([this]() {
auto const spk = getFieldVL(sfSigningPubKey);
// Validations are signed with either the legacy secp256k1 key
// (pre-amendment) or the new dilithium key (post-amendment). Mixed
// sets are expected during the rolling upgrade. Ed25519 has never
// been valid for validations and is still rejected. verifyDigest()
// dispatches per keytype.
auto const kt = publicKeyType(makeSlice(spk));
if (kt != KeyType::Secp256k1 && kt != KeyType::Dilithium)
if (publicKeyType(makeSlice(spk)) != KeyType::Secp256k1)
Throw<std::runtime_error>("Invalid public key in validation");
return PublicKey{makeSlice(spk)};
@@ -233,12 +234,9 @@ STValidation::STValidation(
"xrpl::STValidation::STValidation(PublicKey, SecretKey) : nonzero "
"node");
// First, set our own public key. Only secp256k1 (legacy) and dilithium
// (post-quantum) are valid for signing validations; Ed25519 has never
// been supported here.
if (auto const kt = publicKeyType(pk);
kt != KeyType::Secp256k1 && kt != KeyType::Dilithium)
logicError("Validation signing requires secp256k1 or dilithium key");
// First, set our own public key:
if (publicKeyType(pk) != KeyType::Secp256k1)
logicError("We can only use secp256k1 keys for signing validations");
setFieldVL(sfSigningPubKey, pk.slice());
setFieldU32(sfSigningTime, signTime.time_since_epoch().count());

View File

@@ -26,10 +26,7 @@ public:
static constexpr std::size_t kSize = 32;
private:
// Dilithium secret keys are 2528 bytes; ed25519/secp256k1 are 32.
// Buffer sized for the largest supported key; actual length in size_.
std::uint8_t buf_[2560]{};
std::size_t size_ = 0;
std::uint8_t buf_[kSize]{};
public:
using const_iterator = std::uint8_t const*;
@@ -47,7 +44,6 @@ public:
~SecretKey();
SecretKey(std::array<std::uint8_t, kSize> const& data);
SecretKey(std::array<std::uint8_t, 2560> const& data);
SecretKey(Slice const& slice);
[[nodiscard]] std::uint8_t const*
@@ -59,7 +55,7 @@ public:
[[nodiscard]] std::size_t
size() const
{
return size_;
return sizeof(buf_);
}
/**
@@ -86,13 +82,13 @@ public:
[[nodiscard]] const_iterator
end() const noexcept
{
return buf_ + size_;
return buf_ + sizeof(buf_);
}
[[nodiscard]] const_iterator
cend() const noexcept
{
return buf_ + size_;
return buf_ + sizeof(buf_);
}
};
@@ -123,12 +119,6 @@ toBase58(TokenType type, SecretKey const& sk)
SecretKey
randomSecretKey();
/**
* Create a secret key using secure random numbers.
*/
SecretKey
randomSecretKey(KeyType type);
/**
* Generate a new secret key deterministically.
*/

View File

@@ -10,6 +10,7 @@
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/SField.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
@@ -25,6 +26,101 @@ private:
Blob data_;
public:
/**
* A header is never longer than this. The encoder fills a buffer of this
* size and writes only the bytes it used.
*/
static constexpr int kMaxNumberOfBytesInHeader = 3;
// A field whose size varies is stored as a header holding its length, then
// the field data. The header is 1, 2 or 3 bytes long. Nothing outside it says
// which, so the decoder reads the first byte and its value says how long the
// header is:
//
// 0 ... 192 kMin/kMaxValueOfFirstByteFor1ByteHeader
// 193 ... 240 kMin/kMaxValueOfFirstByteFor2ByteHeader
// 241 ... 254 kMin/kMaxValueOfFirstByteFor3ByteHeader
// 255 belongs to no header
//
// Each range starts one past the end of the range before it.
static constexpr int kMinValueOfFirstByteFor1ByteHeader = 0;
static constexpr int kMaxValueOfFirstByteFor1ByteHeader = 192;
static constexpr int kMinValueOfFirstByteFor2ByteHeader =
kMaxValueOfFirstByteFor1ByteHeader + 1;
static constexpr int kMaxValueOfFirstByteFor2ByteHeader = 240;
static constexpr int kMinValueOfFirstByteFor3ByteHeader =
kMaxValueOfFirstByteFor2ByteHeader + 1;
static constexpr int kMaxValueOfFirstByteFor3ByteHeader = 254;
// A length x too big for one byte is split across the header. For 2 bytes:
//
// first byte = 193 + (x - 193) / 256
// second byte = (x - 193) % 256
//
// so 300 is stored as 193, 107. For 3 bytes it is the same, from 241, with
// the remainder split across two bytes: 20,000 is stored as 241, 29, 95.
static constexpr int kNumberOfValuesInOneByte = 256;
static constexpr int kNumberOfValuesInTwoBytes =
kNumberOfValuesInOneByte * kNumberOfValuesInOneByte;
// Each header length therefore covers a range of field lengths:
//
// 0 ... 192 kMin/kMaxValueOfLengthFor1ByteHeader
// 193 ... 12,480 kMin/kMaxValueOfLengthFor2ByteHeader
// 12,481 ... 918,744 kMin/kMaxValueOfLengthFor3ByteHeader
//
// The encoder always uses the shortest header that fits.
/**
* A 1 byte header holds the length in the byte itself, so both ends of
* this range are the same numbers as the first byte's own range.
*/
static constexpr int kMinValueOfLengthFor1ByteHeader = kMinValueOfFirstByteFor1ByteHeader;
static constexpr int kMaxValueOfLengthFor1ByteHeader = kMaxValueOfFirstByteFor1ByteHeader;
static constexpr int kMinValueOfLengthFor2ByteHeader = kMaxValueOfLengthFor1ByteHeader + 1;
/**
* 48 values of the first byte mean a 2 byte header, and each of them covers
* 256 lengths. The 48 is worked out from the two range ends above, so it
* stays right if either of them changes.
*/
static constexpr int kMaxValueOfLengthFor2ByteHeader = kMinValueOfLengthFor2ByteHeader +
((kMaxValueOfFirstByteFor2ByteHeader - kMaxValueOfFirstByteFor1ByteHeader) *
kNumberOfValuesInOneByte) -
1;
static constexpr int kMinValueOfLengthFor3ByteHeader = kMaxValueOfLengthFor2ByteHeader + 1;
/**
* 14 values of the first byte mean a 3 byte header, and each of them covers
* 65,536 lengths. Counted the same way, that gives the largest length any
* header can state.
*
* Nothing is accepted or rejected against this. The assertion below uses it
* to check that every length the encoder writes is one a header can state.
*/
static constexpr int kMaxRepresentableLength = kMinValueOfLengthFor3ByteHeader +
((kMaxValueOfFirstByteFor3ByteHeader - kMaxValueOfFirstByteFor2ByteHeader) *
kNumberOfValuesInTwoBytes) -
1;
/**
* The largest length the encoder will write. This is the one number here
* that is picked rather than worked out. The decoder accepts nothing above
* it, so both sides agree on the same set of lengths.
*/
static constexpr int kMaxValueOfLengthFor3ByteHeader = 918744;
static_assert(
kMaxValueOfLengthFor3ByteHeader <= kMaxRepresentableLength,
"a length the encoder writes must be one a header can state");
explicit Serializer(int n = 256)
{
data_.reserve(n);
@@ -61,7 +157,7 @@ public:
// assemble functions
int
add8(unsigned char i);
add8(unsigned char byteValue);
int
add16(std::uint16_t i);
@@ -270,18 +366,90 @@ public:
return v.data_ == data_;
}
/**
* Works out how long a header is, from its first byte.
*
* Each overload of decodeVLLength below reads one header length, so call
* this first to learn which of them to call.
*
* @param firstByte First byte of the header, as read from the stream.
* @return How many bytes the whole header takes, counting firstByte: 1, 2
* or 3.
* @throws std::overflow_error if firstByte is the one value that starts no
* header.
*/
static int
decodeLengthLength(int b1);
decodeLengthLength(std::byte firstByte);
/**
* Reads the field length out of a 1 byte header.
*
* @param firstByte The single header byte, which is the length itself.
* @return Field length in bytes, from kMinValueOfLengthFor1ByteHeader to
* kMaxValueOfLengthFor1ByteHeader.
* @throws std::overflow_error if firstByte is big enough to mean a longer
* header, in which case it is not a length by itself.
*/
static int
decodeVLLength(int b1);
decodeVLLength(std::byte firstByte);
/**
* Reads the field length out of a 2 byte header.
*
* @param firstByte First header byte. Its value means a 2 byte header, and
* how far it sits into that range gives the top part of the length.
* @param secondByte Second header byte, holding the rest of the length.
* @return Field length in bytes, from kMinValueOfLengthFor2ByteHeader to
* kMaxValueOfLengthFor2ByteHeader.
* @throws std::overflow_error if firstByte is outside the range that means
* a 2 byte header.
*/
static int
decodeVLLength(int b1, int b2);
decodeVLLength(std::byte firstByte, std::byte secondByte);
/**
* Reads the field length out of a 3 byte header.
*
* @param firstByte First header byte. Its value means a 3 byte header, and
* how far it sits into that range gives the top part of the length.
* @param secondByte Second header byte, holding the middle part of the
* length.
* @param thirdByte Third header byte, holding the low part.
* @return Field length in bytes, from kMinValueOfLengthFor3ByteHeader to
* kMaxValueOfLengthFor3ByteHeader.
* @throws std::overflow_error if firstByte is outside the range that means
* a 3 byte header, or if the three bytes together state a length above
* kMaxValueOfLengthFor3ByteHeader, which the encoder would not write back.
*/
static int
decodeVLLength(int b1, int b2, int b3);
decodeVLLength(std::byte firstByte, std::byte secondByte, std::byte thirdByte);
private:
/**
* Works out how many bytes the header needs for the given length.
*
* This deliberately repeats the width choice addEncoded makes, so that
* addVL's assertion can compare the two. It has no other caller; do not
* reach for it as a utility.
*
* @param length Field length in bytes.
* @return How many header bytes it needs: 1, 2 or 3.
* @throws std::overflow_error if length is negative, or above
* kMaxValueOfLengthFor3ByteHeader.
*/
static int
encodeLengthLength(int length); // length to encode length
encodeLengthLength(int length);
/**
* Appends the length header for a field of the given length.
*
* The field's own data is not written; the caller appends it next.
*
* @param length Field length in bytes.
* @return Offset within this Serializer at which the header was written.
* @throws std::overflow_error if length is negative, or above
* kMaxValueOfLengthFor3ByteHeader.
*/
int
addEncoded(int length);
};
@@ -390,9 +558,15 @@ public:
void
getFieldID(int& type, int& name);
// Returns the size of the VL if the
// next object is a VL. Advances the iterator
// to the beginning of the VL.
/**
* Reads the length header at the read position and steps past it.
*
* @return Field length in bytes. The iterator is left on the first byte of
* the field data.
* @throws std::overflow_error if the header states a length the encoder could
* not have written.
* @throws std::runtime_error if the data runs out before the header does.
*/
int
getVLDataLength();

View File

@@ -1,78 +0,0 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <cstddef>
#include <cstdint>
namespace xrpl {
/**
* Structural validation for MPTStructuredData schemas and data.
*
* A schema is a sequence of one-byte type codes describing a packed
* record layout. Data is the corresponding values packed back-to-back
* in schema order: big-endian integers, VL-prefixed str/bin (standard
* XRPL variable-length encoding, byte counts), arrays as a one-byte
* element count followed by that many elements, and tuples with no
* framing of their own (the markers exist only in the schema).
*
* These functions validate structure only. No values are materialized
* and no semantic checks are performed beyond the bool 0x00/0x01 rule.
*/
/**
* Schema type codes. Codes not listed here are reserved and malformed.
*/
enum class SchemaType : std::uint8_t {
boolean = 0x01, // 1 byte, 0x00 or 0x01
u8 = 0x02, // 1 byte
u16 = 0x03, // 2 bytes, big-endian
u32 = 0x04, // 4 bytes, big-endian
u64 = 0x05, // 8 bytes, big-endian
u128 = 0x06, // 16 bytes
u256 = 0x07, // 32 bytes
xfl = 0x08, // 8 bytes, XLS-17 floating point
account = 0x09, // 20 bytes, AccountID
currency = 0x0A, // 20 bytes, 160-bit currency code
h160 = 0x0B, // 20 bytes
h256 = 0x0C, // 32 bytes
pubkey = 0x0D, // 33 bytes, compressed public key
str = 0x0E, // VL prefix + UTF-8 bytes
bin = 0x0F, // VL prefix + opaque bytes
array = 0x20, // followed by one element type; data: count byte + elements
tupleOpen = 0x30,
tupleClose = 0x31,
};
/**
* Maximum array/tuple nesting depth a schema may declare.
*/
constexpr std::size_t kMaxSchemaDepth = 8;
/**
* Check that a schema is well-formed.
*
* Well-formed means: non-empty, no longer than kMaxSchemaLength, every
* code is a known SchemaType, every array code is followed by an
* element type, every tupleOpen has a matching tupleClose, and nesting
* does not exceed kMaxSchemaDepth.
*/
[[nodiscard]] bool
isWellFormedSchema(Slice schema);
/**
* Check that data decodes exactly against a well-formed schema.
*
* Walks both inputs with two cursors. Every field must be present at
* its declared width (VL-prefixed for str/bin), array counts must be
* consistent with the remaining input, and both cursors must end
* exactly exhausted: truncated data and trailing bytes both fail.
*
* The schema is re-validated during the walk, so a malformed schema
* returns false rather than misreading data.
*/
[[nodiscard]] bool
dataMatchesSchema(Slice schema, Slice data);
} // namespace xrpl

View File

@@ -129,11 +129,8 @@ enum TEMcodes : TERUnderlyingType {
temARRAY_TOO_LARGE,
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_MPT,
temBAD_CIPHERTEXT,
temINVALID_BYTECODE,
temTEMP_DISABLED,
};
//------------------------------------------------------------------------------
@@ -182,9 +179,6 @@ enum TEFcodes : TERUnderlyingType {
tefINVALID_LEDGER_FIX_TYPE,
tefNO_DST_PARTIAL,
tefBAD_PATH_COUNT,
tefNO_BYTECODE,
tefBYTECODE_NOT_INCLUDED,
tefFIREWALL_BLOCK,
};
//------------------------------------------------------------------------------
@@ -376,25 +370,6 @@ enum TECcodes : TERUnderlyingType {
tecNO_DELEGATE_PERMISSION = 198,
tecBAD_PROOF = 199,
tecNO_SPONSOR_PERMISSION = 200,
tecOUT_OF_GAS = 201,
tecBYTECODE_REJECTED = 202,
tecINVALID_PARAMETERS = 203,
tecSUPPLY_EXCEEDED = 204,
tecBALLOT_CLOSED = 205,
tecBALLOT_VOTED = 206,
tecBALLOT_NOT_OPEN = 207,
tecBALLOT_EXISTS = 208,
tecBALLOT_BAD_OPTIONS = 209,
// Audit #20: surfaced when a CL swap loop exhausts the per-call
// maxTickCrossings budget. POST-AUDIT-#19: BookStep now iterates per
// tick range and never crosses >1000 ticks in one applySwap call, so
// this code is not reachable through normal Payment routing. It
// remains the contract for direct curve callers and as a safety net
// for any future code path that bypasses #19's iteration model.
tecAMM_TICK_CAP_HIT = 210,
tecWOULD_CROSS = 211,
tecREPO_ACTIVE = 212,
tecREPO_PENDING = 213,
};
//------------------------------------------------------------------------------

View File

@@ -97,9 +97,7 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfImmediateOrCancel, 0x00020000) \
TF_FLAG(tfFillOrKill, 0x00040000) \
TF_FLAG(tfSell, 0x00080000) \
TF_FLAG(tfHybrid, 0x00100000) \
TF_FLAG(tfAllOrNone, 0x00200000) \
TF_FLAG(tfPostOnly, 0x00400000), \
TF_FLAG(tfHybrid, 0x00100000), \
MASK_ADJ(0)) \
\
TRANSACTION(Payment, \
@@ -164,16 +162,6 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfMPTSetCanHoldConfidentialBalance, 0x00000100), \
MASK_ADJ(0)) \
\
TRANSACTION(TokenIssuanceCreate, \
TF_FLAG(tfTokenCannotLock, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(TokenIssuanceSet, \
TF_FLAG2(tfTokenCannotLock, 0x00000001) \
TF_FLAG(tfTokenLock, 0x00000002) \
TF_FLAG(tfTokenUnlock, 0x00000004), \
MASK_ADJ(0)) \
\
TRANSACTION(NFTokenCreateOffer, \
TF_FLAG(tfSellNFToken, 0x00000001), \
MASK_ADJ(0)) \
@@ -250,29 +238,8 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfSponsorshipEnd, 0x00010000) \
TF_FLAG(tfSponsorshipCreate, 0x00020000) \
TF_FLAG(tfSponsorshipReassign, 0x00040000), \
MASK_ADJ(0)) \
\
TRANSACTION(Contract, \
TF_FLAG(tfImmutable, 0x00010000) \
TF_FLAG(tfCodeImmutable, 0x00020000) \
TF_FLAG(tfABIImmutable, 0x00040000) \
TF_FLAG(tfUndeletable, 0x00080000), \
MASK_ADJ(0)) \
\
TRANSACTION(BallotCreate, \
TF_FLAG(tfVoterRecoverable, lsfVoterRecoverable), /* casts must carry a voter self-recovery vector */ \
MASK_ADJ(0)) \
\
TRANSACTION(SubscriptionSet, /* True, delete the subscription on the first successful claim */ \
TF_FLAG(tfSingleUse, 0x00010000), \
MASK_ADJ(0))
constexpr std::uint32_t tfSendAmount = 0x00010000;
constexpr std::uint32_t tfSendNFToken = 0x00020000;
constexpr std::uint32_t tfAuthorizeToken = 0x00040000;
constexpr std::uint32_t tfContractParameterMask =
~(tfSendAmount | tfSendNFToken | tfAuthorizeToken);
// clang-format on
// Create all the flag values.

View File

@@ -95,12 +95,6 @@ public:
if (obj.isFieldPresent(sfParentBatchID))
parentBatchID_ = obj.getFieldH256(sfParentBatchID);
if (obj.isFieldPresent(sfGasUsed))
gasUsed_ = obj.getFieldU32(sfGasUsed);
if (obj.isFieldPresent(sfVMReturnCode))
vmReturnCode_ = obj.getFieldI32(sfVMReturnCode);
}
[[nodiscard]] std::optional<STAmount> const&
@@ -121,30 +115,6 @@ public:
parentBatchID_ = id;
}
void
setGasUsed(std::optional<std::uint32_t> const gasUsed)
{
gasUsed_ = gasUsed;
}
[[nodiscard]] std::optional<std::uint32_t> const&
getGasUsed() const
{
return gasUsed_;
}
void
setVMReturnCode(std::optional<std::int32_t> const vmReturnCode)
{
vmReturnCode_ = vmReturnCode;
}
[[nodiscard]] std::optional<std::int32_t> const&
getVMReturnCode() const
{
return vmReturnCode_;
}
private:
uint256 transactionID_;
std::uint32_t ledgerSeq_;
@@ -153,8 +123,6 @@ private:
std::optional<STAmount> deliveredAmount_;
std::optional<uint256> parentBatchID_;
std::optional<std::uint32_t> gasUsed_;
std::optional<std::int32_t> vmReturnCode_;
STArray nodes_;
};

View File

@@ -10,32 +10,6 @@ namespace xrpl {
enum class Delegation { Delegable, NotDelegable };
/**
* Whether a smart contract may emit a transaction of this type.
*/
enum class Emittance { Emitable, NotEmitable };
/**
* How an account's firewall treats a transaction the account submits.
*
* The classification is per transaction type and is read through
* firewallAction() in <xrpl/protocol/Firewall.h>.
*/
enum class FirewallAction {
/**
* The firewall inspects the transaction's destination before applying it.
*/
Check,
/**
* The firewall lets the transaction through without inspecting it.
*/
Allow,
/**
* The firewall rejects the transaction while a firewall is set.
*/
Block
};
/**
* Operations a transaction is permitted to perform, as a bitfield.
*
@@ -43,27 +17,25 @@ enum class FirewallAction {
* TxSettings::privileges) and enforced in InvariantCheck.cpp.
*/
enum class Privilege : std::uint16_t {
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
CreateTokenIssuance = 0x4000, // The transaction can create a new token issuance
DestroyTokenIssuance = 0x8000, // The transaction can destroy a token issuance
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
};
// The inner static_cast is not redundant: the underlying type is narrower than
@@ -119,16 +91,6 @@ struct TxSettings
* Operations this transaction is permitted to perform.
*/
Privilege privileges{Privilege::NoPriv};
/**
* Whether a smart contract may emit this transaction.
*/
Emittance emittance{Emittance::Emitable};
/**
* How an account's firewall treats this transaction.
*/
FirewallAction firewall{FirewallAction::Allow};
};
} // namespace xrpl

View File

@@ -34,10 +34,11 @@ concept ValidConstructSTArgs =
// and includes a small-object allocation optimization.
class STVar
{
private:
public:
// The largest "small object" we can accommodate
static constexpr std::size_t kMaxSize = 72;
private:
alignas(std::max_align_t) std::byte d_[kMaxSize] = {};
STBase* p_ = nullptr;

View File

@@ -1,19 +0,0 @@
#if !defined(EMITABLE)
#error "undefined macro: EMITABLE"
#endif
/**
* EMITABLE(name, type, txType, value)
*
* This macro defines a permission:
* name: the name of the permission.
* type: the GranularPermissionType enum.
* txType: the corresponding TxType for this permission.
* value: the uint32 numeric value for the enum type.
*/
/** This removes the contract account the ability to set or remove deposit auth. */
EMITABLE(AccountDepositAuth, ttACCOUNT_SET, 65537)
// ** This removes the contract account the ability to set or remove disable master key. */
EMITABLE(AccountDisableMaster, ttACCOUNT_SET, 65538)

View File

@@ -15,9 +15,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FEATURE(SmartContract, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_4_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocolV1_1, Supported::Yes, VoteBehavior::DefaultNo)
@@ -146,20 +144,3 @@ XRPL_RETIRE_FEATURE(SortedDirectories)
XRPL_RETIRE_FEATURE(TicketBatch)
XRPL_RETIRE_FEATURE(TickSize)
XRPL_RETIRE_FEATURE(TrustSetAuth)
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(ConfidentialMPTKeyRotation, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenPaychan, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Quantum, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Passkey, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTStructuredData, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenIssuance, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(VaultContinuousAccrual, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(CouponPayments, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(ConfidentialVoting, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(AMMCurves, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(OfferQualifiers, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Subscription, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(Repo, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(Beneficiary, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(Firewall, Supported::No, VoteBehavior::DefaultNo)

View File

@@ -153,7 +153,6 @@ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({
{sfAMMID, SoeOptional}, // pseudo-account designator
{sfVaultID, SoeOptional}, // pseudo-account designator
{sfLoanBrokerID, SoeOptional}, // pseudo-account designator
{sfContractID, SoeOptional}, // pseudo-account designator
}))
/** A ledger object which contains a list of object identifiers.
@@ -241,7 +240,6 @@ LEDGER_ENTRY(ltOFFER, 0x006f, Offer, offer, ({
{sfExpiration, SoeOptional},
{sfDomainID, SoeOptional},
{sfAdditionalBooks, SoeOptional},
{sfMinQuantity, SoeOptional},
}))
/** A ledger object which describes a deposit pre-authorization.
@@ -311,11 +309,6 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
{sfBaseFeeDrops, SoeOptional},
{sfReserveBaseDrops, SoeOptional},
{sfReserveIncrementDrops, SoeOptional},
// Smart Escrow fields
{sfGasLimit, SoeOptional},
{sfBytecodeSizeLimit, SoeOptional},
{sfGasPrice, SoeOptional},
{sfPreviousTxnID, SoeOptional},
{sfPreviousTxnLgrSeq, SoeOptional},
}))
@@ -346,8 +339,6 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
{sfCondition, SoeOptional},
{sfCancelAfter, SoeOptional},
{sfFinishAfter, SoeOptional},
{sfBytecode, SoeOptional},
{sfData, SoeOptional},
{sfSourceTag, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfOwnerNode, SoeRequired},
@@ -378,8 +369,6 @@ LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfDestinationNode, SoeOptional},
{sfTransferRate, SoeOptional},
{sfIssuerNode, SoeOptional},
}))
/** The ledger object which tracks the AMM.
@@ -397,19 +386,6 @@ LEDGER_ENTRY(ltAMM, 0x0079, AMM, amm, ({
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeOptional},
{sfPreviousTxnLgrSeq, SoeOptional},
{sfCurveType, SoeDefault},
{sfFeeTier, SoeOptional},
{sfTickSpacing, SoeOptional},
{sfCurrentTick, SoeOptional},
{sfActiveLiquidity, SoeOptional},
{sfSqrtPriceX96, SoeOptional},
{sfFeeGrowthGlobal0, SoeOptional},
{sfFeeGrowthGlobal1, SoeOptional},
{sfAmplification, SoeOptional},
{sfAmplificationTime, SoeOptional},
{sfPositionCount, SoeDefault},
{sfBinStep, SoeOptional},
{sfActiveBinID, SoeOptional},
}))
/** A ledger object which tracks MPTokenIssuance
@@ -425,8 +401,6 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfOutstandingAmount, SoeRequired},
{sfLockedAmount, SoeOptional},
{sfMPTokenMetadata, SoeOptional},
{sfMPTokenSchema, SoeOptional},
{sfTokenIssuanceID, SoeOptional},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfDomainID, SoeOptional},
@@ -434,10 +408,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfReferenceHolding, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
{sfAuditorEncryptionKey, SoeOptional},
{sfIssuerKeyEpoch, SoeOptional},
{sfAuditorKeyEpoch, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
{sfBallotID, SoeOptional}, // ConfidentialVoting: the issuance's open ballot, if any
}))
/** A ledger object which tracks MPToken
@@ -457,11 +428,6 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfIssuerEncryptedBalance, SoeOptional},
{sfAuditorEncryptedBalance, SoeOptional},
{sfHolderEncryptionKey, SoeOptional},
{sfRedemptionAfter, SoeDefault},
{sfCouponAccrued, SoeOptional},
{sfCouponIndex, SoeOptional},
{sfVoteLockedAmount, SoeOptional}, // ConfidentialVoting: locked while a ballot is open
{sfBallotID, SoeOptional}, // ConfidentialVoting: the ballot holding the lock
}))
/** A ledger object which tracks Oracle
@@ -536,70 +502,17 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
{sfAssetsAvailable, SoeDefault},
{sfAssetsMaximum, SoeDefault},
{sfLossUnrealized, SoeDefault},
{sfUnearnedInterest, SoeDefault},
{sfAccrualRate, SoeDefault},
{sfLastAccrualTime, SoeDefault},
{sfShareMPTID, SoeRequired},
{sfWithdrawalPolicy, SoeRequired},
{sfAccountingMethod, SoeDefault},
{sfScale, SoeDefault},
{sfLEVersion, SoeDefault},
{sfVaultKind, SoeDefault},
{sfSubscriptionDate, SoeOptional},
{sfRedemptionDate, SoeOptional},
{sfDealingInterval, SoeDefault},
{sfDealingWindow, SoeDefault},
{sfStruckPrice, SoeDefault},
{sfStruckUntil, SoeDefault},
{sfDepositFee, SoeDefault},
{sfRedemptionFee, SoeDefault},
{sfRedemptionPeriod, SoeDefault},
// no SharesTotal ever (use MPTIssuance.sfOutstandingAmount)
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
}))
/** A ledger object representing a contract source.
\sa keylet::contractSource
*/
LEDGER_ENTRY(ltCONTRACT_SOURCE, 0x0085, ContractSource, contract_source, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfContractHash, SoeRequired},
{sfContractCode, SoeRequired},
{sfFunctions, SoeRequired},
{sfInstanceParameters, SoeOptional},
{sfReferenceCount, SoeRequired},
}))
/** A ledger object representing a contract.
\sa keylet::contract
*/
LEDGER_ENTRY(ltCONTRACT, 0x0086, Contract, contract, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfSequence, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfOwner, SoeRequired},
{sfContractAccount, SoeRequired},
{sfContractHash, SoeRequired},
{sfInstanceParameterValues, SoeOptional},
{sfURI, SoeOptional},
}))
/** A ledger object representing a contract data.
\sa keylet::contractData
*/
LEDGER_ENTRY(ltCONTRACT_DATA, 0x0087, ContractData, contract_data, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfOwner, SoeRequired},
{sfContractAccount, SoeRequired},
{sfContractJson, SoeRequired},
}))
/** Reserve 0x0084-0x0087 for future Vault-related objects. */
/** A ledger object representing a loan broker
@@ -726,208 +639,6 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({
{sfOwnerNode, SoeRequired},
{sfSponseeNode, SoeRequired},
}))
LEDGER_ENTRY(ltPASSKEY_LIST, 0x0091, PasskeyList, passkey_list, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfOwner, SoeRequired},
{sfPasskeys, SoeRequired},
}))
/** A ledger object which describes an IOU token issuance.
\sa keylet::tokenIssuance
*/
LEDGER_ENTRY(ltTOKEN_ISSUANCE, 0x0092, TokenIssuance, token_issuance, ({
{sfIssuer, SoeRequired},
{sfCurrency, SoeRequired},
{sfMaximumAmount, SoeOptional},
{sfIssuedAmount, SoeDefault},
{sfTokenScale, SoeDefault},
{sfMPTokenIssuanceID, SoeOptional},
{sfTransferFee, SoeOptional},
{sfMPTokenMetadata, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object representing a bond coupon schedule: the issuer's
standing offer of fixed periodic coupon payments per unit of a bond
asset, in a settlement asset. One schedule per (Account, BondAsset).
\sa keylet::couponSchedule
*/
LEDGER_ENTRY(ltCOUPON_SCHEDULE, 0x0093, CouponSchedule, coupon_schedule, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfOwner, SoeRequired},
{sfAccount, SoeRequired},
{sfMPTokenIssuanceID, SoeRequired},
{sfCouponAsset, SoeRequired},
{sfAccruedPerUnit, SoeDefault},
{sfPoolAmount, SoeDefault},
{sfClaimantCount, SoeDefault},
{sfCouponCount, SoeDefault},
{sfLastCouponTime, SoeOptional},
{sfCouponAmount, SoeOptional},
{sfCouponInterval, SoeOptional},
{sfFirstCouponTime, SoeOptional},
{sfExpiration, SoeOptional},
{sfCallNoticePeriod, SoeOptional},
{sfEarliestCallTime, SoeOptional},
}))
/** A ledger object representing a confidential ballot with a homomorphically
encrypted tally.
\sa keylet::ballot
*/
LEDGER_ENTRY(ltBALLOT, 0x0094, Ballot, ballot, ({
{sfOwner, SoeRequired},
{sfSequence, SoeRequired},
{sfMPTokenIssuanceID, SoeOptional}, // token mode (one-of with sfDomainID)
{sfDomainID, SoeOptional}, // credential mode (one-of)
{sfDigest, SoeRequired}, // hash of the off-ledger ballot document
{sfURI, SoeOptional},
{sfOptionCount, SoeRequired},
{sfTallyPublicKey, SoeRequired},
{sfAuditorEncryptionKey, SoeOptional}, // optional auditor ElGamal key
{sfEncryptedTally, SoeRequired}, // one ElGamal ciphertext per option
{sfOpenTime, SoeRequired},
{sfCloseTime, SoeRequired},
{sfVoteCount, SoeDefault},
{sfResults, SoeOptional}, // plaintext counts, present after finalize
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A ledger object recording a single voter's cast on a Ballot.
\sa keylet::ballotVote
*/
LEDGER_ENTRY(ltBALLOT_VOTE, 0x0095, BallotVote, ballot_vote, ({
{sfAccount, SoeRequired}, // voter
{sfBallotID, SoeRequired},
{sfBallotWeight, SoeRequired},
{sfEncryptedVotes, SoeRequired}, // vote vector under the tally key
{sfAuditorEncryptedVotes, SoeOptional}, // present iff the ballot has an auditor key
{sfVoterPublicKey, SoeOptional}, // present iff lsfVoterRecoverable
{sfVoterEncryptedVotes, SoeOptional}, // present iff lsfVoterRecoverable
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
LEDGER_ENTRY(ltAMM_POSITION, 0x007a, AMMPosition, amm_position, ({
{sfAccount, SoeRequired},
{sfAMMID, SoeRequired},
{sfTickLower, SoeRequired},
{sfTickUpper, SoeRequired},
{sfPositionLiquidity, SoeRequired},
{sfFeeGrowthInsideLast0, SoeRequired},
{sfFeeGrowthInsideLast1, SoeRequired},
{sfTokensOwed0, SoeDefault},
{sfTokensOwed1, SoeDefault},
{sfOwnerNode, SoeRequired},
}))
LEDGER_ENTRY(ltAMM_TICK, 0x007b, AMMTick, amm_tick, ({
{sfAMMID, SoeRequired},
{sfTickIndex, SoeRequired},
{sfLiquidityNet, SoeRequired}, // stored as UINT64, interpreted as signed
{sfLiquidityGross, SoeRequired},
{sfFeeGrowthOutside0, SoeRequired},
{sfFeeGrowthOutside1, SoeRequired},
{sfOwnerNode, SoeRequired},
}))
LEDGER_ENTRY(ltAMM_TICK_BITMAP, 0x007c, AMMTickBitmap, amm_tick_bitmap, ({
{sfAMMID, SoeRequired},
{sfBitmapWordIndex, SoeRequired},
{sfBitmapBits, SoeRequired},
{sfOwnerNode, SoeRequired},
}))
LEDGER_ENTRY(ltAMM_BIN, 0x0096, AMMBin, amm_bin, ({
{sfAMMID, SoeRequired},
{sfBinID, SoeRequired},
{sfReserve0, SoeRequired},
{sfReserve1, SoeRequired},
{sfFeeGrowthBin0, SoeRequired},
{sfFeeGrowthBin1, SoeRequired},
{sfOutstandingAmount, SoeRequired},
{sfMPTokenIssuanceID, SoeOptional},
{sfOwnerNode, SoeRequired},
}))
LEDGER_ENTRY(ltAMM_BIN_HOLDING, 0x0097, AMMBinHolding, amm_bin_holding, ({
{sfAccount, SoeRequired},
{sfAMMID, SoeRequired},
{sfBinID, SoeRequired},
{sfFeeGrowthInsideLast0, SoeRequired},
{sfFeeGrowthInsideLast1, SoeRequired},
{sfOwnerNode, SoeRequired},
}))
LEDGER_ENTRY(ltSUBSCRIPTION, 0x008A, Subscription, subscription, ({
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfSequence, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfAccount, SoeRequired},
{sfDestination, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfAmount, SoeRequired},
{sfBalance, SoeRequired},
{sfFrequency, SoeRequired},
{sfNextClaimTime, SoeRequired},
{sfExpiration, SoeOptional},
{sfDestinationNode, SoeRequired},
}))
LEDGER_ENTRY(ltREPO, 0x0098, Repo, repo, ({
{sfAccount, SoeRequired},
{sfCounterparty, SoeRequired},
{sfCollateralAmount, SoeRequired, SoeMptSupported},
{sfPurchasePrice, SoeRequired, SoeMptSupported},
{sfInterestRate, SoeRequired},
{sfExpiration, SoeRequired},
{sfStartDate, SoeOptional},
{sfMaturityDate, SoeRequired},
{sfGracePeriod, SoeRequired},
{sfTransferRate, SoeOptional},
{sfData, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfDestinationNode, SoeRequired},
{sfIssuerNode, SoeOptional},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
/** A designation of an account to receive this account's regular key after a
period of inactivity.
\sa keylet::beneficiary
*/
LEDGER_ENTRY(ltBENEFICIARY, 0x0099, Beneficiary, beneficiary, ({
{sfAccount, SoeRequired},
{sfBeneficiary, SoeRequired},
{sfTimeLock, SoeRequired},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
LEDGER_ENTRY(ltFIREWALL, 0x009A, Firewall, firewall, ({
{sfOwner, SoeRequired},
{sfCounterparty, SoeRequired},
{sfMaxFee, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
LEDGER_ENTRY_DUPLICATE(ltWITHDRAW_PREAUTH, 0x009B, WithdrawPreauth, withdraw_preauth, ({
{sfAccount, SoeRequired},
{sfAuthorize, SoeRequired},
{sfDestinationTag, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))
#undef EXPAND
#undef LEDGER_ENTRY_DUPLICATE

View File

@@ -119,16 +119,6 @@ TYPED_SFIELD(sfRemainingOwnerCount, UINT32, 73)
TYPED_SFIELD(sfSponsorFlags, UINT32, 74)
TYPED_SFIELD(sfSubscriptionDate, UINT32, 75)
TYPED_SFIELD(sfRedemptionDate, UINT32, 76)
TYPED_SFIELD(sfIssuerKeyEpoch, UINT32, 77)
TYPED_SFIELD(sfAuditorKeyEpoch, UINT32, 78)
TYPED_SFIELD(sfIssuerKeyMirrorEpoch, UINT32, 79)
TYPED_SFIELD(sfAuditorKeyMirrorEpoch, UINT32, 80)
TYPED_SFIELD(sfGasLimit, UINT32, 81)
TYPED_SFIELD(sfBytecodeSizeLimit, UINT32, 82)
TYPED_SFIELD(sfGasPrice, UINT32, 83)
TYPED_SFIELD(sfGas, UINT32, 84)
TYPED_SFIELD(sfGasUsed, UINT32, 85)
TYPED_SFIELD(sfParameterFlag, UINT32, 86)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)
@@ -221,9 +211,6 @@ TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfReferenceHolding, UINT256, 39)
TYPED_SFIELD(sfBlindingFactor, UINT256, 40)
TYPED_SFIELD(sfObjectID, UINT256, 41)
TYPED_SFIELD(sfContractHash, UINT256, 42)
TYPED_SFIELD(sfContractID, UINT256, 43,
SField::kSmdPseudoAccount | SField::kSmdDefault)
// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)
@@ -247,7 +234,6 @@ TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::kSmdNeedsAsset
// 32-bit signed (common)
TYPED_SFIELD(sfLoanScale, INT32, 1)
TYPED_SFIELD(sfRemainingOwnerCountDelta, INT32, 2)
TYPED_SFIELD(sfVMReturnCode, INT32, 3)
// currency amount (common)
TYPED_SFIELD(sfAmount, AMOUNT, 1)
@@ -332,9 +318,6 @@ TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfBytecode, VL, 47)
TYPED_SFIELD(sfContractCode, VL, 48)
TYPED_SFIELD(sfFunctionName, VL, 49)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
@@ -366,7 +349,6 @@ TYPED_SFIELD(sfHighSponsor, ACCOUNT, 28)
TYPED_SFIELD(sfLowSponsor, ACCOUNT, 29)
TYPED_SFIELD(sfCounterpartySponsor, ACCOUNT, 30)
TYPED_SFIELD(sfSponsee, ACCOUNT, 31)
TYPED_SFIELD(sfContractAccount, ACCOUNT, 32)
// vector of 256-bit
TYPED_SFIELD(sfIndexes, VECTOR256, 1, SField::kSmdNever)
@@ -428,10 +410,6 @@ UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35)
UNTYPED_SFIELD(sfBook, OBJECT, 36)
UNTYPED_SFIELD(sfCounterpartySignature, OBJECT, 37, SField::kSmdDefault, SField::kNotSigning)
UNTYPED_SFIELD(sfSponsorSignature, OBJECT, 38, SField::kSmdDefault, SField::kNotSigning)
UNTYPED_SFIELD(sfFunction, OBJECT, 39)
UNTYPED_SFIELD(sfInstanceParameter, OBJECT, 40)
UNTYPED_SFIELD(sfInstanceParameterValue, OBJECT, 41)
UNTYPED_SFIELD(sfParameter, OBJECT, 42)
// array of objects (common)
// ARRAY/1 is reserved for end of array
@@ -464,113 +442,3 @@ UNTYPED_SFIELD(sfAcceptedCredentials, ARRAY, 28)
UNTYPED_SFIELD(sfPermissions, ARRAY, 29)
UNTYPED_SFIELD(sfRawTransactions, ARRAY, 30)
UNTYPED_SFIELD(sfBatchSigners, ARRAY, 31, SField::kSmdDefault, SField::kNotSigning)
UNTYPED_SFIELD(sfFunctions, ARRAY, 32)
UNTYPED_SFIELD(sfInstanceParameters, ARRAY, 33)
UNTYPED_SFIELD(sfInstanceParameterValues,ARRAY, 34)
UNTYPED_SFIELD(sfParameters, ARRAY, 35)
TYPED_SFIELD(sfParameterValue, DATA, 1, SField::kSmdDefault)
TYPED_SFIELD(sfParameterType, DATATYPE, 1)
TYPED_SFIELD(sfContractJson, JSON, 1)
TYPED_SFIELD(sfSignCount, UINT32, 87)
TYPED_SFIELD(sfPasskeyID, VL, 50)
TYPED_SFIELD(sfAuthenticatorData, VL, 51)
TYPED_SFIELD(sfClientDataJSON, VL, 52)
UNTYPED_SFIELD(sfPasskeySignature, OBJECT, 43, SField::kSmdDefault, SField::kNotSigning)
UNTYPED_SFIELD(sfPasskey, OBJECT, 44)
UNTYPED_SFIELD(sfPasskeys, ARRAY, 36)
TYPED_SFIELD(sfMPTokenSchema, VL, 53)
TYPED_SFIELD(sfTokenScale, UINT8, 7)
TYPED_SFIELD(sfTokenIssuanceID, UINT256, 44)
TYPED_SFIELD(sfIssuedAmount, NUMBER, 18, SField::kSmdNeedsAsset | SField::kSmdDefault)
TYPED_SFIELD(sfCurrency, CURRENCY, 3)
TYPED_SFIELD(sfAccountingMethod, UINT8, 23)
TYPED_SFIELD(sfLastAccrualTime, UINT32, 88)
TYPED_SFIELD(sfDealingInterval, UINT32, 89)
TYPED_SFIELD(sfDealingWindow, UINT32, 90)
TYPED_SFIELD(sfStruckUntil, UINT32, 91)
TYPED_SFIELD(sfDepositFee, UINT32, 92)
TYPED_SFIELD(sfRedemptionFee, UINT32, 93)
TYPED_SFIELD(sfRedemptionPeriod, UINT32, 94)
TYPED_SFIELD(sfRedemptionAfter, UINT32, 95)
TYPED_SFIELD(sfUnearnedInterest, NUMBER, 19, SField::kSmdNeedsAsset | SField::kSmdDefault)
TYPED_SFIELD(sfAccrualRate, NUMBER, 20, SField::kSmdDefault)
TYPED_SFIELD(sfStruckPrice, NUMBER, 21, SField::kSmdDefault)
TYPED_SFIELD(sfCouponInterval, UINT32, 96)
TYPED_SFIELD(sfFirstCouponTime, UINT32, 97)
TYPED_SFIELD(sfCallNoticePeriod, UINT32, 98)
TYPED_SFIELD(sfEarliestCallTime, UINT32, 99)
TYPED_SFIELD(sfLastCouponTime, UINT32, 100)
TYPED_SFIELD(sfClaimantCount, UINT32, 101)
TYPED_SFIELD(sfCouponCount, UINT32, 102)
TYPED_SFIELD(sfCouponAmount, AMOUNT, 35)
TYPED_SFIELD(sfAccruedPerUnit, AMOUNT, 36)
TYPED_SFIELD(sfPoolAmount, AMOUNT, 37)
TYPED_SFIELD(sfCouponAccrued, AMOUNT, 38)
TYPED_SFIELD(sfCouponIndex, AMOUNT, 39)
TYPED_SFIELD(sfCouponAsset, ISSUE, 5)
TYPED_SFIELD(sfOptionCount, UINT8, 24)
TYPED_SFIELD(sfOpenTime, UINT32, 103)
TYPED_SFIELD(sfVoteCount, UINT32, 104)
TYPED_SFIELD(sfBallotWeight, UINT64, 34)
TYPED_SFIELD(sfVoteLockedAmount, UINT64, 35, SField::kSmdBaseTen|SField::kSmdDefault)
TYPED_SFIELD(sfBallotID, UINT256, 45)
TYPED_SFIELD(sfEncryptedVote, VL, 54)
TYPED_SFIELD(sfTallyPublicKey, VL, 55)
TYPED_SFIELD(sfVoterPublicKey, VL, 56)
UNTYPED_SFIELD(sfBallotOption, OBJECT, 45)
UNTYPED_SFIELD(sfBallotResult, OBJECT, 46)
UNTYPED_SFIELD(sfEncryptedTally, ARRAY, 37)
UNTYPED_SFIELD(sfEncryptedVotes, ARRAY, 38)
UNTYPED_SFIELD(sfAuditorEncryptedVotes, ARRAY, 39)
UNTYPED_SFIELD(sfVoterEncryptedVotes, ARRAY, 40)
UNTYPED_SFIELD(sfResults, ARRAY, 41)
TYPED_SFIELD(sfCurveType, UINT8, 25)
TYPED_SFIELD(sfFeeTier, UINT8, 26)
TYPED_SFIELD(sfTickSpacing, UINT16, 23)
TYPED_SFIELD(sfWeight, UINT16, 24)
TYPED_SFIELD(sfBitmapWordIndex, UINT16, 25)
TYPED_SFIELD(sfBinStep, UINT16, 26)
TYPED_SFIELD(sfAmplification, UINT32, 105)
TYPED_SFIELD(sfAmplificationTarget, UINT32, 106)
TYPED_SFIELD(sfAmplificationTime, UINT32, 107)
TYPED_SFIELD(sfPositionCount, UINT32, 108)
TYPED_SFIELD(sfActiveLiquidity, UINT64, 39)
TYPED_SFIELD(sfPositionLiquidity, UINT64, 40)
TYPED_SFIELD(sfLiquidityGross, UINT64, 36)
TYPED_SFIELD(sfLiquidityNet, UINT64, 37)
TYPED_SFIELD(sfShares, UINT64, 38)
TYPED_SFIELD(sfSqrtPriceX96, UINT256, 46)
TYPED_SFIELD(sfBitmapBits, UINT256, 47)
TYPED_SFIELD(sfPositionID, UINT256, 48)
TYPED_SFIELD(sfFeeGrowthGlobal0, NUMBER, 22)
TYPED_SFIELD(sfFeeGrowthGlobal1, NUMBER, 23)
TYPED_SFIELD(sfFeeGrowthOutside0, NUMBER, 24)
TYPED_SFIELD(sfFeeGrowthOutside1, NUMBER, 25)
TYPED_SFIELD(sfFeeGrowthInsideLast0, NUMBER, 26)
TYPED_SFIELD(sfFeeGrowthInsideLast1, NUMBER, 27)
TYPED_SFIELD(sfFeeGrowthBin0, NUMBER, 28)
TYPED_SFIELD(sfFeeGrowthBin1, NUMBER, 29)
TYPED_SFIELD(sfTickLower, INT32, 8)
TYPED_SFIELD(sfTickUpper, INT32, 9)
TYPED_SFIELD(sfTickIndex, INT32, 4)
TYPED_SFIELD(sfCurrentTick, INT32, 5)
TYPED_SFIELD(sfBinID, INT32, 6)
TYPED_SFIELD(sfActiveBinID, INT32, 7)
TYPED_SFIELD(sfTokensOwed0, AMOUNT, 40)
TYPED_SFIELD(sfTokensOwed1, AMOUNT, 41)
TYPED_SFIELD(sfReserve0, AMOUNT, 42)
TYPED_SFIELD(sfReserve1, AMOUNT, 43)
TYPED_SFIELD(sfMinQuantity, AMOUNT, 44)
TYPED_SFIELD(sfFrequency, UINT32, 109)
TYPED_SFIELD(sfStartTime, UINT32, 110)
TYPED_SFIELD(sfNextClaimTime, UINT32, 111)
TYPED_SFIELD(sfSubscriptionID, UINT256, 49)
TYPED_SFIELD(sfMaturityDate, UINT32, 112)
TYPED_SFIELD(sfRepoID, UINT256, 50)
TYPED_SFIELD(sfCollateralAmount, AMOUNT, 45)
TYPED_SFIELD(sfPurchasePrice, AMOUNT, 46)
TYPED_SFIELD(sfTimeLock, UINT32, 113)
TYPED_SFIELD(sfLastInteraction, UINT32, 114)
TYPED_SFIELD(sfBeneficiary, ACCOUNT, 33)
TYPED_SFIELD(sfFirewallID, UINT256, 51)
TYPED_SFIELD(sfBackup, ACCOUNT, 34)

Some files were not shown because too many files have changed in this diff Show More