mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-24 06:00:17 +00:00
Compare commits
43 Commits
ripple/cos
...
ripple/cos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09467f931a | ||
|
|
f05f137c6c | ||
|
|
fac59004f5 | ||
|
|
842f3b3e2d | ||
|
|
30640a626f | ||
|
|
5a5ad8673a | ||
|
|
5265bd8e4e | ||
|
|
8f4e9c25d8 | ||
|
|
d8870162eb | ||
|
|
8b1a2282c3 | ||
|
|
0219c01b33 | ||
|
|
00606bec1a | ||
|
|
2bc17c3e73 | ||
|
|
0229c294a9 | ||
|
|
184fe173fb | ||
|
|
16f7b263fd | ||
|
|
f6b51f0b8b | ||
|
|
07b36871c7 | ||
|
|
638ae08eeb | ||
|
|
24be48ee84 | ||
|
|
e20f448a71 | ||
|
|
f6c80fef68 | ||
|
|
a0c12420b5 | ||
|
|
1245254cac | ||
|
|
b60636169a | ||
|
|
d6022fbc4d | ||
|
|
295b74da1c | ||
|
|
faa2bf583f | ||
|
|
da260fa42b | ||
|
|
53788b193d | ||
|
|
227f1b4d9c | ||
|
|
26b66957ec | ||
|
|
54e62a621f | ||
|
|
53628b70c0 | ||
|
|
6fec2c11bf | ||
|
|
04108a030c | ||
|
|
551a19b10d | ||
|
|
b0a940a383 | ||
|
|
e302e4eeed | ||
|
|
1a4a40ebb8 | ||
|
|
7f55dd390c | ||
|
|
9403736199 | ||
|
|
028783661d |
@@ -64,6 +64,7 @@ words:
|
||||
- blindings
|
||||
- bookdir
|
||||
- Bougalis
|
||||
- bthomee
|
||||
- Britto
|
||||
- Btrfs
|
||||
- Buildx
|
||||
@@ -141,6 +142,7 @@ words:
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
- Injectivity
|
||||
- insuf
|
||||
- insuff
|
||||
- invasively
|
||||
|
||||
@@ -82,7 +82,6 @@ test.app > xrpl.tx
|
||||
test.basics > test.jtx
|
||||
test.basics > xrpl.basics
|
||||
test.basics > xrpl.core
|
||||
test.basics > xrpld.rpc
|
||||
test.basics > xrpl.json
|
||||
test.basics > xrpl.protocol
|
||||
test.beast > xrpl.basics
|
||||
@@ -113,6 +112,7 @@ test.jtx > xrpl.config
|
||||
test.jtx > xrpl.core
|
||||
test.jtx > xrpld.app
|
||||
test.jtx > xrpld.core
|
||||
test.jtx > xrpld.overlay
|
||||
test.jtx > xrpld.rpc
|
||||
test.jtx > xrpl.json
|
||||
test.jtx > xrpl.ledger
|
||||
@@ -286,10 +286,10 @@ xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.config
|
||||
xrpld.perflog > xrpl.core
|
||||
xrpld.perflog > xrpld.app
|
||||
xrpld.perflog > xrpld.rpc
|
||||
xrpld.perflog > xrpl.json
|
||||
xrpld.perflog > xrpl.nodestore
|
||||
xrpld.perflog > xrpl.protocol
|
||||
xrpld.perflog > xrpl.server
|
||||
xrpld.rpc > xrpl.basics
|
||||
xrpld.rpc > xrpl.config
|
||||
xrpld.rpc > xrpl.core
|
||||
|
||||
46
.github/scripts/strategy-matrix/generate.py
vendored
46
.github/scripts/strategy-matrix/generate.py
vendored
@@ -15,6 +15,14 @@ _BASE_CMAKE_ARGS = [
|
||||
"-Drust=ON",
|
||||
]
|
||||
|
||||
# The package formats a config can be packaged as, each with its own
|
||||
# install-test job in reusable-package.yml.
|
||||
PACKAGE_TYPES = ("deb", "rpm")
|
||||
|
||||
# The package name a variant suffixes, as build_pkg.py's BASE_NAME spells it:
|
||||
# the two have to agree, or the artifact globs miss what was built.
|
||||
BASE_NAME = "xrpld"
|
||||
|
||||
# Maps sanitizer names (as used in cmake) to short config-name suffixes.
|
||||
_SANITIZER_SUFFIX: dict[str, str] = {
|
||||
"address": "asan",
|
||||
@@ -62,10 +70,20 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
||||
class PackageConfig:
|
||||
"""The 'package' map of a config whose binaries are also packaged."""
|
||||
|
||||
type: str # "deb" or "rpm"; has to match what the image provides
|
||||
type: str # has to match what the image provides
|
||||
# The packaging container image: a vanilla distro image, not the nix image
|
||||
# the config itself builds in.
|
||||
image: str
|
||||
# A flavour of the package, named xrpld-<variant>, for a config whose
|
||||
# binaries are not the plain release build. A variant needs no counterpart
|
||||
# in the other format.
|
||||
variant: str = ""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
assert self.type in PACKAGE_TYPES, (
|
||||
f"unsupported package type {self.type!r}: "
|
||||
f"use one of {', '.join(PACKAGE_TYPES)}."
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@@ -178,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
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -267,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.
|
||||
|
||||
@@ -341,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(
|
||||
|
||||
15
.github/scripts/strategy-matrix/linux.json
vendored
15
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"image_tag": "sha-473fe44",
|
||||
"image_tag": "sha-060957e",
|
||||
"configs": {
|
||||
"ubuntu": [
|
||||
{
|
||||
@@ -76,6 +76,19 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
6
.github/workflows/build-nix-images.yml
vendored
6
.github/workflows/build-nix-images.yml
vendored
@@ -5,15 +5,13 @@ on:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- ".github/workflows/build-nix-images.yml"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
- "rust-toolchain.toml"
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/*.txt"
|
||||
- "bin/check-tools.sh"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
pull_request:
|
||||
@@ -25,7 +23,7 @@ on:
|
||||
- "nix/**"
|
||||
- "!nix/docker/README.md"
|
||||
- "!nix/devshell.nix"
|
||||
- "!nix/check-tools/*.txt"
|
||||
- "!nix/check-tools/**"
|
||||
- "bin/check-tools.sh"
|
||||
- "bin/default-loader-path.sh"
|
||||
- "bin/install-sanitizer-libs.sh"
|
||||
|
||||
2
.github/workflows/cargo-audit.yml
vendored
2
.github/workflows/cargo-audit.yml
vendored
@@ -34,7 +34,7 @@ permissions:
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to open an issue on scheduled failures.
|
||||
|
||||
7
.github/workflows/on-pr.yml
vendored
7
.github/workflows/on-pr.yml
vendored
@@ -85,6 +85,7 @@ jobs:
|
||||
.github/workflows/reusable-build-test.yml
|
||||
.github/workflows/reusable-check-autogen.yml
|
||||
.github/workflows/reusable-clang-tidy.yml
|
||||
.github/workflows/reusable-package-test-install.yml
|
||||
.github/workflows/reusable-package.yml
|
||||
.github/workflows/reusable-rust.yml
|
||||
.github/workflows/reusable-strategy-matrix.yml
|
||||
@@ -189,6 +190,12 @@ jobs:
|
||||
# matrix (i.e. not yet labeled "Ready to merge" or "Full CI build").
|
||||
if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }}
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
# A pull request builds packages to prove they still build, and publishes
|
||||
# nothing. Stated rather than left to the input's default, so that changing
|
||||
# that default cannot start publishing from pull requests. No secrets are
|
||||
# passed either, which is the second reason a publish here cannot succeed.
|
||||
publish: false
|
||||
|
||||
upload-recipe:
|
||||
needs:
|
||||
|
||||
1
.github/workflows/on-trigger.yml
vendored
1
.github/workflows/on-trigger.yml
vendored
@@ -23,6 +23,7 @@ on:
|
||||
- ".github/workflows/reusable-build-test.yml"
|
||||
- ".github/workflows/reusable-check-autogen.yml"
|
||||
- ".github/workflows/reusable-clang-tidy.yml"
|
||||
- ".github/workflows/reusable-package-test-install.yml"
|
||||
- ".github/workflows/reusable-package.yml"
|
||||
- ".github/workflows/reusable-rust.yml"
|
||||
- ".github/workflows/reusable-strategy-matrix.yml"
|
||||
|
||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@@ -41,7 +41,7 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -439,7 +439,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository_owner == 'XRPLF' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
|
||||
2
.github/workflows/reusable-clang-tidy.yml
vendored
2
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
needs: [determine-files]
|
||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-473fe44"
|
||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-060957e"
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
120
.github/workflows/reusable-package-test-install.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# Install one package format on every distro family it targets, one job per
|
||||
# package name and image, and run the binaries there. Called once per format by
|
||||
# reusable-package.yml, which owns the names and the image lists.
|
||||
name: Install packages
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package_type:
|
||||
description: 'The package format to install ("deb" or "rpm").'
|
||||
required: true
|
||||
type: string
|
||||
package_names:
|
||||
description: "JSON array of package names built for this format."
|
||||
required: true
|
||||
type: string
|
||||
images:
|
||||
description: "JSON array of container images to install in."
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
PACKAGE_DIR: packages
|
||||
|
||||
jobs:
|
||||
install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package_name: ${{ fromJson(inputs.package_names) }}
|
||||
image: ${{ fromJson(inputs.images) }}
|
||||
name: "${{ matrix.package_name }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# Every package lands in one directory; the step below picks its own,
|
||||
# which keeps this independent of the artifact names.
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Find the package
|
||||
id: find
|
||||
env:
|
||||
PACKAGE_NAME: ${{ matrix.package_name }}
|
||||
PACKAGE_TYPE: ${{ inputs.package_type }}
|
||||
run: |
|
||||
# The version follows the name, separated by '_' in a DEB and '-' in an
|
||||
# RPM. Requiring a digit after it is what keeps 'xrpld' from picking up
|
||||
# another package, such as 'xrpld-assert'.
|
||||
pattern="${PACKAGE_NAME}[_-][0-9]*.${PACKAGE_TYPE}"
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "${pattern}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no ${pattern} found in ${PACKAGE_DIR}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "package=${package}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Debian 11 went end-of-life on 2026-08-31
|
||||
# (https://www.debian.org/News/2026/20260831) and its packages are
|
||||
# already partly gone from deb.debian.org, so switch to the
|
||||
# snapshot.debian.org entries the image ships commented out in its
|
||||
# sources.list: they are pinned to the snapshot the image was built
|
||||
# from, so they serve every version it needs and never go away.
|
||||
# Snapshots keep their original, long-passed Valid-Until, hence the
|
||||
# disabled check; the retries absorb snapshot.debian.org's throttling.
|
||||
- name: Switch Debian 11 to snapshot.debian.org
|
||||
if: ${{ matrix.image == 'debian:11' }}
|
||||
run: |
|
||||
sed -i 's|^deb |# deb |; s|^# deb http://snapshot|deb http://snapshot|' /etc/apt/sources.list
|
||||
printf '%s\n' \
|
||||
'Acquire::Check-Valid-Until "false";' \
|
||||
'Acquire::Retries "3";' \
|
||||
>/etc/apt/apt.conf.d/99snapshot
|
||||
|
||||
- name: Install the DEB
|
||||
if: ${{ inputs.package_type == 'deb' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: |
|
||||
# Stock Debian and Ubuntu images carry no package lists, so apt has
|
||||
# nothing to resolve the systemd dependency from until it fetches them.
|
||||
apt-get update -qq
|
||||
apt-get install -y "./${PACKAGE}"
|
||||
|
||||
- name: Install the RPM
|
||||
if: ${{ inputs.package_type == 'rpm' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: dnf install -y "./${PACKAGE}"
|
||||
|
||||
- name: Run xrpld
|
||||
run: xrpld --version
|
||||
|
||||
- name: Run validator-keys
|
||||
run: validator-keys --version
|
||||
|
||||
- name: Run rippled, the legacy compatibility symlink
|
||||
run: rippled --version
|
||||
|
||||
- name: Check the service account
|
||||
run: id xrpld
|
||||
|
||||
- name: Check the state directory
|
||||
run: test -d /var/lib/xrpld
|
||||
|
||||
- name: Check the log directory
|
||||
run: test -d /var/log/xrpld
|
||||
185
.github/workflows/reusable-package.yml
vendored
185
.github/workflows/reusable-package.yml
vendored
@@ -3,8 +3,10 @@
|
||||
#
|
||||
# - 'package' builds and signs one format per config that carries a "package"
|
||||
# map in linux.json; that map names the container image and the format
|
||||
# - 'test-install' installs what was built on a range of distros and runs the
|
||||
# binaries there, so a package that cannot be installed never reaches Nexus
|
||||
# - 'test-install-deb' and 'test-install-rpm' call
|
||||
# reusable-package-test-install.yml to install what was built on a range of
|
||||
# distros and run the binaries there, so a package that cannot be installed
|
||||
# never reaches Nexus
|
||||
# - 'publish' uploads with the image's publish_pkg.py, doing a --dry-run
|
||||
# unless 'publish: true'
|
||||
#
|
||||
@@ -23,7 +25,7 @@ on:
|
||||
description: "The base URL of the Nexus instance hosting the deb and rpm repositories."
|
||||
required: false
|
||||
type: string
|
||||
default: https://packages.xrplf.org
|
||||
default: https://packages-upload.xrplf.org
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
@@ -49,6 +51,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
deb_package_names: ${{ steps.generate.outputs.deb_package_names }}
|
||||
rpm_package_names: ${{ steps.generate.outputs.rpm_package_names }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -107,6 +111,7 @@ jobs:
|
||||
- name: Build package
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
PACKAGE_VARIANT: ${{ matrix.package_variant }}
|
||||
PKG_RELEASE: ${{ steps.release_info.outputs.pkg_release }}
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: |
|
||||
@@ -114,6 +119,7 @@ jobs:
|
||||
--package-type "${PACKAGE_TYPE}" \
|
||||
--build-dir "${BUILD_DIR}" \
|
||||
--pkg-release "${PKG_RELEASE}" \
|
||||
--variant "${PACKAGE_VARIANT}" \
|
||||
--channel "${CHANNEL}"
|
||||
|
||||
# Before the upload, so the artifact, the tested package and the published
|
||||
@@ -125,14 +131,17 @@ jobs:
|
||||
run: ./package/sign_rpm.py --package-dir "${BUILD_DIR}"
|
||||
|
||||
# Split from the debug symbols, which are an order of magnitude larger, so
|
||||
# that test-install downloads only what it installs.
|
||||
# that test-install downloads only what it installs. In the globs below the
|
||||
# version follows the name, separated by '_' in a DEB and '-' in an RPM. A
|
||||
# version starts with a digit and a longer name does not, so that one digit
|
||||
# is what tells 'xrpld-3.4.1-...' from 'xrpld-assert-3.4.1-...'.
|
||||
- name: Upload package artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld_*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-[0-9]*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload debug symbol artifact
|
||||
@@ -140,133 +149,59 @@ jobs:
|
||||
with:
|
||||
name: ${{ matrix.xrpld_artifact_name }}-pkg-debug
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/xrpld-dbgsym_*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/xrpld-debuginfo-*.rpm
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.deb
|
||||
${{ env.BUILD_DIR }}/debbuild/${{ matrix.package_name }}-dbgsym_[0-9]*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/${{ matrix.package_name }}-debuginfo-[0-9]*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
# Every distro family the packages target, oldest release first, so both ends
|
||||
# of the dependency range they declare are exercised.
|
||||
test-install:
|
||||
needs: [package]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- package_type: deb
|
||||
image: debian:11
|
||||
- package_type: deb
|
||||
image: debian:12
|
||||
- package_type: deb
|
||||
image: debian:13
|
||||
- package_type: deb
|
||||
image: ubuntu:20.04
|
||||
- package_type: deb
|
||||
image: ubuntu:22.04
|
||||
- package_type: deb
|
||||
image: ubuntu:24.04
|
||||
- package_type: deb
|
||||
image: ubuntu:26.04
|
||||
# One call per format, so a variant packaged for one format is installed for
|
||||
# that format alone. The images are every distro family that format targets,
|
||||
# oldest release first, so both ends of the dependency range the packages
|
||||
# declare are exercised.
|
||||
test-install-deb:
|
||||
needs: [generate-matrix, package]
|
||||
name: install deb
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: deb
|
||||
package_names: ${{ needs.generate-matrix.outputs.deb_package_names }}
|
||||
images: |
|
||||
[
|
||||
"debian:11",
|
||||
"debian:12",
|
||||
"debian:13",
|
||||
"ubuntu:20.04",
|
||||
"ubuntu:22.04",
|
||||
"ubuntu:24.04",
|
||||
"ubuntu:26.04"
|
||||
]
|
||||
|
||||
- package_type: rpm
|
||||
image: almalinux:9
|
||||
- package_type: rpm
|
||||
image: almalinux:10
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:9
|
||||
- package_type: rpm
|
||||
image: rockylinux/rockylinux:10
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi9/ubi
|
||||
- package_type: rpm
|
||||
image: registry.access.redhat.com/ubi10/ubi
|
||||
name: "install ${{ matrix.package_type }} on ${{ matrix.image }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.image }}
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# Both formats land in one directory; the step below picks its own by
|
||||
# extension, so this stays independent of the artifact names.
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: "*-pkg"
|
||||
merge-multiple: true
|
||||
path: ${{ env.PACKAGE_DIR }}
|
||||
|
||||
- name: Find the package
|
||||
id: find
|
||||
env:
|
||||
PACKAGE_TYPE: ${{ matrix.package_type }}
|
||||
run: |
|
||||
package="$(find "${PACKAGE_DIR}" -type f -name "*.${PACKAGE_TYPE}" -print -quit)"
|
||||
test -n "${package}" || {
|
||||
echo "no .${PACKAGE_TYPE} found in ${PACKAGE_DIR}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "package=${package}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Debian 11 went end-of-life on 2026-08-31
|
||||
# (https://www.debian.org/News/2026/20260831) and its packages are
|
||||
# already partly gone from deb.debian.org, so switch to the
|
||||
# snapshot.debian.org entries the image ships commented out in its
|
||||
# sources.list: they are pinned to the snapshot the image was built
|
||||
# from, so they serve every version it needs and never go away.
|
||||
# Snapshots keep their original, long-passed Valid-Until, hence the
|
||||
# disabled check; the retries absorb snapshot.debian.org's throttling.
|
||||
- name: Switch Debian 11 to snapshot.debian.org
|
||||
if: ${{ matrix.image == 'debian:11' }}
|
||||
run: |
|
||||
sed -i 's|^deb |# deb |; s|^# deb http://snapshot|deb http://snapshot|' /etc/apt/sources.list
|
||||
printf '%s\n' \
|
||||
'Acquire::Check-Valid-Until "false";' \
|
||||
'Acquire::Retries "3";' \
|
||||
>/etc/apt/apt.conf.d/99snapshot
|
||||
|
||||
- name: Install the DEB
|
||||
if: ${{ matrix.package_type == 'deb' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: |
|
||||
# Stock Debian and Ubuntu images carry no package lists, so apt has
|
||||
# nothing to resolve the systemd dependency from until it fetches them.
|
||||
apt-get update -qq
|
||||
apt-get install -y "./${PACKAGE}"
|
||||
|
||||
- name: Install the RPM
|
||||
if: ${{ matrix.package_type == 'rpm' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.find.outputs.package }}
|
||||
run: dnf install -y "./${PACKAGE}"
|
||||
|
||||
- name: Run xrpld
|
||||
run: xrpld --version
|
||||
|
||||
- name: Run validator-keys
|
||||
run: validator-keys --version
|
||||
|
||||
- name: Run rippled, the legacy compatibility symlink
|
||||
run: rippled --version
|
||||
|
||||
- name: Check the service account
|
||||
run: id xrpld
|
||||
|
||||
- name: Check the state directory
|
||||
run: test -d /var/lib/xrpld
|
||||
|
||||
- name: Check the log directory
|
||||
run: test -d /var/log/xrpld
|
||||
test-install-rpm:
|
||||
needs: [generate-matrix, package]
|
||||
name: install rpm
|
||||
uses: ./.github/workflows/reusable-package-test-install.yml
|
||||
with:
|
||||
package_type: rpm
|
||||
package_names: ${{ needs.generate-matrix.outputs.rpm_package_names }}
|
||||
images: |
|
||||
[
|
||||
"almalinux:9",
|
||||
"almalinux:10",
|
||||
"rockylinux/rockylinux:9",
|
||||
"rockylinux/rockylinux:10",
|
||||
"registry.access.redhat.com/ubi9/ubi",
|
||||
"registry.access.redhat.com/ubi10/ubi"
|
||||
]
|
||||
|
||||
publish:
|
||||
needs: [generate-matrix, package, test-install]
|
||||
needs: [generate-matrix, package, test-install-deb, test-install-rpm]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}"
|
||||
# The name says which of the two this is, because the job runs either way:
|
||||
# with publish false it passes --dry-run and uploads nothing, and a job
|
||||
# called "publish ..." succeeding on a pull request reads like a release.
|
||||
name: "publish ${{ matrix.xrpld_artifact_name }}${{ !inputs.publish && ' (dry run)' || '' }}"
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||
|
||||
16
.github/workflows/reusable-rust.yml
vendored
16
.github/workflows/reusable-rust.yml
vendored
@@ -1,8 +1,9 @@
|
||||
# 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, coverage instrumentation and the cargo cache) matches
|
||||
# what production builds use.
|
||||
# (and therefore the lints and the cargo cache) matches what production builds
|
||||
# use. Coverage is the exception: it needs the nightly rustc that honours
|
||||
# #[coverage(off)], which the image carries alongside the pinned stable.
|
||||
#
|
||||
# Rust unit tests are deliberately NOT run here. They run as part of the C++
|
||||
# build (reusable-build-test-config.yml), which already compiles the crates on a
|
||||
@@ -27,7 +28,7 @@ permissions:
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -40,11 +41,14 @@ jobs:
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Use the nightly Rust toolchain
|
||||
run: rust-nightly path >>"${GITHUB_PATH}"
|
||||
|
||||
- name: Use cargo artifacts cache
|
||||
uses: ./.github/actions/cargo-cache
|
||||
|
||||
@@ -53,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage report
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
disable_search: true
|
||||
disable_telem: true
|
||||
@@ -66,7 +70,7 @@ jobs:
|
||||
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
2
.github/workflows/reusable-upload-recipe.yml
vendored
2
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -40,7 +40,7 @@ defaults:
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-473fe44
|
||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-060957e
|
||||
env:
|
||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||
|
||||
@@ -53,6 +53,11 @@ repos:
|
||||
entry: ./bin/pre-commit/check_doxygen_style.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
- id: fix-gtest-names
|
||||
name: "fix gtest names: CamelCase suite, snake_case test case"
|
||||
entry: ./bin/pre-commit/fix_gtest_names.py
|
||||
language: python
|
||||
types_or: [c++, c]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8
|
||||
@@ -70,6 +75,11 @@ 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
|
||||
|
||||
@@ -41,6 +41,7 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta
|
||||
- `gateway_balances`: The `account` and `ident` fields now return an `invalidParams` error if the value is not a string, instead of an `internal` error. [#7655](https://github.com/XRPLF/rippled/pull/7655)
|
||||
- `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728)
|
||||
- `ledger`: `delivered_amount` is now included in the metadata of successful `AccountDelete` transactions when transactions are expanded (`expand`, or admin-only `full`). Previously it was only added for `Payment` and `CheckCash`, which made `ledger` inconsistent with `tx` and `account_tx`. [#5706](https://github.com/XRPLF/rippled/pull/5706)
|
||||
- `noripple_check`: The `transactions` field is no longer included in error responses; it is still returned (possibly as an empty array) whenever `transactions` is `true` and the request succeeds. A malformed `account` is now rejected before the ledger is looked up, so that error response no longer carries the `ledger_hash`, `ledger_index`, and `validated` fields ([#6303](https://github.com/XRPLF/rippled/pull/6303)).
|
||||
|
||||
## XRP Ledger server version 3.3.0
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ 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
|
||||
|
||||
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
149
bin/pre-commit/check_rust_coverage_attrs.py
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check that Rust unit tests stay out of the coverage report.
|
||||
|
||||
cargo-llvm-cov instruments the test code along with everything else, so a test
|
||||
module that is not excluded counts its own body as covered and inflates the
|
||||
reported number. Excluding it takes two attributes:
|
||||
|
||||
* every `#[cfg(test)]` module carries
|
||||
`#[cfg_attr(coverage_nightly, coverage(off))]`;
|
||||
* every crate root (lib.rs, main.rs) carries
|
||||
`#![cfg_attr(coverage_nightly, feature(coverage_attribute))]`, which the
|
||||
attribute above needs in order to compile.
|
||||
|
||||
Both are inert outside the coverage job: cargo-llvm-cov defines
|
||||
`coverage_nightly` only when it runs on a nightly toolchain.
|
||||
|
||||
The crate-root gate is checked even in a crate that has no tests yet, because
|
||||
that is what lets the first test module added later carry the attribute without
|
||||
a build failure. Missing it is a hard error, so it cannot go unnoticed; a
|
||||
missing `coverage(off)` fails open, which is why this check exists.
|
||||
|
||||
Matching is on exact attribute text, which works because `cargo fmt` runs over
|
||||
the whole workspace in the hook ahead of this one: rustfmt puts every attribute
|
||||
on its own line and normalizes what is inside it, turning `#[cfg( test )]`
|
||||
and `#[cfg(test,)]` alike into `#[cfg(test)]`. So there is nothing here that
|
||||
parses Rust. The price is that a cfg this file does not spell out literally --
|
||||
`all(test, ...)`, `any(test, ...)`, `not(test)` -- is reported rather than
|
||||
classified, on the grounds that guessing at coverage semantics is how a check
|
||||
like this ends up quietly wrong.
|
||||
|
||||
Usage: ./bin/pre-commit/check_rust_coverage_attrs.py <file1> <file2> ...
|
||||
|
||||
Exit status is non-zero if any violation is found.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
CRATE_ROOTS = {"lib.rs", "main.rs"}
|
||||
|
||||
FEATURE_ATTR = "#![cfg_attr(coverage_nightly, feature(coverage_attribute))]"
|
||||
COVERAGE_OFF_ATTR = "#[cfg_attr(coverage_nightly, coverage(off))]"
|
||||
CFG_TEST_ATTR = "#[cfg(test)]"
|
||||
|
||||
# Any other cfg that mentions `test`. String literals are blanked before this
|
||||
# runs, so `feature = "test"` does not read as the `test` cfg.
|
||||
RE_CFG_MENTIONS_TEST = re.compile(r"^#\[cfg\(.*\btest\b.*\)\]$")
|
||||
RE_STRING = re.compile(r'"(?:[^"\\]|\\.)*"')
|
||||
RE_MOD = re.compile(r"^(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Finding:
|
||||
line: int
|
||||
label: str
|
||||
message: str
|
||||
|
||||
|
||||
def _check_module(attrs: list[str], line: int, name: str) -> list[Finding]:
|
||||
"""Findings for one module, given the attributes attached to it."""
|
||||
if COVERAGE_OFF_ATTR in attrs:
|
||||
return [] # excluded from coverage; which cfg gates it does not matter
|
||||
if CFG_TEST_ATTR in attrs:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"missing-coverage-off",
|
||||
f"`mod {name}` is #[cfg(test)] but not excluded from coverage; "
|
||||
f"add {COVERAGE_OFF_ATTR}",
|
||||
)
|
||||
]
|
||||
unclassified = [
|
||||
attr for attr in attrs if RE_CFG_MENTIONS_TEST.match(RE_STRING.sub('""', attr))
|
||||
]
|
||||
if unclassified:
|
||||
return [
|
||||
Finding(
|
||||
line,
|
||||
"unclassified-cfg",
|
||||
f"`mod {name}` is gated on {unclassified[0]}, which this check "
|
||||
f"cannot tell apart from a module that ships in the library; "
|
||||
f"add {COVERAGE_OFF_ATTR} if it is test-only, or teach this "
|
||||
f"check the cfg if it is not",
|
||||
)
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def _check_test_modules(lines: list[str]) -> list[Finding]:
|
||||
"""Findings for every test module that is not excluded from coverage."""
|
||||
findings: list[Finding] = []
|
||||
attrs: list[str] = []
|
||||
attrs_line = 0
|
||||
for number, raw in enumerate(lines, start=1):
|
||||
stripped = raw.strip()
|
||||
# Blank lines and comments are allowed between an attribute and its item.
|
||||
if not stripped or stripped.startswith("//"):
|
||||
continue
|
||||
if stripped.startswith("#["):
|
||||
if not attrs:
|
||||
attrs_line = number
|
||||
attrs.append(stripped)
|
||||
continue
|
||||
module = RE_MOD.match(stripped)
|
||||
if module is not None and attrs:
|
||||
findings += _check_module(attrs, attrs_line, module.group(1))
|
||||
attrs = []
|
||||
return findings
|
||||
|
||||
|
||||
def _check_crate_root(name: str, lines: list[str]) -> list[Finding]:
|
||||
"""A finding if a crate root is missing the coverage_attribute feature gate."""
|
||||
if name not in CRATE_ROOTS:
|
||||
return []
|
||||
if any(line.strip() == FEATURE_ATTR for line in lines):
|
||||
return []
|
||||
return [
|
||||
Finding(
|
||||
1,
|
||||
"missing-feature-gate",
|
||||
f"crate root is missing {FEATURE_ATTR}",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def check_source(name: str, text: str) -> list[Finding]:
|
||||
"""Findings for one file's contents; `name` is its base name (lib.rs, ...)."""
|
||||
lines = text.splitlines()
|
||||
return _check_crate_root(name, lines) + _check_test_modules(lines)
|
||||
|
||||
|
||||
def check_file(path: Path) -> list[Finding]:
|
||||
return check_source(path.name, path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
total = 0
|
||||
for path in (Path(name) for name in sys.argv[1:]):
|
||||
for finding in check_file(path):
|
||||
total += 1
|
||||
print(f"{path}:{finding.line}: {finding.label}: {finding.message}")
|
||||
return 1 if total else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
144
bin/pre-commit/fix_gtest_names.py
Executable file
144
bin/pre-commit/fix_gtest_names.py
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Rewrites gtest names to the required style in this project: the suite name is
|
||||
CamelCase, the test-case name is snake_case.
|
||||
|
||||
TEST(SuiteName, test_case_name)
|
||||
|
||||
The gtest `DISABLED_` prefix is kept verbatim on either name.
|
||||
|
||||
Both conversions fold acronyms the way a reader expects:
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`, not
|
||||
`set_and_reset_account_txn_i_d`.
|
||||
|
||||
The first argument of `TEST_F`, `TEST_P`, `TYPED_TEST` and `TYPED_TEST_P` is a
|
||||
fixture class rather than a free identifier, so rewriting it here would leave
|
||||
the class it names behind. Those are reported for a human to rename (clang-tidy
|
||||
checks the class declaration itself, via readability-identifier-naming).
|
||||
|
||||
Usage: ./bin/pre-commit/fix_gtest_names.py <file1> <file2> ...
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
# A test-case definition, `MACRO(SuiteOrFixture, TestName)`, anchored at the
|
||||
# start of a line so that commented-out definitions and project macros that
|
||||
# merely look similar (`TEST_EXPECT(...)`) are left alone. The `\s*` between
|
||||
# arguments allows for a definition clang-format wrapped over several lines.
|
||||
PATTERN = re.compile(
|
||||
r"(?P<head>^[ \t]*(?P<macro>TYPED_TEST_P|TYPED_TEST|TEST_F|TEST_P|TEST)\s*\(\s*)"
|
||||
r"(?P<suite>\w+)(?P<mid>\s*,\s*)(?P<name>\w+)(?P<tail>\s*\))",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
# The macros whose first argument names a fixture class, not a free identifier.
|
||||
FIXTURE_MACROS = ("TEST_F", "TEST_P", "TYPED_TEST", "TYPED_TEST_P")
|
||||
|
||||
DISABLED = "DISABLED_"
|
||||
|
||||
ACRONYM_BOUNDARY = re.compile(r"([A-Z]+)([A-Z][a-z])")
|
||||
WORD_BOUNDARY = re.compile(r"([a-z\d])([A-Z])")
|
||||
|
||||
|
||||
def _split_disabled(name: str) -> tuple[str, str]:
|
||||
"""Splits off gtest's `DISABLED_` prefix, which is kept verbatim."""
|
||||
if name.startswith(DISABLED):
|
||||
return DISABLED, name[len(DISABLED) :]
|
||||
return "", name
|
||||
|
||||
|
||||
def snake_case(name: str) -> str:
|
||||
"""Returns the name in snake_case, leaving acronyms whole.
|
||||
|
||||
`SetAndResetAccountTxnID` -> `set_and_reset_account_txn_id`,
|
||||
`parseStatRSSkB` -> `parse_stat_rs_sk_b`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
core = ACRONYM_BOUNDARY.sub(r"\1_\2", core)
|
||||
return prefix + WORD_BOUNDARY.sub(r"\1_\2", core).lower()
|
||||
|
||||
|
||||
def camel_case(name: str) -> str:
|
||||
"""Returns the name in CamelCase, capitalizing each underscored word.
|
||||
|
||||
Only the letters that have to change are touched, so acronyms survive: a
|
||||
conversion that went via snake_case would turn `SHAMapTest` into
|
||||
`ShaMapTest`, whereas here it is already CamelCase and stays put.
|
||||
`json_value` -> `JsonValue`, `parseStatRSSkB` -> `ParseStatRSSkB`.
|
||||
"""
|
||||
prefix, core = _split_disabled(name)
|
||||
return prefix + "".join(w[:1].upper() + w[1:] for w in core.split("_") if w)
|
||||
|
||||
|
||||
def _corrected(match: re.Match) -> tuple[str, str]:
|
||||
"""Returns the suite and test-case names this definition should end up with."""
|
||||
suite = match["suite"]
|
||||
return (
|
||||
suite if match["macro"] in FIXTURE_MACROS else camel_case(suite),
|
||||
snake_case(match["name"]),
|
||||
)
|
||||
|
||||
|
||||
def fix_source(text: str) -> tuple[str, list[str]]:
|
||||
"""Returns the corrected text and one `line: message` report per bad name."""
|
||||
# gtest joins the suite and test names into one class name, so two test
|
||||
# cases whose joined names agree cannot coexist: `TEST(a, b_c)` and
|
||||
# `TEST(a_b, c)` both define `a_b_c_Test`. A rename that would introduce
|
||||
# such a clash is reported for a human instead of applied.
|
||||
joined = Counter("_".join(_corrected(m)) for m in PATTERN.finditer(text))
|
||||
reports = []
|
||||
|
||||
def rewrite(match: re.Match) -> str:
|
||||
suite, name = match["suite"], match["name"]
|
||||
new_suite, new_name = _corrected(match)
|
||||
line = text.count("\n", 0, match.start()) + 1
|
||||
|
||||
if match["macro"] in FIXTURE_MACROS and camel_case(suite) != suite:
|
||||
reports.append(
|
||||
f"{line}: fixture '{suite}' is not CamelCase: rename the class "
|
||||
f"to '{camel_case(suite)}' by hand"
|
||||
)
|
||||
if (new_suite, new_name) == (suite, name):
|
||||
return match[0]
|
||||
if joined[f"{new_suite}_{new_name}"] > 1:
|
||||
reports.append(
|
||||
f"{line}: cannot rename '{suite}, {name}' to '{new_suite}, "
|
||||
f"{new_name}': another test case already generates that name"
|
||||
)
|
||||
return match[0]
|
||||
if new_suite != suite:
|
||||
reports.append(f"{line}: renamed suite '{suite}' to '{new_suite}'")
|
||||
if new_name != name:
|
||||
reports.append(f"{line}: renamed test case '{name}' to '{new_name}'")
|
||||
return match["head"] + new_suite + match["mid"] + new_name + match["tail"]
|
||||
|
||||
return PATTERN.sub(rewrite, text), reports
|
||||
|
||||
|
||||
def fix_names(path: Path) -> bool:
|
||||
"""Corrects one file's gtest names, reporting each on stdout."""
|
||||
original = path.read_text(encoding="utf-8")
|
||||
fixed, reports = fix_source(original)
|
||||
for report in reports:
|
||||
print(f"{path}:{report}")
|
||||
if fixed != original:
|
||||
path.write_text(fixed, encoding="utf-8")
|
||||
return not reports
|
||||
|
||||
|
||||
def main() -> int:
|
||||
files = [Path(f) for f in sys.argv[1:]]
|
||||
success = True
|
||||
|
||||
for path in files:
|
||||
success &= fix_names(path)
|
||||
|
||||
return 0 if success else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
259
bin/pre-commit/test_fix_gtest_names.py
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for fix_gtest_names.py.
|
||||
|
||||
Run directly (no test framework needed):
|
||||
./bin/pre-commit/test_fix_gtest_names.py
|
||||
or under pytest:
|
||||
pytest bin/pre-commit/test_fix_gtest_names.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from fix_gtest_names import camel_case, fix_source, snake_case
|
||||
|
||||
|
||||
def dedent(text: str) -> str:
|
||||
"""Removes a fixture's common indentation and its leading newline.
|
||||
|
||||
Lets fixtures be written as indented triple-quoted here-docs while keeping
|
||||
honest 1-based line numbers.
|
||||
"""
|
||||
return textwrap.dedent(text).lstrip("\n")
|
||||
|
||||
|
||||
def fixed(text: str) -> str:
|
||||
return fix_source(dedent(text))[0]
|
||||
|
||||
|
||||
def reports(text: str) -> list[str]:
|
||||
return fix_source(dedent(text))[1]
|
||||
|
||||
|
||||
# --- conversion --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_snake_case_conversion() -> None:
|
||||
assert snake_case("BadInputs") == "bad_inputs"
|
||||
assert snake_case("mulDiv") == "mul_div"
|
||||
assert snake_case("already_snake") == "already_snake"
|
||||
assert snake_case("base64") == "base64"
|
||||
|
||||
|
||||
def test_snake_case_keeps_acronyms_whole() -> None:
|
||||
assert snake_case("SetAndResetAccountTxnID") == "set_and_reset_account_txn_id"
|
||||
assert snake_case("XRPToIOU") == "xrp_to_iou"
|
||||
assert snake_case("STAmountMath") == "st_amount_math"
|
||||
|
||||
|
||||
def test_camel_case_conversion() -> None:
|
||||
assert camel_case("json_value") == "JsonValue"
|
||||
assert camel_case("mulDiv") == "MulDiv"
|
||||
assert camel_case("scope") == "Scope"
|
||||
assert camel_case("base64") == "Base64"
|
||||
|
||||
|
||||
def test_camel_case_leaves_acronyms_alone() -> None:
|
||||
# A snake_case round-trip would give `ShaMapTest` / `ParseStatmRsSkB` here.
|
||||
assert camel_case("SHAMapTest") == "SHAMapTest"
|
||||
assert camel_case("parseStatmRSSkB") == "ParseStatmRSSkB"
|
||||
assert camel_case("XRPAmount") == "XRPAmount"
|
||||
assert camel_case("CSPRNG") == "CSPRNG"
|
||||
|
||||
|
||||
def test_disabled_prefix_preserved() -> None:
|
||||
assert snake_case("DISABLED_FooBar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_foo_bar") == "DISABLED_foo_bar"
|
||||
assert snake_case("DISABLED_") == "DISABLED_"
|
||||
assert camel_case("DISABLED_foo_bar") == "DISABLED_FooBar"
|
||||
assert camel_case("DISABLED_") == "DISABLED_"
|
||||
|
||||
|
||||
# --- what counts as a test definition ---------------------------------------
|
||||
|
||||
|
||||
def test_all_macros_recognized() -> None:
|
||||
code = """
|
||||
TEST(Suite, oneName)
|
||||
TEST_F(Fixture, twoName)
|
||||
TEST_P(Fixture, threeName)
|
||||
TYPED_TEST(Fixture, fourName)
|
||||
TYPED_TEST_P(Fixture, fiveName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(Suite, one_name)
|
||||
TEST_F(Fixture, two_name)
|
||||
TEST_P(Fixture, three_name)
|
||||
TYPED_TEST(Fixture, four_name)
|
||||
TYPED_TEST_P(Fixture, five_name)
|
||||
""")
|
||||
|
||||
|
||||
def test_conforming_definitions_untouched() -> None:
|
||||
code = """
|
||||
TEST(AccountSet, bad_inputs)
|
||||
TEST_F(MutexMakeTest, default_constructor)
|
||||
TEST(SHAMap, DISABLED_slow_path)
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_lookalikes_ignored() -> None:
|
||||
code = """
|
||||
// TEST(Suite, notATest)
|
||||
TEST_EXPECT(someCall())
|
||||
TEST_EXPECTS(amount == value, amount.getText())
|
||||
INSTANTIATE_TEST_SUITE_P(Prefix, Fixture, testValues());
|
||||
auto x = TEST(Suite, notATest);
|
||||
TYPED_TEST_SUITE(Fixture, MyTypes);
|
||||
"""
|
||||
assert reports(code) == []
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_indented_and_wrapped_definitions() -> None:
|
||||
code = """
|
||||
namespace ripple {
|
||||
TEST(Suite, indentedName)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrappedName)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
namespace ripple {
|
||||
TEST(Suite, indented_name)
|
||||
}
|
||||
TEST_F(
|
||||
SomeVeryLongFixtureName,
|
||||
wrapped_name)
|
||||
""")
|
||||
|
||||
|
||||
# --- rewriting --------------------------------------------------------------
|
||||
|
||||
|
||||
def test_only_the_two_names_are_rewritten() -> None:
|
||||
code = """
|
||||
TEST(mulDiv, mulDiv)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(MulDiv, mul_div)
|
||||
{
|
||||
auto const mulDiv = 1; // mulDiv stays
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
def test_suite_name_camel_cased() -> None:
|
||||
code = """
|
||||
TEST(json_value, limits)
|
||||
TEST(scope, ScopeExit)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: renamed suite 'json_value' to 'JsonValue'",
|
||||
"2: renamed suite 'scope' to 'Scope'",
|
||||
"2: renamed test case 'ScopeExit' to 'scope_exit'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(JsonValue, limits)
|
||||
TEST(Scope, scope_exit)
|
||||
""")
|
||||
|
||||
|
||||
def test_fixture_reported_but_not_renamed() -> None:
|
||||
# The first argument names a class, so only a human (or clang-tidy) can
|
||||
# rename it; the test-case name is still fixed.
|
||||
code = """
|
||||
TEST_F(my_fixture, someTest)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: fixture 'my_fixture' is not CamelCase: rename the class to "
|
||||
"'MyFixture' by hand",
|
||||
"1: renamed test case 'someTest' to 'some_test'",
|
||||
]
|
||||
assert fixed(code) == dedent("""
|
||||
TEST_F(my_fixture, some_test)
|
||||
""")
|
||||
|
||||
|
||||
def test_reports_carry_line_numbers() -> None:
|
||||
code = """
|
||||
#include <foo.h>
|
||||
|
||||
TEST(Suite, firstName)
|
||||
|
||||
TEST(Suite, secondName)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"3: renamed test case 'firstName' to 'first_name'",
|
||||
"5: renamed test case 'secondName' to 'second_name'",
|
||||
]
|
||||
|
||||
|
||||
# --- collisions -------------------------------------------------------------
|
||||
|
||||
|
||||
def test_collision_reported_and_not_applied() -> None:
|
||||
# Both would define `Suite_mul_div_Test`.
|
||||
code = """
|
||||
TEST(Suite, mulDiv)
|
||||
TEST(Suite, mul_div)
|
||||
"""
|
||||
assert reports(code) == [
|
||||
"1: cannot rename 'Suite, mulDiv' to 'Suite, mul_div': another test "
|
||||
"case already generates that name"
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_collision_between_converging_suites() -> None:
|
||||
# Both suites camel-case to `SuiteA`, so both would define
|
||||
# `SuiteA_one_test_Test`.
|
||||
code = """
|
||||
TEST(SuiteA, oneTest)
|
||||
TEST(Suite_a, one_test)
|
||||
"""
|
||||
assert [r.split(":")[1].strip() for r in reports(code)] == [
|
||||
"cannot rename 'SuiteA, oneTest' to 'SuiteA, one_test'",
|
||||
"cannot rename 'Suite_a, one_test' to 'SuiteA, one_test'",
|
||||
]
|
||||
assert fixed(code) == dedent(code)
|
||||
|
||||
|
||||
def test_same_name_in_different_suites_is_not_a_collision() -> None:
|
||||
code = """
|
||||
TEST(SuiteOne, mulDiv)
|
||||
TEST(SuiteTwo, mulDiv)
|
||||
"""
|
||||
assert fixed(code) == dedent("""
|
||||
TEST(SuiteOne, mul_div)
|
||||
TEST(SuiteTwo, mul_div)
|
||||
""")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
tests = sorted(
|
||||
(name, fn)
|
||||
for name, fn in globals().items()
|
||||
if name.startswith("test_") and callable(fn)
|
||||
)
|
||||
failed = 0
|
||||
for name, fn in tests:
|
||||
try:
|
||||
fn()
|
||||
print(f"PASS {name}")
|
||||
except AssertionError as exc:
|
||||
failed += 1
|
||||
print(f"FAIL {name}: {exc!r}")
|
||||
print(f"\n{len(tests) - failed}/{len(tests)} passed")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -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"
|
||||
|
||||
@@ -149,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/*",
|
||||
|
||||
@@ -8,6 +8,9 @@ cxx = { version = "1.0.198", features = ["c++20"] }
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(coverage)', 'cfg(coverage_nightly)' ] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
overflow-checks = true
|
||||
|
||||
@@ -8,3 +8,6 @@ crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
cxx.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
|
||||
|
||||
#[cxx::bridge(namespace = "rs::hello_world")]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
@@ -8,3 +10,14 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -162,4 +162,89 @@ toUInt64(std::string const& s);
|
||||
bool
|
||||
isProperlyFormedTomlDomain(std::string_view domain);
|
||||
|
||||
/**
|
||||
* Whether a view can be passed on as a C string.
|
||||
*
|
||||
* A reader given only data() stops at the first null, so the view must reach the
|
||||
* terminating null. The test rebuilds the view from data() and compares: a view
|
||||
* that stops earlier rebuilds longer, and so compares unequal.
|
||||
*
|
||||
* consteval because reading the byte after the view is only defined when @p str
|
||||
* points into storage holding a null at or after its end, such as a string
|
||||
* literal. An unterminated view is then a compile error, not an out-of-bounds
|
||||
* read.
|
||||
*
|
||||
* @param str The view to test.
|
||||
* @return Whether @p str is null-terminated. A view with no data is not.
|
||||
*/
|
||||
consteval bool
|
||||
isNullTerminated(std::string_view str)
|
||||
{
|
||||
if (str.data() == nullptr)
|
||||
return false;
|
||||
|
||||
// Reading past the view is the point, so the usual data() warning does not
|
||||
// apply.
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
|
||||
return std::string_view{str.data()} == str;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that is known to reach its terminating null.
|
||||
*
|
||||
* Converts to std::string_view, so it compares and hashes as one. Unlike a
|
||||
* view, asCString() may be handed to a reader that expects a C string, such
|
||||
* as json::StaticString.
|
||||
*
|
||||
* The only constructor is consteval and rejects a view that stops before the
|
||||
* null, so the property holds by construction and no caller asserts it.
|
||||
*/
|
||||
class NullTerminatedView
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Build a view from one that reaches its terminating null.
|
||||
*
|
||||
* Explicit, so that a plain view cannot become a proof of termination by
|
||||
* accident. The conversion the other way stays implicit.
|
||||
*
|
||||
* @param view The string to hold. Rejected at compile time if it stops
|
||||
* before its terminating null, or has no data.
|
||||
*/
|
||||
explicit consteval NullTerminatedView(std::string_view view)
|
||||
: data_(view.data()), size_(view.size())
|
||||
{
|
||||
if (!isNullTerminated(view))
|
||||
throw "xrpl::NullTerminatedView : view does not reach a null";
|
||||
}
|
||||
|
||||
constexpr
|
||||
operator std::string_view() const noexcept
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a view.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view
|
||||
view() const noexcept
|
||||
{
|
||||
return {data_, size_};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string as a C string. Never null.
|
||||
*/
|
||||
[[nodiscard]] constexpr char const*
|
||||
asCString() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
char const* data_;
|
||||
std::size_t size_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -518,7 +518,7 @@ public:
|
||||
* The input must be precisely `2 * bytes` hexadecimal characters
|
||||
* long, with one exception: the value '0'.
|
||||
*
|
||||
* @param sv A null-terminated string of hexadecimal characters
|
||||
* @param sv A string of hexadecimal characters
|
||||
* @return true if the input was parsed properly; false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/core/Job.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
@@ -9,7 +10,8 @@
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace beast {
|
||||
class Journal;
|
||||
@@ -67,7 +69,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcStart(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcStart(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log successful finish of RPC call
|
||||
@@ -76,7 +78,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcFinish(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcFinish(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log errored RPC call
|
||||
@@ -85,7 +87,7 @@ public:
|
||||
* @param requestId Unique identifier to track command
|
||||
*/
|
||||
virtual void
|
||||
rpcError(std::string const& method, std::uint64_t requestId) = 0;
|
||||
rpcError(std::string_view method, std::uint64_t requestId) = 0;
|
||||
|
||||
/**
|
||||
* Log queued job
|
||||
@@ -150,10 +152,20 @@ public:
|
||||
PerfLog::Setup
|
||||
setupPerfLog(Section const& section, std::filesystem::path const& configDir);
|
||||
|
||||
/**
|
||||
* @param methodNames The RPC methods to count, one counter per name. Reported
|
||||
* as JSON keys that borrow each name and read it as a C string, which is
|
||||
* why the parameter type requires one that reaches its terminating null.
|
||||
* The names must outlive the returned object, which holds views of them.
|
||||
* The range itself need not: it is copied.
|
||||
* Passed in rather than looked up here, so that this layer needs no
|
||||
* knowledge of the dispatch table.
|
||||
*/
|
||||
std::unique_ptr<PerfLog>
|
||||
makePerfLog(
|
||||
PerfLog::Setup const& setup,
|
||||
Application& app,
|
||||
std::span<NullTerminatedView const> methodNames,
|
||||
beast::Journal journal,
|
||||
std::function<void()>&& signalStop);
|
||||
|
||||
@@ -161,7 +173,7 @@ template <typename Func, class Rep, class Period>
|
||||
auto
|
||||
measureDurationAndLog(
|
||||
Func&& func,
|
||||
std::string const& actionDescription,
|
||||
std::string_view actionDescription,
|
||||
std::chrono::duration<Rep, Period> maxDelay,
|
||||
beast::Journal const& journal)
|
||||
{
|
||||
|
||||
45
include/xrpl/ledger/entries/AMMEntry.h
Normal file
45
include/xrpl/ledger/entries/AMMEntry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AMMEntry : public SLEBase<ViewT, ltAMM>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltAMM>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AMMEntry(
|
||||
Asset const& issue1,
|
||||
Asset const& issue2,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amm(issue1, issue2), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit AMMEntry(
|
||||
uint256 const& ammID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amm(ammID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AMMEntryR = AMMEntry<ReadView>;
|
||||
using AMMEntryW = AMMEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/AccountRootEntry.h
Normal file
35
include/xrpl/ledger/entries/AccountRootEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltACCOUNT_ROOT>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AccountRootEntry(
|
||||
AccountID const& id,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::account(id), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AccountRootEntryR = AccountRootEntry<ReadView>;
|
||||
using AccountRootEntryW = AccountRootEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/AmendmentsEntry.h
Normal file
33
include/xrpl/ledger/entries/AmendmentsEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class AmendmentsEntry : public SLEBase<ViewT, ltAMENDMENTS>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltAMENDMENTS>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit AmendmentsEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::amendments(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using AmendmentsEntryR = AmendmentsEntry<ReadView>;
|
||||
using AmendmentsEntryW = AmendmentsEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/BridgeEntry.h
Normal file
36
include/xrpl/ledger/entries/BridgeEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class BridgeEntry : public SLEBase<ViewT, ltBRIDGE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltBRIDGE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit BridgeEntry(
|
||||
STXChainBridge const& bridge,
|
||||
STXChainBridge::ChainType chainType,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::bridge(bridge, chainType), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using BridgeEntryR = BridgeEntry<ReadView>;
|
||||
using BridgeEntryW = BridgeEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/CheckEntry.h
Normal file
46
include/xrpl/ledger/entries/CheckEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class CheckEntry : public SLEBase<ViewT, ltCHECK>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltCHECK>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit CheckEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::check(id, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CheckEntry(
|
||||
uint256 const& checkID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::check(checkID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using CheckEntryR = CheckEntry<ReadView>;
|
||||
using CheckEntryW = CheckEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
47
include/xrpl/ledger/entries/CredentialEntry.h
Normal file
47
include/xrpl/ledger/entries/CredentialEntry.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class CredentialEntry : public SLEBase<ViewT, ltCREDENTIAL>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltCREDENTIAL>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit CredentialEntry(
|
||||
AccountID const& subject,
|
||||
AccountID const& issuer,
|
||||
Slice const& credType,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::credential(subject, issuer, credType), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CredentialEntry(
|
||||
uint256 const& credentialID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::credential(credentialID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using CredentialEntryR = CredentialEntry<ReadView>;
|
||||
using CredentialEntryW = CredentialEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/DIDEntry.h
Normal file
35
include/xrpl/ledger/entries/DIDEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DIDEntry : public SLEBase<ViewT, ltDID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DIDEntry(
|
||||
AccountID const& account,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::did(account), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DIDEntryR = DIDEntry<ReadView>;
|
||||
using DIDEntryW = DIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/DelegateEntry.h
Normal file
36
include/xrpl/ledger/entries/DelegateEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DelegateEntry : public SLEBase<ViewT, ltDELEGATE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDELEGATE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DelegateEntry(
|
||||
AccountID const& account,
|
||||
AccountID const& authorizedAccount,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::delegate(account, authorizedAccount), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DelegateEntryR = DelegateEntry<ReadView>;
|
||||
using DelegateEntryW = DelegateEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
58
include/xrpl/ledger/entries/DepositPreauthEntry.h
Normal file
58
include/xrpl/ledger/entries/DepositPreauthEntry.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DepositPreauthEntry : public SLEBase<ViewT, ltDEPOSIT_PREAUTH>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDEPOSIT_PREAUTH>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
AccountID const& owner,
|
||||
AccountID const& preauthorized,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(owner, preauthorized), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
AccountID const& owner,
|
||||
std::set<std::pair<AccountID, Slice>> const& authCreds,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(owner, authCreds), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DepositPreauthEntry(
|
||||
uint256 const& preauthID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::depositPreauth(preauthID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DepositPreauthEntryR = DepositPreauthEntry<ReadView>;
|
||||
using DepositPreauthEntryW = DepositPreauthEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
50
include/xrpl/ledger/entries/DirectoryNodeEntry.h
Normal file
50
include/xrpl/ledger/entries/DirectoryNodeEntry.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class DirectoryNodeEntry : public SLEBase<ViewT, ltDIR_NODE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltDIR_NODE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit DirectoryNodeEntry(
|
||||
AccountID const& id,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ownerDir(id), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a specific page of the directory rooted at @p root.
|
||||
*/
|
||||
explicit DirectoryNodeEntry(
|
||||
uint256 const& root,
|
||||
std::uint64_t index,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::page(root, index), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using DirectoryNodeEntryR = DirectoryNodeEntry<ReadView>;
|
||||
using DirectoryNodeEntryW = DirectoryNodeEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/ledger/entries/EscrowEntry.h
Normal file
37
include/xrpl/ledger/entries/EscrowEntry.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class EscrowEntry : public SLEBase<ViewT, ltESCROW>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltESCROW>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit EscrowEntry(
|
||||
AccountID const& src,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::escrow(src, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using EscrowEntryR = EscrowEntry<ReadView>;
|
||||
using EscrowEntryW = EscrowEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/FeeSettingsEntry.h
Normal file
33
include/xrpl/ledger/entries/FeeSettingsEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class FeeSettingsEntry : public SLEBase<ViewT, ltFEE_SETTINGS>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltFEE_SETTINGS>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit FeeSettingsEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::feeSettings(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using FeeSettingsEntryR = FeeSettingsEntry<ReadView>;
|
||||
using FeeSettingsEntryW = FeeSettingsEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/LedgerHashesEntry.h
Normal file
33
include/xrpl/ledger/entries/LedgerHashesEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LedgerHashesEntry : public SLEBase<ViewT, ltLEDGER_HASHES>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLEDGER_HASHES>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LedgerHashesEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::skip(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
||||
using LedgerHashesEntryW = LedgerHashesEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/LoanBrokerEntry.h
Normal file
46
include/xrpl/ledger/entries/LoanBrokerEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LoanBrokerEntry : public SLEBase<ViewT, ltLOAN_BROKER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLOAN_BROKER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LoanBrokerEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loanBroker(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit LoanBrokerEntry(
|
||||
uint256 const& loanBrokerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loanBroker(loanBrokerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LoanBrokerEntryR = LoanBrokerEntry<ReadView>;
|
||||
using LoanBrokerEntryW = LoanBrokerEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
45
include/xrpl/ledger/entries/LoanEntry.h
Normal file
45
include/xrpl/ledger/entries/LoanEntry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class LoanEntry : public SLEBase<ViewT, ltLOAN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltLOAN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit LoanEntry(
|
||||
uint256 const& loanBrokerID,
|
||||
SeqProxy const& loanSeq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loan(loanBrokerID, loanSeq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit LoanEntry(
|
||||
uint256 const& loanID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::loan(loanID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using LoanEntryR = LoanEntry<ReadView>;
|
||||
using LoanEntryW = LoanEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/ledger/entries/MPTokenEntry.h
Normal file
55
include/xrpl/ledger/entries/MPTokenEntry.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class MPTokenEntry : public SLEBase<ViewT, ltMPTOKEN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltMPTOKEN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit MPTokenEntry(
|
||||
MPTID const& issuanceID,
|
||||
AccountID const& holder,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(issuanceID, holder), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenEntry(
|
||||
uint256 const& issuanceKey,
|
||||
AccountID const& holder,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(issuanceKey, holder), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenEntry(
|
||||
uint256 const& mptokenKey,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptoken(mptokenKey), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using MPTokenEntryR = MPTokenEntry<ReadView>;
|
||||
using MPTokenEntryW = MPTokenEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
56
include/xrpl/ledger/entries/MPTokenIssuanceEntry.h
Normal file
56
include/xrpl/ledger/entries/MPTokenIssuanceEntry.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class MPTokenIssuanceEntry : public SLEBase<ViewT, ltMPTOKEN_ISSUANCE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltMPTOKEN_ISSUANCE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
std::uint32_t seq,
|
||||
AccountID const& issuer,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(makeMptID(seq, issuer)), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
MPTID const& issuanceID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(issuanceID), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MPTokenIssuanceEntry(
|
||||
uint256 const& issuanceKey,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::mptokenIssuance(issuanceKey), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using MPTokenIssuanceEntryR = MPTokenIssuanceEntry<ReadView>;
|
||||
using MPTokenIssuanceEntryW = MPTokenIssuanceEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/NFTokenOfferEntry.h
Normal file
46
include/xrpl/ledger/entries/NFTokenOfferEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NFTokenOfferEntry : public SLEBase<ViewT, ltNFTOKEN_OFFER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNFTOKEN_OFFER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NFTokenOfferEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenOffer(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit NFTokenOfferEntry(
|
||||
uint256 const& offerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenOffer(offerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NFTokenOfferEntryR = NFTokenOfferEntry<ReadView>;
|
||||
using NFTokenOfferEntryW = NFTokenOfferEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/ledger/entries/NFTokenPageEntry.h
Normal file
37
include/xrpl/ledger/entries/NFTokenPageEntry.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NFTokenPageEntry : public SLEBase<ViewT, ltNFTOKEN_PAGE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNFTOKEN_PAGE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NFTokenPageEntry(
|
||||
Keylet const& page,
|
||||
uint256 const& token,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::nftokenPage(page, token), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NFTokenPageEntryR = NFTokenPageEntry<ReadView>;
|
||||
using NFTokenPageEntryW = NFTokenPageEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/ledger/entries/NegativeUNLEntry.h
Normal file
33
include/xrpl/ledger/entries/NegativeUNLEntry.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class NegativeUNLEntry : public SLEBase<ViewT, ltNEGATIVE_UNL>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltNEGATIVE_UNL>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit NegativeUNLEntry(
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::negativeUNL(), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using NegativeUNLEntryR = NegativeUNLEntry<ReadView>;
|
||||
using NegativeUNLEntryW = NegativeUNLEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/OfferEntry.h
Normal file
46
include/xrpl/ledger/entries/OfferEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class OfferEntry : public SLEBase<ViewT, ltOFFER>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltOFFER>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit OfferEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::offer(id, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit OfferEntry(
|
||||
uint256 const& offerID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::offer(offerID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using OfferEntryR = OfferEntry<ReadView>;
|
||||
using OfferEntryW = OfferEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/OracleEntry.h
Normal file
38
include/xrpl/ledger/entries/OracleEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class OracleEntry : public SLEBase<ViewT, ltORACLE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltORACLE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit OracleEntry(
|
||||
AccountID const& account,
|
||||
std::uint32_t documentID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::oracle(account, documentID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using OracleEntryR = OracleEntry<ReadView>;
|
||||
using OracleEntryW = OracleEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/PayChannelEntry.h
Normal file
38
include/xrpl/ledger/entries/PayChannelEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class PayChannelEntry : public SLEBase<ViewT, ltPAYCHAN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltPAYCHAN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit PayChannelEntry(
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::payChannel(src, dst, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PayChannelEntryR = PayChannelEntry<ReadView>;
|
||||
using PayChannelEntryW = PayChannelEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/PermissionedDomainEntry.h
Normal file
46
include/xrpl/ledger/entries/PermissionedDomainEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class PermissionedDomainEntry : public SLEBase<ViewT, ltPERMISSIONED_DOMAIN>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltPERMISSIONED_DOMAIN>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit PermissionedDomainEntry(
|
||||
AccountID const& account,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::permissionedDomain(account, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit PermissionedDomainEntry(
|
||||
uint256 const& domainID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::permissionedDomain(domainID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PermissionedDomainEntryR = PermissionedDomainEntry<ReadView>;
|
||||
using PermissionedDomainEntryW = PermissionedDomainEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
48
include/xrpl/ledger/entries/RippleStateEntry.h
Normal file
48
include/xrpl/ledger/entries/RippleStateEntry.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class RippleStateEntry : public SLEBase<ViewT, ltRIPPLE_STATE>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltRIPPLE_STATE>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit RippleStateEntry(
|
||||
AccountID const& id0,
|
||||
AccountID const& id1,
|
||||
Currency const& currency,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::trustLine(id0, id1, currency), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit RippleStateEntry(
|
||||
AccountID const& id,
|
||||
Issue const& issue,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::trustLine(id, issue), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using RippleStateEntryR = RippleStateEntry<ReadView>;
|
||||
using RippleStateEntryW = RippleStateEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
503
include/xrpl/ledger/entries/SLEBase.h
Normal file
503
include/xrpl/ledger/entries/SLEBase.h
Normal file
@@ -0,0 +1,503 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Concept to distinguish read-only vs writable view types
|
||||
template <typename V>
|
||||
concept IsWritableView = std::derived_from<V, ApplyView>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Resolves a keylet for a read-only entry.
|
||||
*
|
||||
* ReadView::read() on an ApplyView returns the underlying ledger's entry
|
||||
* whenever the view is not already tracking one, while peek() installs the
|
||||
* view's own copy and returns that. A read-only entry built with read()
|
||||
* would therefore hold an SLE that goes stale the moment anything peeks the
|
||||
* same key and modifies it. Resolve through peek() whenever the view really is
|
||||
* an ApplyView, so every entry over that view shares one SLE.
|
||||
*
|
||||
* @note The const_cast is what makes reaching ApplyView::peek() possible, and
|
||||
* it is defined behavior only when the view really is a non-const
|
||||
* object that the caller merely observes through a const reference.
|
||||
* That holds for every production view today, but it is not a
|
||||
* guarantee the codebase makes: the unit tests already build
|
||||
* genuinely const ApplyView-derived objects (`Sandbox const` in
|
||||
* Directory_test.cpp and View_test.cpp, `PaymentSandbox const` in
|
||||
* TheoreticalQuality_test.cpp and View_test.cpp). Constructing a
|
||||
* read-only entry over one of those would be undefined behavior, so
|
||||
* do not, until #8069 removes the cast -- by giving ApplyView a
|
||||
* const-qualified peek(), which needs no amendment because
|
||||
* Action::Cache is invisible to apply(), visit() and metadata.
|
||||
*
|
||||
* @note Consequently a "read-only" entry over an ApplyView is not free of
|
||||
* side effects: peek() installs an Action::Cache entry in the apply
|
||||
* state table. That is benign for transaction metadata -- Cache entries
|
||||
* are skipped in ApplyStateTable::apply(), ::visit() and in metadata
|
||||
* generation -- but it does cost one deep SLE copy on first touch.
|
||||
*/
|
||||
inline SLE::const_pointer
|
||||
resolveEntry(ReadView const& view, Keylet const& key)
|
||||
{
|
||||
// Safe only for a view that is not itself a const object -- see the
|
||||
// note above. The entry holds a const reference because it does not
|
||||
// modify the view, not because the view is const.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
||||
if (auto const applyView = dynamic_cast<ApplyView*>(const_cast<ReadView*>(&view)))
|
||||
return applyView->peek(key);
|
||||
return view.read(key);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* View-parameterized base class for all ledger entries.
|
||||
*
|
||||
* SLEBase<ReadView> — read-only: holds shared_ptr<SLE const> + ReadView const&
|
||||
* SLEBase<ApplyView> — writable: holds shared_ptr<SLE> + ApplyView& + Keylet,
|
||||
* plus insert/update/erase operations
|
||||
*
|
||||
* Write-only members are gated by `requires` clauses, providing compile-time
|
||||
* guarantees that read-only entries cannot mutate state.
|
||||
*
|
||||
* @tparam EntryType the ledger entry type this entry is statically bound to.
|
||||
* Derived per-type entries pass their own type (e.g. ltACCOUNT_ROOT); the
|
||||
* generic ReadOnlySLE / WritableSLE aliases leave it at ltANY, which opts out
|
||||
* of the static type check. Binding the type here is what keeps an entry for
|
||||
* one entry type from being constructed or converted from another -- see the
|
||||
* converting constructor below.
|
||||
*
|
||||
* Derived classes should provide domain-specific accessors that hide
|
||||
* implementation details of the underlying ledger entry format.
|
||||
*/
|
||||
template <typename ViewT, LedgerEntryType EntryType = ltANY>
|
||||
class SLEBase
|
||||
{
|
||||
public:
|
||||
static constexpr bool kIsWritable = IsWritableView<ViewT>;
|
||||
|
||||
// The ledger entry type this entry is bound to, and whether that binding
|
||||
// is meaningful (ltANY means "any type", i.e. no static check).
|
||||
static constexpr LedgerEntryType kEntryType = EntryType;
|
||||
static constexpr bool kIsTyped = (EntryType != ltANY);
|
||||
|
||||
// SLE pointer type: mutable for writable views, const for read-only
|
||||
using SlePtrType = std::conditional_t<kIsWritable, SLE::pointer, SLE::const_pointer>;
|
||||
|
||||
// View reference type: ApplyView& for writable, ReadView const& for
|
||||
// read-only
|
||||
using ViewRefType = std::conditional_t<kIsWritable, ApplyView&, ReadView const&>;
|
||||
|
||||
// Non-virtual by design: these entries are parameterized on the view and
|
||||
// entry type, never used polymorphically through a base pointer. A vptr
|
||||
// would be 8 bytes of pure overhead on a type meant to be as cheap as the
|
||||
// shared_ptr it wraps. See the static_assert below the class.
|
||||
//
|
||||
// The destructor is public because the ReadOnlySLE / WritableSLE aliases
|
||||
// name this class directly and are used as value types. Since it is not
|
||||
// virtual, never delete a derived entry through an SLEBase*.
|
||||
~SLEBase() = default;
|
||||
|
||||
SLEBase(SLEBase const&)
|
||||
requires(!kIsWritable)
|
||||
= default;
|
||||
SLEBase(SLEBase&&) = default;
|
||||
SLEBase&
|
||||
operator=(SLEBase const&) = delete;
|
||||
SLEBase&
|
||||
operator=(SLEBase&&) = delete;
|
||||
SLEBase() = delete;
|
||||
|
||||
// --- Constructors that adopt/resolve an SLE (public so the ReadOnlySLE /
|
||||
// WritableSLE aliases and the per-type entries can be built directly
|
||||
// from a keylet, or -- read-only only -- from an already-fetched
|
||||
// SLE). ---
|
||||
|
||||
/**
|
||||
* Constructor for read-only context (adopt an already-fetched SLE).
|
||||
*
|
||||
* There is deliberately no writable equivalent: a writable entry needs
|
||||
* a Keylet so that newSLE() can still build an entry when none exists,
|
||||
* and that cannot be recovered from a null SLE.
|
||||
*/
|
||||
explicit SLEBase(
|
||||
SLE::const_pointer sle,
|
||||
ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires(!kIsWritable)
|
||||
: view_(view), sle_(std::move(sle)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || !sle_ || sle_->getType() == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : adopted SLE matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for read-only context (read from view by keylet)
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires(!kIsWritable)
|
||||
: view_(view), sle_(detail::resolveEntry(view, key)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || key.type == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : keylet matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converting constructor: writable → read-only.
|
||||
*
|
||||
* Enables implicit conversion from SLEBase<ApplyView> to
|
||||
* SLEBase<ReadView>, so functions taking ReadOnlySLE const& can accept
|
||||
* WritableSLE.
|
||||
*
|
||||
* Constrained to the same entry type (or to a ltANY target, i.e. widening
|
||||
* a typed entry to a generic ReadOnlySLE). The constraint is load-bearing:
|
||||
* this constructor is inherited into every per-type entry, and unconstrained
|
||||
* it would bind any writable entry that slices to SLEBase, so an OfferEntryW
|
||||
* would convert to an AccountRootEntryR with no cast at the call site.
|
||||
*/
|
||||
template <typename OtherViewT, LedgerEntryType OtherType>
|
||||
SLEBase(SLEBase<OtherViewT, OtherType> const& other)
|
||||
requires(!kIsWritable && IsWritableView<OtherViewT> &&
|
||||
(OtherType == EntryType || EntryType == ltANY))
|
||||
: view_(other.readView()), sle_(other.rawSle()), j_(other.journal())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context (peek from view by keylet)
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ApplyView& view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: view_(view), key_(key), sle_(view_.peek(key)), j_(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!kIsTyped || key.type == kEntryType,
|
||||
"xrpl::SLEBase::SLEBase : keylet matches bound entry type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context, for call sites that hold an
|
||||
* ApplyViewContext (peek from ctx.view by keylet).
|
||||
*
|
||||
* ctx.tx is not retained: this exists purely so transactors can pass the
|
||||
* context they already have instead of spelling out ctx.view. If an entry
|
||||
* ever needs the applying transaction, store it here rather than adding
|
||||
* another overload.
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ApplyViewContext const& ctx,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: SLEBase(key, ctx.view, j)
|
||||
{
|
||||
}
|
||||
|
||||
// --- Common interface (always available) ---
|
||||
|
||||
/**
|
||||
* Returns true if the ledger entry exists
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
exists() const
|
||||
{
|
||||
return sle_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit conversion to bool for convenient existence checking
|
||||
*/
|
||||
explicit
|
||||
operator bool() const
|
||||
{
|
||||
return exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying SLE for read access.
|
||||
*
|
||||
* Prefer operator-> / operator* for field access; this is for the call
|
||||
* sites that need the shared_ptr itself.
|
||||
*/
|
||||
[[nodiscard]] SLE::const_pointer
|
||||
rawSle() const
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ledger entry type of this entry.
|
||||
*
|
||||
* For a per-type entry this is kEntryType, known at compile time and
|
||||
* valid whether or not the entry exists. Only the generic ReadOnlySLE /
|
||||
* WritableSLE aliases have to read it back out of the SLE.
|
||||
*
|
||||
* @throws std::logic_error for a generic (ltANY) entry if exists() is
|
||||
* false.
|
||||
*/
|
||||
[[nodiscard]] LedgerEntryType
|
||||
type() const
|
||||
{
|
||||
if constexpr (kIsTyped)
|
||||
{
|
||||
return kEntryType;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::type : entry does not exist");
|
||||
return sle_->getType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keylet identifying this entry.
|
||||
*
|
||||
* Writable entries keep the keylet they were built from, so it is valid
|
||||
* even before newSLE(). Read-only entries derive it from the SLE, which
|
||||
* must therefore exist.
|
||||
*
|
||||
* @throws std::logic_error for a read-only entry if exists() is false.
|
||||
*/
|
||||
[[nodiscard]] Keylet
|
||||
keylet() const
|
||||
{
|
||||
if constexpr (kIsWritable)
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::keylet : entry does not exist");
|
||||
// Take the type from the SLE, not from kEntryType: the adopt-SLE
|
||||
// constructor's type check is assert-only, so a Release build can
|
||||
// be holding an SLE whose type disagrees with the binding, and the
|
||||
// SLE is the one telling the truth.
|
||||
return Keylet(sle_->getType(), sle_->key());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ledger key of this entry.
|
||||
*
|
||||
* @throws std::logic_error same as keylet(): for read-only entries,
|
||||
* if exists() is false.
|
||||
*/
|
||||
[[nodiscard]] uint256
|
||||
key() const
|
||||
{
|
||||
return keylet().key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the read view (always available; ApplyView inherits ReadView)
|
||||
*/
|
||||
[[nodiscard]] ReadView const&
|
||||
readView() const
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Const dereference operators (always available)
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
STLedgerEntry const*
|
||||
operator->() const
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry const&
|
||||
operator*() const
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator* : entry does not exist");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
// --- Writable interface (compile-time gated) ---
|
||||
//
|
||||
// Everything that hands out mutable access (or mutates) is non-const, so
|
||||
// that a `FooEntryW const&` is as inert as a `FooEntryR`. Use readView()
|
||||
// when a const entry only needs to inspect the view.
|
||||
|
||||
/**
|
||||
* Returns the underlying SLE for write access.
|
||||
*
|
||||
* Prefer operator-> / operator* for field access; this is for the call
|
||||
* sites that need the shared_ptr itself.
|
||||
*/
|
||||
[[nodiscard]] SlePtrType const&
|
||||
mutableRawSle()
|
||||
requires kIsWritable
|
||||
{
|
||||
return sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the apply view for write operations
|
||||
*/
|
||||
[[nodiscard]] ApplyView&
|
||||
applyView()
|
||||
requires kIsWritable
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable dereference operators
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
STLedgerEntry*
|
||||
operator->()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
|
||||
return sle_.get();
|
||||
}
|
||||
|
||||
STLedgerEntry&
|
||||
operator*()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::operator* : entry does not exist");
|
||||
return *sle_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the entry into the view.
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
insert()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::insert : entry does not exist");
|
||||
view_.insert(sle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the entry from the view.
|
||||
*
|
||||
* Drops the SLE afterwards, so the entry reports !exists() and any
|
||||
* further use throws here rather than either throwing from deep inside
|
||||
* ApplyStateTable or -- worse -- silently succeeding. For an
|
||||
* entry that already existed, ApplyStateTable::erase keeps holding this
|
||||
* exact SLE and builds the DeletedNode's FinalFields from it, so a write
|
||||
* through the entry after erase() would land in transaction metadata
|
||||
* with no diagnostic at all.
|
||||
*
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
erase()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::erase : entry does not exist");
|
||||
view_.erase(sle_);
|
||||
sle_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws std::logic_error if exists() is false.
|
||||
*/
|
||||
void
|
||||
update()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (!exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::update : entry does not exist");
|
||||
view_.update(sle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws std::logic_error if exists() is true: newSLE() would otherwise
|
||||
* silently discard the SLE already held.
|
||||
*/
|
||||
void
|
||||
newSLE()
|
||||
requires kIsWritable
|
||||
{
|
||||
if (exists())
|
||||
Throw<std::logic_error>("xrpl::SLEBase::newSLE : entry already exists");
|
||||
sle_ = std::make_shared<SLE>(key_);
|
||||
}
|
||||
|
||||
[[nodiscard]] beast::Journal
|
||||
journal() const
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
protected:
|
||||
ViewRefType view_;
|
||||
|
||||
// Keylet is only meaningful for writable views, which need it to build an
|
||||
// SLE that does not exist yet; read-only entries derive it from the SLE.
|
||||
struct Empty
|
||||
{
|
||||
};
|
||||
|
||||
// No default member initializer: Keylet is not default-constructible, so
|
||||
// every writable constructor must initialize key_ explicitly.
|
||||
[[no_unique_address]]
|
||||
std::conditional_t<kIsWritable, Keylet, Empty> key_;
|
||||
|
||||
SlePtrType sle_{};
|
||||
beast::Journal j_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic (any-entry-type) SLE entries.
|
||||
*
|
||||
* Use these when the concrete ledger entry type is not known at a given site;
|
||||
* otherwise prefer the per-type entries (e.g. AccountRootEntry.h), which
|
||||
* additionally enforce the entry type at compile time.
|
||||
*
|
||||
* SLE::const_pointer / SLE::const_ref -> ReadOnlySLE
|
||||
* SLE::pointer / SLE::ref -> WritableSLE
|
||||
*/
|
||||
using ReadOnlySLE = SLEBase<ReadView>;
|
||||
using WritableSLE = SLEBase<ApplyView>;
|
||||
|
||||
static_assert(
|
||||
!std::is_polymorphic_v<ReadOnlySLE> && !std::is_polymorphic_v<WritableSLE>,
|
||||
"SLEBase must stay a thin value type; it must not acquire a vtable");
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/ledger/entries/SignerListEntry.h
Normal file
35
include/xrpl/ledger/entries/SignerListEntry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class SignerListEntry : public SLEBase<ViewT, ltSIGNER_LIST>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltSIGNER_LIST>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit SignerListEntry(
|
||||
AccountID const& account,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::signerList(account), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using SignerListEntryR = SignerListEntry<ReadView>;
|
||||
using SignerListEntryW = SignerListEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
36
include/xrpl/ledger/entries/SponsorshipEntry.h
Normal file
36
include/xrpl/ledger/entries/SponsorshipEntry.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class SponsorshipEntry : public SLEBase<ViewT, ltSPONSORSHIP>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltSPONSORSHIP>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit SponsorshipEntry(
|
||||
AccountID const& sponsor,
|
||||
AccountID const& sponsee,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::sponsorship(sponsor, sponsee), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using SponsorshipEntryR = SponsorshipEntry<ReadView>;
|
||||
using SponsorshipEntryW = SponsorshipEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/TicketEntry.h
Normal file
46
include/xrpl/ledger/entries/TicketEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class TicketEntry : public SLEBase<ViewT, ltTICKET>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltTICKET>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit TicketEntry(
|
||||
AccountID const& id,
|
||||
SeqProxy const& ticketSeq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ticket(id, ticketSeq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TicketEntry(
|
||||
uint256 const& ticketID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::ticket(ticketID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using TicketEntryR = TicketEntry<ReadView>;
|
||||
using TicketEntryW = TicketEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/TransactionProposalEntry.h
Normal file
38
include/xrpl/ledger/entries/TransactionProposalEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class TransactionProposalEntry : public SLEBase<ViewT, ltTRANSACTION_PROPOSAL>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltTRANSACTION_PROPOSAL>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit TransactionProposalEntry(
|
||||
AccountID const& target,
|
||||
std::uint32_t ticketSequence,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::txProposal(target, ticketSequence), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using TransactionProposalEntryR = TransactionProposalEntry<ReadView>;
|
||||
using TransactionProposalEntryW = TransactionProposalEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
46
include/xrpl/ledger/entries/VaultEntry.h
Normal file
46
include/xrpl/ledger/entries/VaultEntry.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class VaultEntry : public SLEBase<ViewT, ltVAULT>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltVAULT>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit VaultEntry(
|
||||
AccountID const& owner,
|
||||
SeqProxy const& seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::vault(owner, seq), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
explicit VaultEntry(
|
||||
uint256 const& vaultID,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::vault(vaultID), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using VaultEntryR = VaultEntry<ReadView>;
|
||||
using VaultEntryW = VaultEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
38
include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h
Normal file
38
include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class XChainOwnedClaimIDEntry : public SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit XChainOwnedClaimIDEntry(
|
||||
STXChainBridge const& bridge,
|
||||
std::uint64_t seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::xChainClaimID(bridge, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using XChainOwnedClaimIDEntryR = XChainOwnedClaimIDEntry<ReadView>;
|
||||
using XChainOwnedClaimIDEntryW = XChainOwnedClaimIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
class XChainOwnedCreateAccountClaimIDEntry
|
||||
: public SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>
|
||||
{
|
||||
public:
|
||||
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>;
|
||||
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using Base::Base;
|
||||
|
||||
explicit XChainOwnedCreateAccountClaimIDEntry(
|
||||
STXChainBridge const& bridge,
|
||||
std::uint64_t seq,
|
||||
Base::ViewRefType view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: Base(keylet::xChainCreateAccountClaimID(bridge, seq), view, j)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using XChainOwnedCreateAccountClaimIDEntryR = XChainOwnedCreateAccountClaimIDEntry<ReadView>;
|
||||
using XChainOwnedCreateAccountClaimIDEntryW = XChainOwnedCreateAccountClaimIDEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -338,9 +338,9 @@ struct AccountingDeltas
|
||||
Number debtTotalDelta;
|
||||
};
|
||||
|
||||
// Whole-life (pre-LendingProtocolV1_1) recognition model: interest is
|
||||
// recognized into AssetsTotal/DebtTotal up front, at origination.
|
||||
namespace accrual {
|
||||
// Instant interest recognition (pre-LendingProtocolV1_1): interest is
|
||||
// recognized into AssetsTotal/DebtTotal immediately, at origination.
|
||||
namespace instant_recognition {
|
||||
|
||||
// LoanSet origination: what's added to Vault.AssetsTotal and LoanBroker.DebtTotal
|
||||
AccountingDeltas
|
||||
@@ -362,7 +362,7 @@ loanVaultExposure(SLE::const_ref loanSle);
|
||||
AccountingDeltas
|
||||
loanPaymentDeltas(LoanPaymentParts const& parts);
|
||||
|
||||
} // namespace accrual
|
||||
} // namespace instant_recognition
|
||||
|
||||
// Cash-basis (LendingProtocolV1_1) recognition model: AssetsTotal/DebtTotal
|
||||
// are principal-only, interest is recognized only as it's actually paid.
|
||||
@@ -381,7 +381,7 @@ loanPaymentDeltas(LoanPaymentParts const& parts);
|
||||
|
||||
// Public dispatchers: pick cash_basis:: if featureLendingProtocolV1_1 is
|
||||
// enabled AND the Vault's LEVersion (VaultHelpers::getVaultVersion) is
|
||||
// VaultVersion::CashBasis, else accrual::. These are the only entry points
|
||||
// VaultVersion::CashBasis, else instant_recognition::. These are the only entry points
|
||||
// transactors call.
|
||||
AccountingDeltas
|
||||
loanOriginationDeltas(
|
||||
|
||||
@@ -172,8 +172,8 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref
|
||||
|
||||
/**
|
||||
* Resolves a Vault's LEVersion, the single point every accounting touch
|
||||
* point should call to determine which recognition model (accrual vs.
|
||||
* cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
|
||||
* point should call to determine which recognition model (instant interest
|
||||
* recognition vs. cash-basis) a Vault uses. Vaults created before featureLendingProtocolV1_1
|
||||
* activated never have sfLEVersion set, which resolves here to
|
||||
* VaultVersion::Legacy.
|
||||
*
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
void
|
||||
importDatabase(Database& source) override
|
||||
{
|
||||
importInternal(*backend_.get(), source);
|
||||
importInternal(*backend_, source);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -26,7 +26,8 @@ struct Config
|
||||
/**
|
||||
* The largest number of public peer slots to allow.
|
||||
* This includes both inbound and outbound, but does not include
|
||||
* fixed peers.
|
||||
* fixed peers. A configuration built by `makeConfig` always holds
|
||||
* `maxPeers == inPeers + outPeers`.
|
||||
*/
|
||||
std::size_t maxPeers{tuning::kDefaultMaxPeers};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STInteger.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
@@ -301,6 +302,87 @@ verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 co
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a holder's issuer mirror is encrypted under the
|
||||
* issuance's currently registered issuer key.
|
||||
*
|
||||
* Verifies that the holder's issuer mirror epoch matches the active issuer key
|
||||
* epoch on the issuance. An absent mirror epoch defaults to epoch 0. A holder without an issuer
|
||||
* mirror is considered stale, as there is no key anchor for future re-encryptions.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger object.
|
||||
* @return true if the MPToken's issuer mirror is current. false if stale.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isIssuerMirrorCurrent(SLE const& issuance, SLE const& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a holder's auditor mirror is encrypted under the
|
||||
* issuance's currently registered auditor key.
|
||||
*
|
||||
* Verifies that the holder's auditor mirror epoch matches the active auditor key
|
||||
* epoch on the issuance. An absent mirror epoch defaults to epoch 0. An issuance
|
||||
* without an auditor key requires no auditor mirror and is considered current.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger object.
|
||||
* @return true if the auditor mirror is current or not required.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isAuditorMirrorCurrent(SLE const& issuance, SLE const& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Checks whether each mirror a holder is required to have is encrypted
|
||||
* under the issuance's currently registered ElGamal keys.
|
||||
*
|
||||
* Verifies that both the issuer mirror and the auditor mirror (if required)
|
||||
* are current. This serves as a combined check, ensuring all necessary
|
||||
* holder mirror epochs match the active key epochs on the issuance.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger object.
|
||||
* @return true if the required mirrors are current.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
areMirrorsCurrent(SLE const& issuance, SLE const& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Set the holder's issuer mirror epoch to match the issuance's current issuer key epoch.
|
||||
*
|
||||
* Call this after writing the issuer mirror ciphertext under the issuance's
|
||||
* currently registered issuer key, so that the mirror reads as current afterwards.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger entry to update.
|
||||
*/
|
||||
void
|
||||
setIssuerMirrorEpoch(SLE const& issuance, SLE& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Set the holder's auditor mirror epoch to match the issuance's current auditor key epoch.
|
||||
*
|
||||
* Call this after writing the auditor mirror ciphertext under the issuance's
|
||||
* currently registered auditor key. Does nothing when the holder has no auditor mirror.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger entry to update.
|
||||
*/
|
||||
void
|
||||
setAuditorMirrorEpoch(SLE const& issuance, SLE& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Set the holder's MPToken mirror epochs to match the issuance's current key epochs.
|
||||
*
|
||||
* Call this after writing mirror ciphertexts under the issuance's currently
|
||||
* registered keys, so that the mirrors read as current afterwards.
|
||||
*
|
||||
* @param issuance The MPTokenIssuance ledger object.
|
||||
* @param mptoken The holder's MPToken ledger entry to update.
|
||||
*/
|
||||
void
|
||||
setMirrorEpochs(SLE const& issuance, SLE& mptoken);
|
||||
|
||||
/**
|
||||
* @brief Verifies revealed amount encryptions for all recipients.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -322,7 +322,7 @@ constexpr std::uint8_t kVaultMaximumIouScale = 18;
|
||||
* Vault ledger-entry schema versions. Assigned to newly created
|
||||
* Vaults once featureLendingProtocolV1_1 is enabled. Vaults created before
|
||||
* activation are left without LEVersion (implicit legacy version 0,
|
||||
* accrual-basis accounting).
|
||||
* instant interest recognition).
|
||||
*/
|
||||
enum class VaultVersion : uint8_t {
|
||||
Legacy = 0,
|
||||
@@ -540,6 +540,11 @@ constexpr std::size_t kEcConvertBackProofLength =
|
||||
*/
|
||||
constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_SIZE;
|
||||
|
||||
/**
|
||||
* Length of compact equality proof.
|
||||
*/
|
||||
constexpr std::size_t kEcEqualityProofLength = 128;
|
||||
|
||||
/**
|
||||
* Extra base fee multiplier charged to confidential MPT transactions.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -129,8 +129,11 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_MPT,
|
||||
temBAD_CIPHERTEXT,
|
||||
temINVALID_BYTECODE,
|
||||
temTEMP_DISABLED,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -179,6 +182,8 @@ enum TEFcodes : TERUnderlyingType {
|
||||
tefINVALID_LEDGER_FIX_TYPE,
|
||||
tefNO_DST_PARTIAL,
|
||||
tefBAD_PATH_COUNT,
|
||||
tefNO_BYTECODE,
|
||||
tefBYTECODE_NOT_INCLUDED,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -370,6 +375,8 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecNO_DELEGATE_PERMISSION = 198,
|
||||
tecBAD_PROOF = 199,
|
||||
tecNO_SPONSOR_PERMISSION = 200,
|
||||
tecOUT_OF_GAS = 201,
|
||||
tecBYTECODE_REJECTED = 202,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(Cosign, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(ConfidentialMPTKeyRotation, Supported::No, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -309,6 +309,11 @@ 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},
|
||||
}))
|
||||
@@ -339,6 +344,8 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({
|
||||
{sfCondition, SoeOptional},
|
||||
{sfCancelAfter, SoeOptional},
|
||||
{sfFinishAfter, SoeOptional},
|
||||
{sfBytecode, SoeOptional},
|
||||
{sfData, SoeOptional},
|
||||
{sfSourceTag, SoeOptional},
|
||||
{sfDestinationTag, SoeOptional},
|
||||
{sfOwnerNode, SoeRequired},
|
||||
@@ -429,6 +436,8 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
|
||||
{sfConfidentialBalanceVersion, SoeDefault},
|
||||
{sfIssuerEncryptedBalance, SoeOptional},
|
||||
{sfAuditorEncryptedBalance, SoeOptional},
|
||||
{sfIssuerKeyMirrorEpoch, SoeOptional},
|
||||
{sfAuditorKeyMirrorEpoch, SoeOptional},
|
||||
{sfHolderEncryptionKey, SoeOptional},
|
||||
}))
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ 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)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -239,6 +244,7 @@ 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)
|
||||
@@ -323,6 +329,7 @@ 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)
|
||||
|
||||
// account (common)
|
||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||
|
||||
@@ -66,11 +66,13 @@ TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, ({.delegable = Delegation::Delegable}), ({
|
||||
{sfDestination, SoeRequired},
|
||||
{sfDestinationTag, SoeOptional},
|
||||
{sfAmount, SoeRequired, SoeMptSupported},
|
||||
{sfCondition, SoeOptional},
|
||||
{sfCancelAfter, SoeOptional},
|
||||
{sfFinishAfter, SoeOptional},
|
||||
{sfDestinationTag, SoeOptional},
|
||||
{sfBytecode, SoeOptional},
|
||||
{sfData, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction type completes an existing escrow. */
|
||||
@@ -83,6 +85,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, ({.delegable = Delegation::Delegab
|
||||
{sfFulfillment, SoeOptional},
|
||||
{sfCondition, SoeOptional},
|
||||
{sfCredentialIDs, SoeOptional},
|
||||
{sfGas, SoeOptional},
|
||||
}))
|
||||
|
||||
|
||||
@@ -1131,12 +1134,25 @@ TRANSACTION(ttSPONSORSHIP_SET, 91, SponsorshipSet,
|
||||
{sfRemainingOwnerCountDelta, SoeOptional},
|
||||
}))
|
||||
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/token/ConfidentialMPTMirrorUpdate.h>
|
||||
#endif
|
||||
TRANSACTION(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, 92, ConfidentialMPTMirrorUpdate,
|
||||
({.delegable = Delegation::Delegable, .amendment = featureConfidentialMPTKeyRotation}),
|
||||
({
|
||||
{sfMPTokenIssuanceID, SoeRequired},
|
||||
{sfHolder, SoeOptional},
|
||||
{sfIssuerEncryptedAmount, SoeOptional},
|
||||
{sfAuditorEncryptedAmount, SoeOptional},
|
||||
{sfZKProof, SoeRequired},
|
||||
}))
|
||||
|
||||
/** This transaction posts an unsigned transaction on-ledger as a
|
||||
TransactionProposal, pending multi-signature collection. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpl/tx/transactors/proposal/TransactionProposalCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttTRANSACTION_PROPOSAL_CREATE, 92, TransactionProposalCreate,
|
||||
TRANSACTION(ttTRANSACTION_PROPOSAL_CREATE, 93, TransactionProposalCreate,
|
||||
({.amendment = featureCosign}),
|
||||
({
|
||||
{sfProposedTransaction, SoeRequired},
|
||||
@@ -1173,6 +1189,10 @@ TRANSACTION(ttFEE, 101, SetFee,
|
||||
{sfBaseFeeDrops, SoeOptional},
|
||||
{sfReserveBaseDrops, SoeOptional},
|
||||
{sfReserveIncrementDrops, SoeOptional},
|
||||
// Smart Escrow fields
|
||||
{sfGasLimit, SoeOptional},
|
||||
{sfBytecodeSizeLimit, SoeOptional},
|
||||
{sfGasPrice, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This system-generated transaction type is used to update the network's negative UNL
|
||||
|
||||
@@ -278,6 +278,7 @@ JSS(frozen_balances); // out: GatewayBalances
|
||||
JSS(full); // in: LedgerClearer, handlers/Ledger
|
||||
JSS(full_reply); // out: PathFind
|
||||
JSS(fullbelow_size); // out: GetCounts
|
||||
JSS(gateway); // in: noripple_check
|
||||
JSS(git); // out: server_info
|
||||
JSS(good); // out: RPCVersion
|
||||
JSS(hash); // out: NetworkOPs, InboundLedger, LedgerToJson, STTx; field
|
||||
@@ -481,6 +482,7 @@ JSS(ports); // out: NetworkOPs
|
||||
JSS(previous); // out: Reservations
|
||||
JSS(previous_ledger); // out: LedgerPropose
|
||||
JSS(price); // out: amm_info, AuctionSlot
|
||||
JSS(problems); // out: noripple_check
|
||||
JSS(proof); // in: BookOffers
|
||||
JSS(propose_seq); // out: LedgerPropose
|
||||
JSS(proposers); // out: NetworkOPs, LedgerConsensus
|
||||
@@ -660,6 +662,7 @@ JSS(url); // in/out: Subscribe, Unsubscribe
|
||||
JSS(url_password); // in: Subscribe
|
||||
JSS(url_username); // in: Subscribe
|
||||
JSS(urlgravatar); //
|
||||
JSS(user); // in: noripple_check
|
||||
JSS(username); // in: Subscribe
|
||||
JSS(validated); // out: NetworkOPs, RPCHelpers, AccountTx*, Tx
|
||||
JSS(validator_list_expires); // out: NetworkOps, ValidatorList
|
||||
|
||||
@@ -174,6 +174,54 @@ public:
|
||||
return this->sle_->isFieldPresent(sfFinishAfter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBytecode (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getBytecode() const
|
||||
{
|
||||
if (hasBytecode())
|
||||
return this->sle_->at(sfBytecode);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfBytecode is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasBytecode() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfBytecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfData (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getData() const
|
||||
{
|
||||
if (hasData())
|
||||
return this->sle_->at(sfData);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfData is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasData() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfSourceTag (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -453,6 +501,28 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBytecode (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowBuilder&
|
||||
setBytecode(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBytecode] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfData (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowBuilder&
|
||||
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfData] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfSourceTag (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -213,6 +213,78 @@ public:
|
||||
return this->sle_->isFieldPresent(sfReserveIncrementDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasLimit (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasLimit() const
|
||||
{
|
||||
if (hasGasLimit())
|
||||
return this->sle_->at(sfGasLimit);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasLimit() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfGasLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBytecodeSizeLimit (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getBytecodeSizeLimit() const
|
||||
{
|
||||
if (hasBytecodeSizeLimit())
|
||||
return this->sle_->at(sfBytecodeSizeLimit);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfBytecodeSizeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasBytecodeSizeLimit() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfBytecodeSizeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasPrice (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasPrice() const
|
||||
{
|
||||
if (hasGasPrice())
|
||||
return this->sle_->at(sfGasPrice);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasPrice is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasPrice() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfGasPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfPreviousTxnID (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -375,6 +447,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasLimit (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setGasLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBytecodeSizeLimit (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setBytecodeSizeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBytecodeSizeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasPrice (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
FeeSettingsBuilder&
|
||||
setGasPrice(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasPrice] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfPreviousTxnID (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -268,6 +268,54 @@ public:
|
||||
return this->sle_->isFieldPresent(sfAuditorEncryptedBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerKeyMirrorEpoch (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getIssuerKeyMirrorEpoch() const
|
||||
{
|
||||
if (hasIssuerKeyMirrorEpoch())
|
||||
return this->sle_->at(sfIssuerKeyMirrorEpoch);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerKeyMirrorEpoch is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerKeyMirrorEpoch() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfIssuerKeyMirrorEpoch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorKeyMirrorEpoch (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getAuditorKeyMirrorEpoch() const
|
||||
{
|
||||
if (hasAuditorKeyMirrorEpoch())
|
||||
return this->sle_->at(sfAuditorKeyMirrorEpoch);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorKeyMirrorEpoch is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorKeyMirrorEpoch() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfAuditorKeyMirrorEpoch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolderEncryptionKey (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
@@ -471,6 +519,28 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerKeyMirrorEpoch (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setIssuerKeyMirrorEpoch(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerKeyMirrorEpoch] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorKeyMirrorEpoch (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
MPTokenBuilder&
|
||||
setAuditorKeyMirrorEpoch(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorKeyMirrorEpoch] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolderEncryptionKey (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
// This file is auto-generated. Do not edit.
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBase.h>
|
||||
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::transactions {
|
||||
|
||||
class ConfidentialMPTMirrorUpdateBuilder;
|
||||
|
||||
/**
|
||||
* @brief Transaction: ConfidentialMPTMirrorUpdate
|
||||
*
|
||||
* Type: ttCONFIDENTIAL_MPT_MIRROR_UPDATE (92)
|
||||
* Delegable: Delegation::Delegable
|
||||
* Amendment: featureConfidentialMPTKeyRotation
|
||||
* Privileges: Privilege::NoPriv
|
||||
*
|
||||
* Immutable wrapper around STTx providing type-safe field access.
|
||||
* Use ConfidentialMPTMirrorUpdateBuilder to construct new transactions.
|
||||
*/
|
||||
class ConfidentialMPTMirrorUpdate : public TransactionBase
|
||||
{
|
||||
public:
|
||||
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MIRROR_UPDATE;
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTMirrorUpdate transaction wrapper from an existing STTx object.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
explicit ConfidentialMPTMirrorUpdate(std::shared_ptr<STTx const> tx)
|
||||
: TransactionBase(std::move(tx))
|
||||
{
|
||||
// Verify transaction type
|
||||
if (tx_->getTxnType() != txType)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMirrorUpdate");
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction-specific field getters
|
||||
|
||||
/**
|
||||
* @brief Get sfMPTokenIssuanceID (SoeRequired)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_UINT192::type::value_type
|
||||
getMPTokenIssuanceID() const
|
||||
{
|
||||
return this->tx_->at(sfMPTokenIssuanceID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfHolder (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_ACCOUNT::type::value_type>
|
||||
getHolder() const
|
||||
{
|
||||
if (hasHolder())
|
||||
{
|
||||
return this->tx_->at(sfHolder);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfHolder is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasHolder() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerEncryptedAmount (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getIssuerEncryptedAmount() const
|
||||
{
|
||||
if (hasIssuerEncryptedAmount())
|
||||
{
|
||||
return this->tx_->at(sfIssuerEncryptedAmount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerEncryptedAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfIssuerEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAuditorEncryptedAmount (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getAuditorEncryptedAmount() const
|
||||
{
|
||||
if (hasAuditorEncryptedAmount())
|
||||
{
|
||||
return this->tx_->at(sfAuditorEncryptedAmount);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfAuditorEncryptedAmount is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasAuditorEncryptedAmount() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfZKProof (SoeRequired)
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SF_VL::type::value_type
|
||||
getZKProof() const
|
||||
{
|
||||
return this->tx_->at(sfZKProof);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Builder for ConfidentialMPTMirrorUpdate transactions.
|
||||
*
|
||||
* Provides a fluent interface for constructing transactions with method chaining.
|
||||
* Uses STObject internally for flexible transaction construction.
|
||||
* Inherits common field setters from TransactionBuilderBase.
|
||||
*/
|
||||
class ConfidentialMPTMirrorUpdateBuilder : public TransactionBuilderBase<ConfidentialMPTMirrorUpdateBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ConfidentialMPTMirrorUpdateBuilder with required fields.
|
||||
* @param account The account initiating the transaction.
|
||||
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
|
||||
* @param zKProof The sfZKProof field value.
|
||||
* @param sequence Optional sequence number for the transaction.
|
||||
* @param fee Optional fee for the transaction.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder(SF_ACCOUNT::type::value_type account,
|
||||
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::decay_t<typename SF_VL::type::value_type> const& zKProof, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
|
||||
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
|
||||
)
|
||||
: TransactionBuilderBase<ConfidentialMPTMirrorUpdateBuilder>(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, account, sequence, fee)
|
||||
{
|
||||
setMPTokenIssuanceID(mPTokenIssuanceID);
|
||||
setZKProof(zKProof);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a ConfidentialMPTMirrorUpdateBuilder from an existing STTx object.
|
||||
* @param tx The existing transaction to copy from.
|
||||
* @throws std::runtime_error if the transaction type doesn't match.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder(std::shared_ptr<STTx const> tx)
|
||||
{
|
||||
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MIRROR_UPDATE)
|
||||
{
|
||||
throw std::runtime_error("Invalid transaction type for ConfidentialMPTMirrorUpdateBuilder");
|
||||
}
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transaction-specific field setters
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Set sfMPTokenIssuanceID (SoeRequired)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder&
|
||||
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
|
||||
{
|
||||
object_[sfMPTokenIssuanceID] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfHolder (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder&
|
||||
setHolder(std::decay_t<typename SF_ACCOUNT::type::value_type> const& value)
|
||||
{
|
||||
object_[sfHolder] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerEncryptedAmount (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder&
|
||||
setIssuerEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAuditorEncryptedAmount (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder&
|
||||
setAuditorEncryptedAmount(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfAuditorEncryptedAmount] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfZKProof (SoeRequired)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdateBuilder&
|
||||
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfZKProof] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the ConfidentialMPTMirrorUpdate wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
* @param secretKey The secret key for signing.
|
||||
* @return The constructed transaction wrapper.
|
||||
*/
|
||||
ConfidentialMPTMirrorUpdate
|
||||
build(PublicKey const& publicKey, SecretKey const& secretKey)
|
||||
{
|
||||
sign(publicKey, secretKey);
|
||||
return ConfidentialMPTMirrorUpdate{std::make_shared<STTx>(std::move(object_))};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::transactions
|
||||
@@ -58,6 +58,32 @@ public:
|
||||
return this->tx_->at(sfDestination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestinationTag (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getDestinationTag() const
|
||||
{
|
||||
if (hasDestinationTag())
|
||||
{
|
||||
return this->tx_->at(sfDestinationTag);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDestinationTag is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDestinationTag() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfDestinationTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
@@ -149,29 +175,55 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfDestinationTag (SoeOptional)
|
||||
* @brief Get sfBytecode (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getDestinationTag() const
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getBytecode() const
|
||||
{
|
||||
if (hasDestinationTag())
|
||||
if (hasBytecode())
|
||||
{
|
||||
return this->tx_->at(sfDestinationTag);
|
||||
return this->tx_->at(sfBytecode);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfDestinationTag is present.
|
||||
* @brief Check if sfBytecode is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasDestinationTag() const
|
||||
hasBytecode() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfDestinationTag);
|
||||
return this->tx_->isFieldPresent(sfBytecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfData (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_VL::type::value_type>
|
||||
getData() const
|
||||
{
|
||||
if (hasData())
|
||||
{
|
||||
return this->tx_->at(sfData);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfData is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasData() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfData);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -232,6 +284,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestinationTag (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestinationTag] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
@@ -278,13 +341,24 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfDestinationTag (SoeOptional)
|
||||
* @brief Set sfBytecode (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setDestinationTag(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
setBytecode(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfDestinationTag] = value;
|
||||
object_[sfBytecode] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfData (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowCreateBuilder&
|
||||
setData(std::decay_t<typename SF_VL::type::value_type> const& value)
|
||||
{
|
||||
object_[sfData] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,32 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfCredentialIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGas (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGas() const
|
||||
{
|
||||
if (hasGas())
|
||||
{
|
||||
return this->tx_->at(sfGas);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGas is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGas() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfGas);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -249,6 +275,17 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGas (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
EscrowFinishBuilder&
|
||||
setGas(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGas] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the EscrowFinish wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -254,6 +254,84 @@ public:
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfReserveIncrementDrops);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasLimit (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasLimit() const
|
||||
{
|
||||
if (hasGasLimit())
|
||||
{
|
||||
return this->tx_->at(sfGasLimit);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasLimit() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfGasLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfBytecodeSizeLimit (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getBytecodeSizeLimit() const
|
||||
{
|
||||
if (hasBytecodeSizeLimit())
|
||||
{
|
||||
return this->tx_->at(sfBytecodeSizeLimit);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfBytecodeSizeLimit is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasBytecodeSizeLimit() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfBytecodeSizeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfGasPrice (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getGasPrice() const
|
||||
{
|
||||
if (hasGasPrice())
|
||||
{
|
||||
return this->tx_->at(sfGasPrice);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfGasPrice is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasGasPrice() const
|
||||
{
|
||||
return this->tx_->isFieldPresent(sfGasPrice);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -386,6 +464,39 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasLimit (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setGasLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfBytecodeSizeLimit (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setBytecodeSizeLimit(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfBytecodeSizeLimit] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfGasPrice (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
SetFeeBuilder&
|
||||
setGasPrice(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfGasPrice] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the SetFee wrapper.
|
||||
* @param publicKey The public key for signing.
|
||||
|
||||
@@ -18,7 +18,7 @@ class TransactionProposalCreateBuilder;
|
||||
/**
|
||||
* @brief Transaction: TransactionProposalCreate
|
||||
*
|
||||
* Type: ttTRANSACTION_PROPOSAL_CREATE (92)
|
||||
* Type: ttTRANSACTION_PROPOSAL_CREATE (93)
|
||||
* Delegable: Delegation::NotDelegable
|
||||
* Amendment: featureCosign
|
||||
* Privileges: Privilege::NoPriv
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
#include <xrpl/shamap/SHAMapLeafNode.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
#include <xrpl/shamap/SHAMapNodeID.h>
|
||||
#include <xrpl/shamap/SHAMapTreeNode.h>
|
||||
|
||||
#include <condition_variable>
|
||||
@@ -34,7 +35,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class SHAMapNodeID;
|
||||
class SHAMapSyncFilter;
|
||||
|
||||
/**
|
||||
@@ -420,7 +420,107 @@ public:
|
||||
invariants() const;
|
||||
|
||||
private:
|
||||
using SharedPtrNodeStack = std::stack<std::pair<SHAMapTreeNodePtr, SHAMapNodeID>>;
|
||||
/**
|
||||
* A path from the root of the map down to some node, pairing each node with the ID naming its
|
||||
* position.
|
||||
*
|
||||
* The two halves of an entry must agree, and the only way to get that wrong is to compute an ID
|
||||
* from the wrong branch. So this type does not accept an ID at all: every push takes the branch
|
||||
* being descended and derives the ID itself, so a node and its ID cannot disagree. Reads are
|
||||
* exposed through the same accessors a std::stack would offer.
|
||||
*/
|
||||
class NodePathStack
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] bool
|
||||
empty() const
|
||||
{
|
||||
return stack_.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t
|
||||
size() const
|
||||
{
|
||||
return stack_.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<SHAMapTreeNodePtr, SHAMapNodeID> const&
|
||||
top() const
|
||||
{
|
||||
XRPL_ASSERT(!stack_.empty(), "xrpl::SHAMap::NodePathStack::top : non-empty stack");
|
||||
return stack_.top();
|
||||
}
|
||||
|
||||
void
|
||||
pop()
|
||||
{
|
||||
XRPL_ASSERT(!stack_.empty(), "xrpl::SHAMap::NodePathStack::pop : non-empty stack");
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
stack_ = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a path at the root of the map, whose ID is the zero-depth ID by definition.
|
||||
*/
|
||||
void
|
||||
pushRoot(SHAMapTreeNodePtr node)
|
||||
{
|
||||
XRPL_ASSERT(stack_.empty(), "xrpl::SHAMap::NodePathStack::pushRoot : empty stack");
|
||||
stack_.emplace(std::move(node), SHAMapNodeID{});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the path to the child of the current node reached by `branch`.
|
||||
*
|
||||
* A node keeps the depth it was reached at, never a normalized kLeafDepth. Only a leaf may
|
||||
* sit at kLeafDepth, since an inner node there would have no branch left to select.
|
||||
*/
|
||||
void
|
||||
pushChild(SHAMapTreeNodePtr node, unsigned int branch)
|
||||
{
|
||||
XRPL_ASSERT(node, "xrpl::SHAMap::NodePathStack::pushChild : non-null node input");
|
||||
XRPL_ASSERT(
|
||||
!stack_.empty(), "xrpl::SHAMap::NodePathStack::pushChild : non-empty stack");
|
||||
auto childID = stack_.top().second.getChildNodeID(branch);
|
||||
XRPL_ASSERT_IF(
|
||||
node->isInner(),
|
||||
childID.getDepth() < kLeafDepth,
|
||||
"xrpl::SHAMap::NodePathStack::pushChild : inner node above leaf depth");
|
||||
XRPL_ASSERT_IF(
|
||||
node->isLeaf(),
|
||||
childID.isPrefixOf(leafKey(*node)),
|
||||
"xrpl::SHAMap::NodePathStack::pushChild : leaf key below branch");
|
||||
stack_.emplace(std::move(node), std::move(childID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the path to a node lying on the path to `target`.
|
||||
*
|
||||
* For nodes not reached by descending a known branch: the walk tracks only the key it is
|
||||
* heading for, or the node is newly created. Either way `target` selects the branch.
|
||||
*/
|
||||
void
|
||||
pushNode(SHAMapTreeNodePtr node, uint256 const& target)
|
||||
{
|
||||
if (stack_.empty())
|
||||
{
|
||||
pushRoot(std::move(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
pushChild(std::move(node), selectBranch(stack_.top().second, target));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::stack<std::pair<SHAMapTreeNodePtr, SHAMapNodeID>> stack_;
|
||||
};
|
||||
|
||||
using DeltaRef =
|
||||
std::pair<boost::intrusive_ptr<SHAMapItem const>, boost::intrusive_ptr<SHAMapItem const>>;
|
||||
|
||||
@@ -447,7 +547,7 @@ private:
|
||||
* Update hashes up to the root
|
||||
*/
|
||||
void
|
||||
dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr terminal);
|
||||
dirtyUp(NodePathStack& stack, uint256 const& target, SHAMapTreeNodePtr terminal);
|
||||
|
||||
/**
|
||||
* Walk towards the specified id, returning the node. Caller must check
|
||||
@@ -455,7 +555,7 @@ private:
|
||||
* id
|
||||
*/
|
||||
SHAMapLeafNode*
|
||||
walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack = nullptr) const;
|
||||
walkTowardsKey(uint256 const& id, NodePathStack* stack = nullptr) const;
|
||||
/**
|
||||
* Return nullptr if key not found
|
||||
*/
|
||||
@@ -482,27 +582,15 @@ private:
|
||||
SHAMapTreeNodePtr
|
||||
writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const;
|
||||
|
||||
// returns the first item at or below this node
|
||||
SHAMapLeafNode*
|
||||
firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, unsigned int branch = 0u) const;
|
||||
|
||||
// returns the last item at or below this node
|
||||
SHAMapLeafNode*
|
||||
lastBelow(
|
||||
SHAMapTreeNodePtr node,
|
||||
SharedPtrNodeStack& stack,
|
||||
unsigned int branch = kBranchFactor) const;
|
||||
|
||||
// direction in which belowHelper scans an inner node's branches
|
||||
// direction in which a scan walks an inner node's branches
|
||||
enum class BelowDirection { First, Last };
|
||||
|
||||
// helper function for firstBelow and lastBelow
|
||||
/**
|
||||
* Returns the first or last item at or below the node already on top of `stack`, extending
|
||||
* `stack` with the path walked to reach it.
|
||||
*/
|
||||
SHAMapLeafNode*
|
||||
belowHelper(
|
||||
SHAMapTreeNodePtr node,
|
||||
SharedPtrNodeStack& stack,
|
||||
unsigned int branch,
|
||||
BelowDirection direction) const;
|
||||
belowHelper(NodePathStack& stack, BelowDirection direction) const;
|
||||
|
||||
// Simple descent
|
||||
// Get a child of the specified node
|
||||
@@ -550,9 +638,9 @@ private:
|
||||
hasLeafNode(uint256 const& tag, SHAMapHash const& hash) const;
|
||||
|
||||
SHAMapLeafNode const*
|
||||
peekFirstItem(SharedPtrNodeStack& stack) const;
|
||||
peekFirstItem(NodePathStack& stack) const;
|
||||
SHAMapLeafNode const*
|
||||
peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const;
|
||||
peekNextItem(uint256 const& id, NodePathStack& stack) const;
|
||||
bool
|
||||
walkBranch(
|
||||
SHAMapTreeNode* node,
|
||||
@@ -697,7 +785,7 @@ public:
|
||||
using pointer = value_type const*;
|
||||
|
||||
private:
|
||||
SharedPtrNodeStack stack_;
|
||||
NodePathStack stack_;
|
||||
SHAMap const* map_ = nullptr;
|
||||
pointer item_ = nullptr;
|
||||
|
||||
@@ -723,7 +811,7 @@ public:
|
||||
private:
|
||||
explicit ConstIterator(SHAMap const* map);
|
||||
ConstIterator(SHAMap const* map, std::nullptr_t);
|
||||
ConstIterator(SHAMap const* map, pointer item, SharedPtrNodeStack&& stack);
|
||||
ConstIterator(SHAMap const* map, pointer item, NodePathStack&& stack);
|
||||
|
||||
friend bool
|
||||
operator==(ConstIterator const& x, ConstIterator const& y);
|
||||
@@ -742,10 +830,7 @@ inline SHAMap::ConstIterator::ConstIterator(SHAMap const* map, std::nullptr_t) :
|
||||
{
|
||||
}
|
||||
|
||||
inline SHAMap::ConstIterator::ConstIterator(
|
||||
SHAMap const* map,
|
||||
pointer item,
|
||||
SharedPtrNodeStack&& stack)
|
||||
inline SHAMap::ConstIterator::ConstIterator(SHAMap const* map, pointer item, NodePathStack&& stack)
|
||||
: stack_(std::move(stack)), map_(map), item_(item)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -129,6 +130,14 @@ public:
|
||||
view_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a newly-created order book directory with the shared,
|
||||
* process-wide OrderBookDB, unless this transaction is being applied
|
||||
* under TapDryRun.
|
||||
*/
|
||||
void
|
||||
addOrderBook(Book const& book);
|
||||
|
||||
ApplyViewContext
|
||||
getApplyViewContext()
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
@@ -410,16 +411,21 @@ invokeCheckPermission(ReadView const& view, STTx const& tx);
|
||||
*
|
||||
* No validation is done or implied by this function.
|
||||
*
|
||||
* Caller is responsible for handling any exceptions.
|
||||
* Since none should be thrown, that will usually
|
||||
* mean terminating.
|
||||
*
|
||||
* Callers do not expect this function to throw; exceptions from a transactor's
|
||||
* `calculateBaseFee` are caught and reported as an error instead.
|
||||
* @param view The current open ledger.
|
||||
* @param tx The transaction to be checked.
|
||||
*
|
||||
* @return The base fee.
|
||||
* @return The base fee on success. Returns `std::unexpected(temUNKNOWN)` if the transaction
|
||||
* type is not recognized, and `std::unexpected(tefEXCEPTION)` if the transactor's
|
||||
* `calculateBaseFee` threw.
|
||||
*
|
||||
* @note Failure is reported as an error rather than a fee of zero because a
|
||||
* zero (or default) fee would pass checkFee and let the transaction be
|
||||
* applied for less than it owes. Callers that only need a fee hint may fall
|
||||
* back to a default; callers deciding whether to apply should reject.
|
||||
*/
|
||||
XRPAmount
|
||||
[[nodiscard]] std::expected<XRPAmount, TER>
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,9 +38,11 @@ namespace xrpl {
|
||||
* f. A Loan must reference a live `ltLOAN_BROKER`, and that broker must
|
||||
* reference a live `ltVAULT`.
|
||||
* g. Post-conditions for the Loan paid down by a successful `ttLOAN_PAY`:
|
||||
* `PaymentRemaining > 0` after: `PrincipalOutstanding` and
|
||||
* `PaymentRemaining` strictly decrease; `NextPaymentDueDate`
|
||||
* advances by N * `PaymentInterval`, N > 0.
|
||||
* `PaymentRemaining > 0` after: neither `PrincipalOutstanding` nor
|
||||
* `TotalValueOutstanding` increases, and at least one of them
|
||||
* strictly decreases;
|
||||
* `PaymentRemaining` strictly decreases;
|
||||
* `NextPaymentDueDate` advances by N * `PaymentInterval`, N > 0.
|
||||
* `PaymentRemaining == 0` after: pinned by checks 1 and 5b.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Updates the encrypted mirror balances of a Confidential MPToken.
|
||||
*
|
||||
* @details
|
||||
* This transaction updates a single holder's mirrored confidential balances
|
||||
* (`sfIssuerEncryptedBalance` and/or `sfAuditorEncryptedBalance`) with the latest
|
||||
* ElGamal public keys defined on the `MPTokenIssuance`.
|
||||
*
|
||||
* It supports both issuer and holder self-migration modes, each mode supports multiple flows:
|
||||
* - Issuer mode: Submitted by the issuer.
|
||||
* 1. Issuer Key Rotation Migration: Re-encrypts the
|
||||
* holder's `sfIssuerEncryptedBalance` under the issuer's new ElGamal public key.
|
||||
*
|
||||
* 2. Auditor Key Rotation Migration: Re-encrypts the
|
||||
* holder's `sfAuditorEncryptedBalance` under the auditor's new ElGamal public key.
|
||||
*
|
||||
* 3. Simultaneous Rotation Migration: Updates both the issuer
|
||||
* and auditor encrypted balances in a single transaction to optimize network throughput.
|
||||
*
|
||||
* 4. Auditor Late-Registration Migration: When the issuer ElGamal
|
||||
* public key is already registered on the `MPTokenIssuance` object, the issuer can
|
||||
* register an auditor key at a later time through `MPTokenIssuanceSet`. Then the issuer uses this
|
||||
* flow to set the holder's initial `sfAuditorEncryptedBalance` on `MPToken` object.
|
||||
*
|
||||
* - Holder self-migration mode: Submitted by the holder. The holder decrypts their own
|
||||
* `sfConfidentialBalanceSpending` with holder's private key to recover the balance and
|
||||
* re-encrypts it under the relevant new ElGamal public key(s). This mode is always
|
||||
* available to the holder and is not conditioned on the issuer being unable to migrate
|
||||
* them: the ledger cannot verify whether an issuer has really lost its private key. That
|
||||
* loss is only the expected motivation, since an issuer that still holds its key can
|
||||
* migrate holders itself in issuer mode.
|
||||
* @note All holder migration flows strictly require the holder's
|
||||
* `sfConfidentialBalanceInbox` to be canonically zero; the holder must run
|
||||
* `ConfidentialMPTMergeInbox` first so the spending balance reflects the
|
||||
* full balance.
|
||||
*
|
||||
* 5. Holder Issuer-Mirror Migration: Re-encrypts the holder's
|
||||
* `sfIssuerEncryptedBalance` under the issuer's new ElGamal public key.
|
||||
*
|
||||
* 6. Holder Auditor-Mirror Migration: Re-encrypts the holder's
|
||||
* `sfAuditorEncryptedBalance` under the auditor's new ElGamal public key, or
|
||||
* sets it for the first time when the auditor key was late-registered. This is the
|
||||
* holder-driven counterpart to flows 2 and 4, for when the issuer does not migrate
|
||||
* the holder itself.
|
||||
*
|
||||
* 7. Simultaneous Holder Self-Migration: Updates both the issuer and auditor
|
||||
* encrypted balances in a single transaction (both keys have rotated).
|
||||
*/
|
||||
class ConfidentialMPTMirrorUpdate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
|
||||
|
||||
explicit ConfidentialMPTMirrorUpdate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
void
|
||||
visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override;
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalizeInvariants(
|
||||
STTx const& tx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -131,6 +131,9 @@ Rust toolchain:
|
||||
✅ rust-analyzer
|
||||
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
|
||||
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
|
||||
✅ rust-nightly
|
||||
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
|
||||
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly
|
||||
✅ rustc
|
||||
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc
|
||||
@@ -140,4 +143,4 @@ Rust toolchain:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 44 checked tools are present and runnable.
|
||||
✅ All 45 checked tools are present and runnable.
|
||||
|
||||
@@ -131,6 +131,9 @@ Rust toolchain:
|
||||
✅ rust-analyzer
|
||||
rust-analyzer 1.97.1 (8bab26f 2026-07-14)
|
||||
/nix/store/lr3m97p3hx1k22a7c44pb0wa7rbayhfi-rust-analyzer-preview-1.97.1-x86_64-unknown-linux-gnu/bin/rust-analyzer
|
||||
✅ rust-nightly
|
||||
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
|
||||
/nix/store/j7kf7a5h4xypzp6x1skg4dsdx2k4fwb3-rust-nightly/bin/rust-nightly
|
||||
✅ rustc
|
||||
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
||||
/nix/store/40d3mzka7r1ps71l0yv2fs6616nbw85m-rust-minimal-1.97.1/bin/rustc
|
||||
@@ -168,4 +171,4 @@ Mold:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 52 checked tools are present and runnable.
|
||||
✅ All 53 checked tools are present and runnable.
|
||||
|
||||
@@ -131,6 +131,9 @@ Rust toolchain:
|
||||
✅ rust-analyzer
|
||||
rust-analyzer 1.97.1 (8bab26f 2026-07-14)
|
||||
/nix/store/262830dlw2517lnagfx7i7agqgl4fmsd-rust-analyzer-preview-1.97.1-aarch64-unknown-linux-gnu/bin/rust-analyzer
|
||||
✅ rust-nightly
|
||||
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
|
||||
/nix/store/c59pxk1yikdlf129qwyg4fplmxcrha0k-rust-nightly/bin/rust-nightly
|
||||
✅ rustc
|
||||
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
||||
/nix/store/a6p27cg6b8szfixfyvkssx6l0c345zw8-rust-minimal-1.97.1/bin/rustc
|
||||
@@ -168,4 +171,4 @@ Mold:
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
✅ All 52 checked tools are present and runnable.
|
||||
✅ All 53 checked tools are present and runnable.
|
||||
|
||||
@@ -15,7 +15,8 @@ package/
|
||||
publish_pkg.py Uploads built packages to the XRPLF Nexus repositories (called by CI, and shipped in that image)
|
||||
rpm/
|
||||
xrpld.spec RPM spec
|
||||
debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, xrpld.lintian-overrides, source/format)
|
||||
debian/ Debian control files (control.in, lintian-overrides.in, rules, copyright, docs, links, source/format).
|
||||
The `.in` files are templates rendered by `build_pkg.py`; `docs` and `links` are staged under the package name
|
||||
shared/
|
||||
xrpld.service systemd unit file (used by both RPM and DEB)
|
||||
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
|
||||
@@ -32,20 +33,74 @@ packaging job cannot drift apart. Today only `linux/amd64` is emitted. The map
|
||||
pins the full container image in `image` — edit that field to move to a new
|
||||
image and both CI and local builds pick it up — and names the format that image
|
||||
builds in `type`, which CI passes to `build_pkg.py` as `--package-type`; the two
|
||||
have to stay in step.
|
||||
have to stay in step. An optional `variant` names a flavour of the package (see
|
||||
[Package variants](#package-variants)), and CI passes it as `--variant`.
|
||||
|
||||
| Package type | Image (`configs.<distro>[].package.image` in `linux.json`) | Tools required |
|
||||
| ------------ | ---------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-<sha>` | `rpmbuild`, `rpmsign` |
|
||||
| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-<sha>` | `dpkg-buildpackage`, debhelper with compat level 13, `lintian` |
|
||||
|
||||
To print the full packaging matrix (artifact names and images) for the current
|
||||
`linux.json`:
|
||||
To print the full packaging matrix (artifact names, images and package names)
|
||||
for the current `linux.json`:
|
||||
|
||||
```bash
|
||||
./.github/scripts/strategy-matrix/generate.py --packaging
|
||||
```
|
||||
|
||||
## Package variants
|
||||
|
||||
A config whose binaries are not the plain release build cannot be packaged as
|
||||
`xrpld`: both would carry the same name and version, so whichever published last
|
||||
would win. It is packaged as a **variant** instead — `variant: "assert"` in its
|
||||
`package` map, which CI passes to `build_pkg.py` as `--variant assert`,
|
||||
producing `xrpld-assert`. What the build option itself does is a build concern,
|
||||
not a packaging one; see the options table in [`BUILD.md`](../BUILD.md).
|
||||
|
||||
A variant ships the same paths as `xrpld` — `/usr/bin/xrpld`, `/etc/xrpld`,
|
||||
`xrpld.service`, `/etc/logrotate.d/xrpld` — differing only in the per-package
|
||||
documentation directory, so it declares itself a stand-in for the plain package
|
||||
rather than something installable next to it: `Conflicts`, `Replaces` and a
|
||||
versioned `Provides: xrpld` on Debian, `Conflicts` and `Provides` on RPM.
|
||||
Neither format declares `Obsoletes`, so `apt upgrade` and `dnf upgrade` keep an
|
||||
installed flavour on its own flavour, and switching is always explicit:
|
||||
|
||||
```bash
|
||||
apt-get install xrpld-assert # apt removes the plain package itself
|
||||
dnf swap xrpld xrpld-VARIANT # 'dnf install' alone stops at the conflict
|
||||
```
|
||||
|
||||
Only the DEB packages carry a variant today — `xrpld-assert` comes from the
|
||||
`debian` config alone, there being no call for an assert build on RHEL-based
|
||||
distributions — but the RPM side works the same way if one is added.
|
||||
|
||||
A switch is a removal plus an installation rather than an upgrade, so unlike a
|
||||
version upgrade it stops the service: Debian's scriptlets start it again, while
|
||||
on RPM the operator runs `systemctl start xrpld`. Configuration survives either
|
||||
way, being conffiles on Debian and `%config(noreplace)` on RPM.
|
||||
|
||||
`dnf` installs the replacement before erasing the old flavour, whose `%preun`
|
||||
would leave `xrpld.service` disabled, so `%postun` re-applies the preset when
|
||||
the unit file outlives the erase — which, since rpm keeps a file another
|
||||
installed package owns, happens only during a swap. The cost is that a
|
||||
deliberate `systemctl disable` is not carried across an RPM switch.
|
||||
|
||||
The alternative is an `xrpld-common` package owning the unit, the sysusers and
|
||||
tmpfiles snippets and the configuration, required by both flavours at an exact
|
||||
version: nothing is erased mid-swap, so no scriptlet has to detect one. It is
|
||||
not worth it for a single variant — it moves files out of the production
|
||||
package, and a sanitizer flavour would likely need its own unit anyway, putting
|
||||
the lifecycle back where it is now.
|
||||
|
||||
Adding a variant is the flavour in `VARIANTS` in `build_pkg.py`, which is the
|
||||
list `--variant` accepts, plus a config in `linux.json` with the CMake arguments
|
||||
and a `package` map naming it, for one format or for both: `generate.py
|
||||
--packaging` emits the package names per format, and the `test-install-deb` and
|
||||
`test-install-rpm` jobs install what their own format produced.
|
||||
|
||||
Operators switch between the flavours as described in
|
||||
[`docs/install.md`](../docs/install.md#optional-the-assert-enabled-build).
|
||||
|
||||
## Building packages
|
||||
|
||||
### Via CI
|
||||
@@ -56,9 +111,11 @@ Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call
|
||||
1. `package` fans out one job per config carrying a `package` map, building and
|
||||
signing in that config's container, and uploading `<config>-pkg` alongside
|
||||
`<config>-pkg-debug` for the much larger debug symbols.
|
||||
2. `test-install` installs `<config>-pkg` in the container of every distro the
|
||||
packages target and runs the binaries there, so one that cannot be installed
|
||||
never reaches Nexus.
|
||||
2. `test-install-deb` and `test-install-rpm` call
|
||||
[`reusable-package-test-install.yml`](../.github/workflows/reusable-package-test-install.yml)
|
||||
with their format's package names and distro images, installing each package
|
||||
in the container of every distro that format targets and running the binaries
|
||||
there, so one that cannot be installed never reaches Nexus.
|
||||
3. `publish` uploads both artifacts, or lists what it would upload.
|
||||
|
||||
The packaging script derives the package version from the downloaded binary's
|
||||
@@ -104,6 +161,9 @@ docker run --rm \
|
||||
# build/rpmbuild/RPMS/x86_64/*.rpm
|
||||
```
|
||||
|
||||
Add `--variant assert` to package binaries built with `-Dassert=ON`; the package
|
||||
is then named `xrpld-assert`.
|
||||
|
||||
### Via CMake (host-side target)
|
||||
|
||||
If you run CMake configure on a host that has `rpmbuild` or `dpkg-buildpackage`
|
||||
@@ -133,11 +193,17 @@ The package version is not a CMake input on this path: `build_pkg.py` derives it
|
||||
from the just-built `xrpld` binary's `xrpld --version` output. The package
|
||||
release defaults to 1 and is overridable with `-Dpkg_release=N`.
|
||||
|
||||
`-Dassert=ON` passes `--variant assert`, so such a build packages as
|
||||
`xrpld-assert` without anything else being asked for.
|
||||
|
||||
## Publishing packages
|
||||
|
||||
Packages are published to the XRPLF repositories on Sonatype Nexus at
|
||||
`https://packages.xrplf.org`. The `release-info` action decides the channel from
|
||||
the event, and `publish_pkg.py` maps that channel to its repositories:
|
||||
Packages are published to the XRPLF repositories on Sonatype Nexus through
|
||||
`https://packages-upload.xrplf.org`. Reads go through
|
||||
`https://packages.xrplf.org`, which Cloudflare proxies to cache them and which
|
||||
rejects request bodies over 100 MB, so uploads use the DNS-only host instead.
|
||||
The `release-info` action decides the channel from the event, and
|
||||
`publish_pkg.py` maps that channel to its repositories:
|
||||
|
||||
| Event | Version | Channel | DEB repository | RPM upload repository |
|
||||
| ------------------------ | ----------------- | --------- | -------------- | --------------------- |
|
||||
@@ -147,6 +213,9 @@ the event, and `publish_pkg.py` maps that channel to its repositories:
|
||||
| push to `develop` | `xrpld --version` | `develop` | `deb-develop` | `rpm-develop-hosted` |
|
||||
| tag, non-public codebase | _any_ | `private` | `deb-private` | `rpm-private-hosted` |
|
||||
|
||||
A variant is published to the same channel under its own name, so
|
||||
`xrpld-assert` never overwrites `xrpld`.
|
||||
|
||||
Only a tag names a channel — do not extend that to `develop`, where
|
||||
`BuildInfo.cpp`'s `versionString` moves through `-bN`, `-rcN` and even the final
|
||||
version during a release cycle, which would send develop builds into `stable`.
|
||||
@@ -160,7 +229,7 @@ the last, and the date and hash say which commit a package on
|
||||
`packages.xrplf.org` came from. Both reach the packaging scripts as arguments,
|
||||
so neither script derives anything itself.
|
||||
|
||||
Publishing is its own job, gated behind `test-install`, uploading from the same
|
||||
Publishing is its own job, gated behind the install tests, uploading from the same
|
||||
image that built the packages with the `publish_pkg.py` shipped in it — the
|
||||
same copy other repositories run. Without `publish: true` the job is a
|
||||
`--dry-run`, listing the uploads it would make without needing credentials, so
|
||||
@@ -175,7 +244,7 @@ Nexus owns the repository metadata; nothing here indexes anything. Worth knowing
|
||||
- Each apt-hosted repository needs a distribution (ours use `any`) and a PGP
|
||||
signing keypair configured in Nexus, which rejects one created without a
|
||||
keypair. Nexus signs the apt metadata with it, never the packages.
|
||||
- Hosted yum repositories cannot be signed by Nexus, so each `rpm-<channel>-hosted`
|
||||
- yum-hosted repositories cannot be signed by Nexus, so each `rpm-<channel>-hosted`
|
||||
repository sits behind a `rpm-<channel>` yum group repository whose metadata
|
||||
Nexus signs. Uploads go to the hosted repository; clients point at the group
|
||||
and verify the metadata with `repo_gpgcheck=1`. Nexus never signs the RPMs
|
||||
@@ -244,6 +313,19 @@ pre-release ordering convention, so RPM filenames/NVRs begin with forms like
|
||||
`xrpld-3.2.0~b1-...` and `xrpld-3.2.0~rc1-...` instead of encoding
|
||||
pre-releases with an older `0.<release>.<suffix>` RPM `Release` value.
|
||||
|
||||
`--variant` is the flavour of the package, empty by default and accepting only
|
||||
the flavours in `VARIANTS`; see [Package variants](#package-variants). The RPM
|
||||
path passes it to the spec as the `pkg_variant` macro, which suffixes `Name` and
|
||||
adds the `Conflicts`/`Provides` pair. Debian control files have no conditionals, so the DEB path renders
|
||||
`debian/control.in` and `debian/lintian-overrides.in` instead, substituting
|
||||
`@PKG@` with the package name and `@VARIANT_FIELDS@` with the
|
||||
`Conflicts`/`Replaces`/`Provides` block, empty for the plain package; a token
|
||||
with no value fails the build rather than reaching dpkg. The files debhelper
|
||||
keys by package name (`docs`, `links`, and the units) are staged under that same
|
||||
name. The paths inside the package are unchanged either way, so `debian/rules`
|
||||
reads its package name from `dh_listpackages` and names the unit, sysusers,
|
||||
tmpfiles and logrotate files with `--name xrpld`.
|
||||
|
||||
The package format is `--package-type`, either `deb` or `rpm`. It is required,
|
||||
so a job never silently builds the wrong format for the image it runs in; the
|
||||
matching build tool still has to be on PATH.
|
||||
@@ -286,8 +368,13 @@ service restart.
|
||||
1. Creates a staging source tree at `debbuild/source/` inside the build directory.
|
||||
2. Stages the binaries, configs, `README.md`, `LICENSE.md`, and
|
||||
`validator-keys-LICENSE`.
|
||||
3. Copies `package/debian/` control files into `debbuild/source/debian/`.
|
||||
4. Copies shared service/sysusers/tmpfiles/logrotate into `debian/` where `dh_installsystemd`, `dh_installsysusers`, `dh_installtmpfiles` and `dh_installlogrotate` pick them up automatically.
|
||||
3. Stages `package/debian/` into `debbuild/source/debian/`: the `.in` templates
|
||||
are rendered, and the files debhelper keys by package name (`docs`, `links`,
|
||||
`lintian-overrides`) are staged under the name being built.
|
||||
4. Copies shared service/sysusers/tmpfiles/logrotate into `debian/` as
|
||||
`<package>.xrpld.*`, which `dh_installsystemd`, `dh_installsysusers`,
|
||||
`dh_installtmpfiles` and `dh_installlogrotate` read because `debian/rules`
|
||||
passes them `--name xrpld`.
|
||||
5. Generates a minimal `debian/changelog` using `${pkg_version}-${PKG_RELEASE}`,
|
||||
where `pkg_version` is derived from the binary-reported `xrpld` version.
|
||||
6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands.
|
||||
|
||||
@@ -21,6 +21,14 @@ SRC_DIR = Path(__file__).resolve().parents[1]
|
||||
|
||||
PRE_RELEASE = re.compile(r"^(b|rc)(0|[1-9][0-9]*)(\+.*)?$")
|
||||
|
||||
# The package name a variant suffixes, and the name every variant keeps for its
|
||||
# on-disk paths (/usr/bin/xrpld, /etc/xrpld, xrpld.service).
|
||||
BASE_NAME = "xrpld"
|
||||
|
||||
# The flavours that can be built, '' being the plain xrpld package. A variant
|
||||
# needs a config in linux.json to be built by CI; see package/README.md.
|
||||
VARIANTS = ("", "assert")
|
||||
|
||||
# Files both packaging systems consume, staged under the same names.
|
||||
STAGED_FROM_BUILD = ("xrpld", "validator-keys", "validator-keys-LICENSE")
|
||||
STAGED_FROM_SRC = {
|
||||
@@ -31,6 +39,18 @@ STAGED_FROM_SRC = {
|
||||
}
|
||||
STAGED_UNITS = ("xrpld.service", "xrpld.sysusers", "xrpld.tmpfiles", "xrpld.logrotate")
|
||||
|
||||
# debian/ files debhelper keys by package name, staged as '<package>.<name>'.
|
||||
DEBIAN_PKG_FILES = ("docs", "links")
|
||||
|
||||
# Debian control files have no conditionals, so what makes a variant replace the
|
||||
# plain package is rendered into control.in rather than written there.
|
||||
DEB_VARIANT_FIELDS = """\
|
||||
Conflicts: xrpld
|
||||
Replaces: xrpld
|
||||
Provides: xrpld (= ${binary:Version})"""
|
||||
|
||||
TOKEN = re.compile(r"@[A-Z_]+@")
|
||||
|
||||
|
||||
def run(*command: object, cwd: Path | None = None) -> None:
|
||||
"""Echo a command and run it."""
|
||||
@@ -75,6 +95,28 @@ def package_version(reported: str) -> str:
|
||||
return version
|
||||
|
||||
|
||||
def render(template: Path, dest: Path, values: dict[str, str]) -> None:
|
||||
"""Write template to dest with its @TOKEN@ placeholders substituted.
|
||||
|
||||
A token left without a value fails the build rather than reaching dpkg.
|
||||
"""
|
||||
text = template.read_text()
|
||||
for token, value in values.items():
|
||||
text = text.replace(f"@{token}@", value)
|
||||
|
||||
missing = sorted(set(TOKEN.findall(text)))
|
||||
assert not missing, f"{template}: no value for {', '.join(missing)}"
|
||||
|
||||
# An empty value at the end of a stanza would otherwise leave a blank line,
|
||||
# which is what ends a stanza.
|
||||
dest.write_text(text.rstrip("\n") + "\n")
|
||||
|
||||
|
||||
def package_name(variant: str) -> str:
|
||||
"""The binary package name for a variant: '' -> xrpld, 'assert' -> xrpld-assert."""
|
||||
return f"{BASE_NAME}-{variant}" if variant else BASE_NAME
|
||||
|
||||
|
||||
def read_version(xrpld: Path) -> str:
|
||||
"""Read the version from the binary that is about to be packaged."""
|
||||
fields = capture(xrpld, "--version").partition("\n")[0].split()
|
||||
@@ -135,17 +177,18 @@ def stage_common(build_dir: Path, dest: Path) -> None:
|
||||
shutil.copy2(SRC_DIR / source, dest / name)
|
||||
|
||||
|
||||
def stage_units(dest: Path) -> None:
|
||||
def stage_units(dest: Path, *, prefix: str = "") -> None:
|
||||
"""Copy the systemd, sysusers, tmpfiles and logrotate files into dest.
|
||||
|
||||
Each format wants them somewhere else: rpmbuild reads them from SOURCES,
|
||||
debhelper from debian/.
|
||||
Each format wants them somewhere else: rpmbuild reads them from SOURCES by
|
||||
path, debhelper from debian/ by package name -- hence 'prefix', which makes
|
||||
the copies 'xrpld-assert.xrpld.service' and so on.
|
||||
"""
|
||||
for name in STAGED_UNITS:
|
||||
shutil.copy2(SRC_DIR / "package" / "shared" / name, dest / name)
|
||||
shutil.copy2(SRC_DIR / "package" / "shared" / name, dest / f"{prefix}{name}")
|
||||
|
||||
|
||||
def build_rpm(build_dir: Path, *, version: str, pkg_release: str) -> None:
|
||||
def build_rpm(build_dir: Path, *, version: str, pkg_release: str, variant: str) -> None:
|
||||
"""Stage the spec and its sources, then build the binary RPMs."""
|
||||
topdir = build_dir / "rpmbuild"
|
||||
for name in ("BUILD", "BUILDROOT", "RPMS", "SOURCES", "SPECS", "SRPMS"):
|
||||
@@ -156,6 +199,9 @@ def build_rpm(build_dir: Path, *, version: str, pkg_release: str) -> None:
|
||||
stage_common(build_dir, topdir / "SOURCES")
|
||||
stage_units(topdir / "SOURCES")
|
||||
|
||||
# The spec defaults it to nothing, so a plain build is unchanged.
|
||||
variant_defines = ["--define", f"pkg_variant {variant}"] if variant else []
|
||||
|
||||
run(
|
||||
"rpmbuild",
|
||||
"-bb",
|
||||
@@ -168,10 +214,29 @@ def build_rpm(build_dir: Path, *, version: str, pkg_release: str) -> None:
|
||||
# The image tracks the newest distro, but the packages target el9.
|
||||
"--define",
|
||||
"dist .el9",
|
||||
*variant_defines,
|
||||
spec,
|
||||
)
|
||||
|
||||
|
||||
def stage_debian(dest: Path, name: str) -> None:
|
||||
"""Stage the debian directory for the package name being built."""
|
||||
source = SRC_DIR / "package" / "debian"
|
||||
shutil.copytree(
|
||||
source, dest, ignore=shutil.ignore_patterns("*.in", *DEBIAN_PKG_FILES)
|
||||
)
|
||||
|
||||
values = {
|
||||
"PKG": name,
|
||||
"VARIANT_FIELDS": "" if name == BASE_NAME else DEB_VARIANT_FIELDS,
|
||||
}
|
||||
render(source / "control.in", dest / "control", values)
|
||||
render(source / "lintian-overrides.in", dest / f"{name}.lintian-overrides", values)
|
||||
|
||||
for suffix in DEBIAN_PKG_FILES:
|
||||
shutil.copy2(source / suffix, dest / f"{name}.{suffix}")
|
||||
|
||||
|
||||
def build_deb(
|
||||
build_dir: Path,
|
||||
*,
|
||||
@@ -180,21 +245,23 @@ def build_deb(
|
||||
pkg_release: str,
|
||||
channel: str,
|
||||
epoch: int,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Stage the debian directory and its sources, then build the binary DEBs."""
|
||||
staging = build_dir / "debbuild" / "source"
|
||||
stage_common(build_dir, staging)
|
||||
shutil.copytree(SRC_DIR / "package" / "debian", staging / "debian")
|
||||
stage_debian(staging / "debian", name)
|
||||
|
||||
# debhelper picks these up from debian/ automatically.
|
||||
stage_units(staging / "debian")
|
||||
# Prefixed whether it is a variant's name or not: debian/rules names them
|
||||
# explicitly either way.
|
||||
stage_units(staging / "debian", prefix=f"{name}.")
|
||||
|
||||
date = datetime.fromtimestamp(epoch, timezone.utc).strftime(
|
||||
"%a, %d %b %Y %H:%M:%S %z"
|
||||
)
|
||||
# The leading spaces are significant to dpkg.
|
||||
changelog = textwrap.dedent(f"""\
|
||||
xrpld ({version}-{pkg_release}) {channel}; urgency=medium
|
||||
{name} ({version}-{pkg_release}) {channel}; urgency=medium
|
||||
* Release {reported}.
|
||||
|
||||
-- XRPL Foundation <contact@xrplf.org> {date}
|
||||
@@ -223,6 +290,14 @@ def main() -> None:
|
||||
default="1",
|
||||
help="package release iteration (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--variant",
|
||||
default="",
|
||||
choices=VARIANTS,
|
||||
help="the flavour of the package to build: 'assert' produces "
|
||||
"xrpld-assert, which ships the same paths as xrpld and replaces it "
|
||||
"(default: the plain xrpld package)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--channel",
|
||||
required=True,
|
||||
@@ -234,6 +309,8 @@ def main() -> None:
|
||||
build_dir: Path = args.build_dir.resolve()
|
||||
pkg_release: str = args.pkg_release
|
||||
channel: str = args.channel
|
||||
variant: str = args.variant
|
||||
name = package_name(variant)
|
||||
|
||||
assert build_dir.is_dir(), (
|
||||
f"build directory not found: {build_dir}. Build the binaries before "
|
||||
@@ -253,6 +330,8 @@ def main() -> None:
|
||||
for tree in ("debbuild", "rpmbuild"):
|
||||
shutil.rmtree(build_dir / tree, ignore_errors=True)
|
||||
|
||||
print(f"Building {package_type} {name} {version}-{pkg_release}", flush=True)
|
||||
|
||||
if package_type == "deb":
|
||||
build_deb(
|
||||
build_dir,
|
||||
@@ -261,9 +340,10 @@ def main() -> None:
|
||||
pkg_release=pkg_release,
|
||||
channel=channel,
|
||||
epoch=epoch,
|
||||
name=name,
|
||||
)
|
||||
else:
|
||||
build_rpm(build_dir, version=version, pkg_release=pkg_release)
|
||||
build_rpm(build_dir, version=version, pkg_release=pkg_release, variant=variant)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Source: xrpld
|
||||
Source: @PKG@
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: XRPL Foundation <contact@xrplf.org>
|
||||
@@ -11,7 +11,7 @@ Homepage: https://github.com/XRPLF/rippled
|
||||
Vcs-Git: https://github.com/XRPLF/rippled.git
|
||||
Vcs-Browser: https://github.com/XRPLF/rippled
|
||||
|
||||
Package: xrpld
|
||||
Package: @PKG@
|
||||
Architecture: any
|
||||
Depends:
|
||||
${shlibs:Depends},
|
||||
@@ -22,3 +22,4 @@ Description: XRP Ledger daemon
|
||||
transactions, and maintains the ledger database.
|
||||
This package also includes the validator-keys tool for validator key
|
||||
management.
|
||||
@VARIANT_FIELDS@
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user