Compare commits

..

103 Commits

Author SHA1 Message Date
Mayukha Vadari
1eab60b9cb fix branch 2026-05-20 16:33:41 -04:00
Mayukha Vadari
ac794f695d Merge branch 'mvadari/rearch/account' of https://github.com/XRPLF/rippled into mvadari/rearch/token 2026-05-15 18:00:06 -04:00
Mayukha Vadari
d38f45701f clean up (roll back AI weirdness) 2026-05-15 17:35:14 -04:00
Mayukha Vadari
b557403ab6 fix clang-tidy issues 2026-05-15 16:19:09 -04:00
Mayukha Vadari
8c80a6aad5 Merge branch 'develop' into mvadari/rearch/account 2026-05-15 15:07:01 -04:00
Mayukha Vadari
9b1406ad46 Merge branch 'develop' of https://github.com/XRPLF/rippled into mvadari/rearch/account 2026-05-15 14:54:02 -04:00
Mayukha Vadari
1306d35e4d Merge branch 'develop' into mvadari/rearch/account 2026-05-15 11:28:34 -04:00
Mayukha Vadari
144835ca5d fix clang-tidy 2026-05-14 18:36:48 -04:00
Mayukha Vadari
554c0baa0a fix tests 2026-05-14 17:33:10 -04:00
Mayukha Vadari
7661483c4c fix clang-tidy issues 2026-05-14 11:39:44 -04:00
Mayukha Vadari
6cb5842a05 fix build 2026-05-14 11:14:47 -04:00
Mayukha Vadari
bcd26397fb Merge branch 'develop' into mvadari/rearch/account 2026-05-14 10:10:55 -04:00
Mayukha Vadari
ffaf51043d fix clang-tidy issues 2026-05-04 16:37:38 -04:00
Mayukha Vadari
cedebd5a86 fix tests 2026-05-04 16:30:38 -04:00
Mayukha Vadari
700cd6a2b1 Merge branch 'develop' into mvadari/rearch/account 2026-05-04 14:42:01 -04:00
Mayukha Vadari
3d2e0788cc fix clang-tidy 2026-04-22 14:19:12 -04:00
Mayukha Vadari
d7b6c8e098 Update include/xrpl/ledger/helpers/SLEBase.h
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-04-22 13:44:38 -04:00
Mayukha Vadari
d10bb71eea Merge branch 'develop' into mvadari/rearch/account 2026-04-22 13:43:42 -04:00
Mayukha Vadari
5dbbd9ba8d is_writable -> isWritable 2026-04-22 13:41:44 -04:00
Mayukha Vadari
fb37cc2f42 oops 2026-04-21 16:16:13 -04:00
Mayukha Vadari
fabf328485 fix build (smh clang-tidy) 2026-04-21 15:46:21 -04:00
Mayukha Vadari
dc2430ea86 fix clang-tidy issues 2026-04-21 15:22:12 -04:00
Mayukha Vadari
0de37c2cad respond to comments 2026-04-21 15:14:07 -04:00
Mayukha Vadari
3d9eba73e8 Merge branch 'develop' into mvadari/rearch/account 2026-04-21 15:01:14 -04:00
Mayukha Vadari
dae0a53c20 Merge branch 'mvadari/rearch/account' into mvadari/rearch/token 2026-04-10 18:06:38 -04:00
Mayukha Vadari
ea3ca12894 fix clang-tidy 2026-04-09 18:05:07 -04:00
Mayukha Vadari
7b27096aac Merge branch 'develop' into mvadari/rearch/account 2026-04-09 18:04:49 -04:00
Mayukha Vadari
c00491baa1 fix one last typo bug 2026-04-09 17:52:04 -04:00
Mayukha Vadari
d616aff4e2 fix tests (typos) 2026-04-09 17:14:46 -04:00
Mayukha Vadari
be9913f16f Merge branch 'develop' into mvadari/rearch/account 2026-04-08 16:51:06 -04:00
Mayukha Vadari
b99a7666ab respond to human comments 2026-04-08 16:50:55 -04:00
Mayukha Vadari
683ff84ee0 respond to copilot comments 2026-04-08 16:42:53 -04:00
Mayukha Vadari
9c9281e5d9 Merge branch 'develop' into mvadari/rearch/account 2026-04-08 16:33:54 -04:00
Mayukha Vadari
2d6450904d Merge branch 'develop' into mvadari/rearch/account 2026-04-07 09:22:44 -04:00
Mayukha Vadari
8269964c76 fix clang-tidy issues 2026-04-03 11:05:55 -04:00
Mayukha Vadari
945b4f77cd roll back unrelated changes 2026-04-03 10:42:51 -04:00
Mayukha Vadari
03863d91ad fix clang-tidy issues 2026-04-03 10:37:45 -04:00
Mayukha Vadari
737d128cca fix build issues 2026-04-03 10:08:20 -04:00
Mayukha Vadari
97629e7477 fix clang-tidy issues 2026-04-03 10:02:48 -04:00
Mayukha Vadari
b0ca6adf7a Merge branch 'develop' of https://github.com/XRPLF/rippled into mvadari/rearch/account 2026-04-03 09:58:21 -04:00
Mayukha Vadari
e2238b13de fix merge issues 2026-04-02 18:56:33 -04:00
Mayukha Vadari
27e9d0d695 Merge branch 'develop' into mvadari/rearch/account 2026-04-02 18:55:11 -04:00
Mayukha Vadari
b3829e5e61 update readme 2026-04-02 18:48:00 -04:00
Mayukha Vadari
50968111da minor cleanups 2026-04-02 18:45:24 -04:00
Mayukha Vadari
fa77338bdc cleanup 2026-04-02 18:35:15 -04:00
Mayukha Vadari
ffd96e354f remove old isPseudoAccount helper 2026-04-02 18:28:29 -04:00
Mayukha Vadari
e9287812ef move Journal to constructor 2026-04-02 18:03:35 -04:00
Mayukha Vadari
819d3fcdfc Merge branch 'develop' into mvadari/rearch/account 2026-04-02 16:00:16 -04:00
Mayukha Vadari
dcdc5e1b52 fix clang-tidy 2026-03-24 16:12:37 -07:00
Mayukha Vadari
c0895c6e2e fix pre-commit issues 2026-03-24 14:45:37 -07:00
Mayukha Vadari
5b0b1ff1f6 refactor: Replace SLE helper inheritance with view-parameterized templates 2026-03-24 14:41:19 -07:00
Mayukha Vadari
e9c4ed6a74 establish architecture for creating a new object 2026-03-24 12:19:16 -07:00
Mayukha Vadari
deaa23f4e5 cleanup 2026-03-24 12:19:16 -07:00
Mayukha Vadari
638477c824 fix pre-commit issues 2026-03-24 12:19:16 -07:00
Mayukha Vadari
9b42e178a2 add readme 2026-03-24 12:19:16 -07:00
Mayukha Vadari
f41c02d486 WrappedSLEBase -> SLEBase 2026-03-24 12:19:16 -07:00
Mayukha Vadari
a423813098 fix issues 2026-03-24 12:19:16 -07:00
Mayukha Vadari
56c173a097 fix account_ 2026-03-24 12:19:16 -07:00
Mayukha Vadari
7900fa9ead more fixes 2026-03-24 12:19:14 -07:00
Mayukha Vadari
0ffb3e2227 fix long tail issues 2026-03-24 12:14:31 -07:00
Mayukha Vadari
add3d7e68d fix more AccountRoot stuff 2026-03-24 12:14:31 -07:00
Mayukha Vadari
c24432f43a change . to -> 2026-03-24 12:14:31 -07:00
Mayukha Vadari
1ccd84e43a clean up comments 2026-03-24 12:14:31 -07:00
Mayukha Vadari
1cc7424934 Add insert/update/erase to WritableSLE 2026-03-24 12:14:31 -07:00
Mayukha Vadari
b5562cc81e split up read only and write 2026-03-24 12:14:30 -07:00
Mayukha Vadari
e6369c0faa minor improvements 2026-03-24 12:14:30 -07:00
Mayukha Vadari
43caa7d47a fix remaining build issues 2026-03-24 12:14:30 -07:00
Mayukha Vadari
e0f487bb2e fix more build issues 2026-03-24 12:14:30 -07:00
Mayukha Vadari
a8987cf271 fix all the build issues 2026-03-24 12:14:29 -07:00
Mayukha Vadari
4157e3684c First cut of WrappedAccountRoot 2026-03-24 12:13:58 -07:00
Mayukha Vadari
659f455335 add WrappedSLEBase 2026-03-24 12:13:58 -07:00
Mayukha Vadari
3bc460951c add makeNew functions 2026-03-23 11:07:31 -07:00
Mayukha Vadari
d1dae53097 fix clang-tidy 2026-03-23 10:54:06 -07:00
Mayukha Vadari
733dd51720 fix includes 2026-03-23 10:54:06 -07:00
Mayukha Vadari
9d67db1843 fix rebase issues 2026-03-23 10:54:06 -07:00
Mayukha Vadari
29bb59698f fix pre-commit issues 2026-03-23 10:54:06 -07:00
Mayukha Vadari
3b2dcefb81 fix more stuff 2026-03-23 10:54:06 -07:00
Mayukha Vadari
4e92ec2daf fix issues 2026-03-23 10:54:06 -07:00
Mayukha Vadari
6afcf51dee rename MPToken -> MPTokenIssuance 2026-03-23 10:54:06 -07:00
Mayukha Vadari
7e62ecb63b more usage of IOUToken 2026-03-23 10:54:06 -07:00
Mayukha Vadari
ad2c359f21 more usage of MPToken 2026-03-23 10:54:06 -07:00
Mayukha Vadari
96d4a69a37 add some more helper functions 2026-03-23 10:54:06 -07:00
Mayukha Vadari
14fbdd5a1c implement most of the token stuff 2026-03-23 10:54:06 -07:00
Mayukha Vadari
3c5e87888e establish architecture for creating a new object 2026-03-23 10:53:45 -07:00
Mayukha Vadari
8e218a6285 cleanup 2026-03-23 09:59:13 -07:00
Mayukha Vadari
8a1adc4ec9 fix pre-commit issues 2026-03-23 09:29:38 -07:00
Mayukha Vadari
c9db124015 add readme 2026-03-23 09:23:50 -07:00
Mayukha Vadari
7056b43c5e WrappedSLEBase -> SLEBase 2026-03-23 09:23:43 -07:00
Mayukha Vadari
ee370fefee fix issues 2026-03-23 09:23:02 -07:00
Mayukha Vadari
e68d74cda4 fix account_ 2026-03-23 09:23:01 -07:00
Mayukha Vadari
89a3a6c379 more fixes 2026-03-23 09:22:59 -07:00
Mayukha Vadari
f3a6d17b5b fix long tail issues 2026-03-23 09:21:44 -07:00
Mayukha Vadari
45531b1eaf fix more AccountRoot stuff 2026-03-23 09:21:44 -07:00
Mayukha Vadari
fa8de2b47f change . to -> 2026-03-23 09:21:44 -07:00
Mayukha Vadari
6fcda8802e clean up comments 2026-03-23 09:21:44 -07:00
Mayukha Vadari
0cf42b150c Add insert/update/erase to WritableSLE 2026-03-23 09:21:44 -07:00
Mayukha Vadari
74b010a905 split up read only and write 2026-03-23 09:21:44 -07:00
Mayukha Vadari
265deccabe minor improvements 2026-03-23 09:21:44 -07:00
Mayukha Vadari
ea1146d413 fix remaining build issues 2026-03-23 09:21:44 -07:00
Mayukha Vadari
5fa9bb53a8 fix more build issues 2026-03-23 09:21:44 -07:00
Mayukha Vadari
d9bf263543 fix all the build issues 2026-03-23 09:21:42 -07:00
Mayukha Vadari
1f73da466c First cut of WrappedAccountRoot 2026-03-23 09:21:17 -07:00
Mayukha Vadari
e9eb5e53c9 add WrappedSLEBase 2026-03-23 09:21:15 -07:00
358 changed files with 6181 additions and 11811 deletions

View File

@@ -191,11 +191,8 @@ CheckOptions:
readability-identifier-naming.ParameterCase: camelBack
readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.MemberCase: camelBack
readability-identifier-naming.PrivateMemberCase: camelBack
readability-identifier-naming.PrivateMemberSuffix: _
readability-identifier-naming.ProtectedMemberCase: camelBack
readability-identifier-naming.ProtectedMemberSuffix: _
readability-identifier-naming.PublicMemberCase: camelBack
readability-identifier-naming.PublicMemberSuffix: ""
readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$"

View File

@@ -32,32 +32,7 @@ We will further set additional CMake arguments as follows:
"""
def build_config_name(os_entry: dict[str, str], platform: str, build_type: str) -> str:
parts = [os_entry["distro_name"]]
for key in ("distro_version", "compiler_name", "compiler_version"):
if value := os_entry[key]:
parts.append(value)
parts.append("arm64" if "arm64" in platform else "amd64")
parts.append(build_type.lower())
return "-".join(parts)
def generate_packaging_matrix(config: Config) -> list[dict]:
"""Emit one entry per os entry with `package: true`. Architecture is
hardcoded to linux/amd64 here (and the runner is hardcoded at the
workflow level) until arm64 packaging is ready.
"""
return [
{
"artifact_name": f"xrpld-{build_config_name(os, 'linux/amd64', 'Release')}",
"os": os,
}
for os in config.os
if os.get("package", False)
]
def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
def generate_strategy_matrix(all: bool, config: Config) -> list:
configurations = []
for architecture, os, build_type, cmake_args in itertools.product(
config.architecture, config.os, config.build_type, config.cmake_args
@@ -126,15 +101,14 @@ def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
continue
# RHEL:
# - 9 using GCC 12: Debug and Release on linux/amd64
# (Release is required for RPM packaging).
# - 9 using GCC 12: Debug on linux/amd64.
# - 10 using Clang: Release on linux/amd64.
if os["distro_name"] == "rhel":
skip = True
if os["distro_version"] == "9":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type in ["Debug", "Release"]
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
skip = False
@@ -149,8 +123,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
continue
# Ubuntu:
# - Jammy using GCC 12: Debug on linux/arm64, Release on
# linux/amd64 (Release is required for DEB packaging).
# - Jammy using GCC 12: Debug on linux/arm64.
# - Noble using GCC 14: Release on linux/amd64.
# - Noble using Clang 18: Debug on linux/amd64.
# - Noble using Clang 19: Release on linux/arm64.
@@ -163,12 +136,6 @@ def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
and architecture["platform"] == "linux/arm64"
):
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
skip = False
elif os["distro_version"] == "noble":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14"
@@ -251,7 +218,17 @@ def generate_strategy_matrix(all: bool, config: Config) -> list[dict]:
# Generate a unique name for the configuration, e.g. macos-arm64-debug
# or debian-bookworm-gcc-12-amd64-release.
config_name = build_config_name(os, architecture["platform"], build_type)
config_name = os["distro_name"]
if (n := os["distro_version"]) != "":
config_name += f"-{n}"
if (n := os["compiler_name"]) != "":
config_name += f"-{n}"
if (n := os["compiler_version"]) != "":
config_name += f"-{n}"
config_name += (
f"-{architecture['platform'][architecture['platform'].find('/')+1:]}"
)
config_name += f"-{build_type.lower()}"
if "-Dcoverage=ON" in cmake_args:
config_name += "-coverage"
if "-Dunity=ON" in cmake_args:
@@ -355,19 +332,10 @@ if __name__ == "__main__":
required=False,
type=Path,
)
parser.add_argument(
"-p",
"--packaging",
help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.",
action="store_true",
)
args = parser.parse_args()
matrix = []
if args.packaging:
config_path = args.config if args.config else THIS_DIR / "linux.json"
matrix += generate_packaging_matrix(read_config(config_path))
elif args.config is None or args.config == "":
if args.config is None or args.config == "":
matrix += generate_strategy_matrix(
args.all, read_config(THIS_DIR / "linux.json")
)

View File

@@ -127,8 +127,7 @@
"distro_version": "9",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9",
"package": true
"image_sha": "4c086b9"
},
{
"distro_name": "rhel",
@@ -170,8 +169,7 @@
"distro_version": "jammy",
"compiler_name": "gcc",
"compiler_version": "12",
"image_sha": "4c086b9",
"package": true
"image_sha": "4c086b9"
},
{
"distro_name": "ubuntu",

View File

@@ -64,13 +64,11 @@ jobs:
.github/workflows/reusable-build-test-config.yml
.github/workflows/reusable-build-test.yml
.github/workflows/reusable-clang-tidy.yml
.github/workflows/reusable-package.yml
.github/workflows/reusable-strategy-matrix.yml
.github/workflows/reusable-test.yml
.github/workflows/reusable-upload-recipe.yml
.clang-tidy
.codecov.yml
cfg/**
cmake/**
conan/**
external/**
@@ -80,10 +78,6 @@ jobs:
CMakeLists.txt
conanfile.py
conan.lock
LICENSE.md
package/**
README.md
- name: Check whether to run
# This step determines whether the rest of the workflow should
# run. The rest of the workflow will run if this job runs AND at
@@ -140,11 +134,6 @@ jobs:
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
package:
needs: [should-run, build-test]
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/reusable-package.yml
upload-recipe:
needs:
- should-run
@@ -179,7 +168,6 @@ jobs:
- check-rename
- clang-tidy
- build-test
- package
- upload-recipe
- notify-clio
runs-on: ubuntu-latest

View File

@@ -1,5 +1,5 @@
# This workflow uploads the libxrpl recipe to the Conan remote and builds
# release packages when a versioned tag is pushed.
# This workflow uploads the libxrpl recipe to the Conan remote when a versioned
# tag is pushed.
name: Tag
on:
@@ -22,22 +22,3 @@ jobs:
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
build-test:
if: ${{ github.repository == 'XRPLF/rippled' }}
uses: ./.github/workflows/reusable-build-test.yml
strategy:
fail-fast: true
matrix:
os: [linux]
with:
ccache_enabled: false
os: ${{ matrix.os }}
strategy_matrix: minimal
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
package:
if: ${{ github.repository == 'XRPLF/rippled' }}
needs: build-test
uses: ./.github/workflows/reusable-package.yml

View File

@@ -21,13 +21,11 @@ on:
- ".github/workflows/reusable-build-test-config.yml"
- ".github/workflows/reusable-build-test.yml"
- ".github/workflows/reusable-clang-tidy.yml"
- ".github/workflows/reusable-package.yml"
- ".github/workflows/reusable-strategy-matrix.yml"
- ".github/workflows/reusable-test.yml"
- ".github/workflows/reusable-upload-recipe.yml"
- ".clang-tidy"
- ".codecov.yml"
- "cfg/**"
- "cmake/**"
- "conan/**"
- "external/**"
@@ -37,9 +35,6 @@ on:
- "CMakeLists.txt"
- "conanfile.py"
- "conan.lock"
- "LICENSE.md"
- "package/**"
- "README.md"
# Run at 06:32 UTC on every day of the week from Monday through Friday. This
# will force all dependencies to be rebuilt, which is useful to verify that
@@ -100,7 +95,3 @@ jobs:
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
package:
needs: build-test
uses: ./.github/workflows/reusable-package.yml

View File

@@ -181,7 +181,7 @@ jobs:
- name: Build the binary
working-directory: ${{ env.BUILD_DIR }}
env:
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
BUILD_NPROC: ${{ runner.os == 'Linux' && '16' || steps.nproc.outputs.nproc }}
BUILD_TYPE: ${{ inputs.build_type }}
CMAKE_TARGET: ${{ inputs.cmake_target }}
run: |

View File

@@ -1,99 +0,0 @@
# Build Linux packages (DEB and RPM) from pre-built binary artifacts.
# Discovers which configurations to package from linux.json (os entries
# with "package": true) and fans out one job per entry. Today only
# linux/amd64 is emitted; the architecture is hardcoded both here
# (runner) and in generate.py.
name: Package
on:
workflow_call:
inputs:
pkg_release:
description: "Package release number. Increment when repackaging the same executable."
required: false
type: string
default: "1"
defaults:
run:
shell: bash
env:
BUILD_DIR: build
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.13
- name: Generate packaging matrix
id: generate
working-directory: .github/scripts/strategy-matrix
run: |
./generate.py --packaging --config=linux.json >> "${GITHUB_OUTPUT}"
generate-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: |
.github/actions/generate-version
src/libxrpl/protocol/BuildInfo.cpp
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package:
needs: [generate-matrix, generate-version]
if: ${{ github.event.repository.visibility == 'public' }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
name: "${{ matrix.artifact_name }}"
permissions:
contents: read
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: ${{ format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) }}
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download pre-built binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.artifact_name }}
path: ${{ env.BUILD_DIR }}
- name: Make binary executable
run: chmod +x "${BUILD_DIR}/xrpld"
- name: Build package
env:
PKG_VERSION: ${{ needs.generate-version.outputs.version }}
PKG_RELEASE: ${{ inputs.pkg_release }}
run: ./package/build_pkg.sh
- name: Upload package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }}
path: |
${{ env.BUILD_DIR }}/debbuild/*.deb
${{ env.BUILD_DIR }}/debbuild/*.ddeb
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.rpm
if-no-files-found: error

View File

@@ -427,19 +427,16 @@ install ccache --version 4.11.3 --allow-downgrade`.
Single-config generators:
```
cmake --build . --parallel N
cmake --build .
```
Multi-config generators:
```
cmake --build . --config Release --parallel N
cmake --build . --config Debug --parallel N
cmake --build . --config Release
cmake --build . --config Debug
```
Replace the `--parallel` parameter N with the desired number of parallel jobs. A common starting point is half of the number of available CPU
cores.
5. Test xrpld.
Single-config generators:

View File

@@ -134,7 +134,6 @@ endif()
include(XrplCore)
include(XrplProtocolAutogen)
include(XrplInstall)
include(XrplPackaging)
include(XrplValidatorKeys)
if(tests)

View File

@@ -28,7 +28,7 @@
# https://vl.ripple.com
# https://unl.xrplf.org
# http://127.0.0.1:8000
# file:///etc/xrpld/vl.txt
# file:///etc/opt/xrpld/vl.txt
#
# [validator_list_keys]
#

View File

@@ -1466,7 +1466,10 @@ admin = 127.0.0.1
protocol = http
[port_peer]
port = 2459
# Many servers still use the legacy port of 51235, so for backward-compatibility
# we maintain that port number here. However, for new servers we recommend
# changing this to the default port of 2459.
port = 51235
ip = 0.0.0.0
# alternatively, to accept connections on IPv4 + IPv6, use:
#ip = ::

View File

@@ -1,44 +0,0 @@
#[===================================================================[
Linux packaging support: 'package' target.
The packaging script (package/build_pkg.sh) installs to FHS-standard
paths (/usr/bin, /etc/xrpld, etc.) regardless of CMAKE_INSTALL_PREFIX,
so no prefix guard is needed here.
#]===================================================================]
if(NOT is_linux)
message(STATUS "Packaging not supported on non-Linux hosts")
return()
endif()
if(NOT DEFINED pkg_release)
set(pkg_release 1)
endif()
find_program(RPMBUILD_EXECUTABLE rpmbuild)
find_program(DPKG_BUILDPACKAGE_EXECUTABLE dpkg-buildpackage)
if(NOT (RPMBUILD_EXECUTABLE OR DPKG_BUILDPACKAGE_EXECUTABLE))
message(
STATUS
"Neither rpmbuild nor dpkg-buildpackage found; 'package' target not available"
)
return()
endif()
set(package_env
SRC_DIR=${CMAKE_SOURCE_DIR}
BUILD_DIR=${CMAKE_BINARY_DIR}
PKG_VERSION=${xrpld_version}
PKG_RELEASE=${pkg_release}
)
add_custom_target(
package
COMMAND
${CMAKE_COMMAND} -E env ${package_env}
${CMAKE_SOURCE_DIR}/package/build_pkg.sh
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS xrpld
COMMENT "Building Linux package (deb/rpm inferred from host tooling)"
VERBATIM
)

View File

@@ -99,15 +99,12 @@ words:
- desync
- desynced
- determ
- disablerepo
- distro
- doxyfile
- dxrpl
- enabled
- enablerepo
- endmacro
- exceptioned
- EXPECT_STREQ
- Falco
- fcontext
- finalizers
@@ -165,7 +162,6 @@ words:
- Merkle
- Metafuncton
- misprediction
- missingok
- mptbalance
- MPTDEX
- mptflags
@@ -197,9 +193,7 @@ words:
- NOLINT
- NOLINTNEXTLINE
- nonxrp
- noreplace
- noripple
- notifempty
- nudb
- nullptr
- nunl
@@ -219,7 +213,6 @@ words:
- preauthorize
- preauthorizes
- preclaim
- preun
- protobuf
- protos
- ptrs
@@ -254,14 +247,12 @@ words:
- sfields
- shamap
- shamapitem
- shlibs
- sidechain
- SIGGOOD
- sle
- sles
- soci
- socidb
- SRPMS
- sslws
- statsd
- STATSDCOLLECTOR
@@ -289,8 +280,8 @@ words:
- txn
- txns
- txs
- ubsan
- UBSAN
- ubsan
- umant
- unacquired
- unambiguity
@@ -327,6 +318,7 @@ words:
- xbridge
- xchain
- ximinez
- EXPECT_STREQ
- XMACRO
- xrpkuwait
- xrpl

View File

@@ -181,14 +181,14 @@ private:
beast::insight::Collector::ptr const& collector)
: hook(collector->makeHook(handler))
, size(collector->makeGauge(prefix, "size"))
, hitRate(collector->makeGauge(prefix, "hit_rate"))
, hit_rate(collector->makeGauge(prefix, "hit_rate"))
{
}
beast::insight::Hook hook;
beast::insight::Gauge size;
beast::insight::Gauge hitRate;
beast::insight::Gauge hit_rate;
std::size_t hits{0};
std::size_t misses{0};
@@ -197,16 +197,16 @@ private:
class KeyOnlyEntry
{
public:
clock_type::time_point lastAccess;
clock_type::time_point last_access;
explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess)
explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : last_access(lastAccess)
{
}
void
touch(clock_type::time_point const& now)
{
lastAccess = now;
last_access = now;
}
};
@@ -214,10 +214,10 @@ private:
{
public:
shared_weak_combo_pointer_type ptr;
clock_type::time_point lastAccess;
clock_type::time_point last_access;
ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr)
: ptr(ptr), lastAccess(lastAccess)
: ptr(ptr), last_access(lastAccess)
{
}
@@ -246,7 +246,7 @@ private:
void
touch(clock_type::time_point const& now)
{
lastAccess = now;
last_access = now;
}
};
@@ -286,13 +286,13 @@ private:
std::string name_;
// Desired number of cache entries (0 = ignore)
int const targetSize_;
int const target_size_;
// Desired maximum cache age
clock_type::duration const targetAge_;
clock_type::duration const target_age_;
// Number of items cached
int cacheCount_{0};
int cache_count_{0};
cache_type cache_; // Hold strong reference to recent objects
std::uint64_t hits_{0};
std::uint64_t misses_{0};

View File

@@ -34,8 +34,8 @@ inline TaggedCache<
, clock_(clock)
, stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector)
, name_(name)
, targetSize_(size)
, targetAge_(expiration)
, target_size_(size)
, target_age_(expiration)
{
}
@@ -86,7 +86,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
getCacheSize() const
{
std::scoped_lock const lock(mutex_);
return cacheCount_;
return cache_count_;
}
template <
@@ -139,7 +139,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{
std::scoped_lock const lock(mutex_);
cache_.clear();
cacheCount_ = 0;
cache_count_ = 0;
}
template <
@@ -157,7 +157,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{
std::scoped_lock const lock(mutex_);
cache_.clear();
cacheCount_ = 0;
cache_count_ = 0;
hits_ = 0;
misses_ = 0;
}
@@ -213,21 +213,21 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
{
std::scoped_lock const lock(mutex_);
if (targetSize_ == 0 || (static_cast<int>(cache_.size()) <= targetSize_))
if (target_size_ == 0 || (static_cast<int>(cache_.size()) <= target_size_))
{
whenExpire = now - targetAge_;
whenExpire = now - target_age_;
}
else
{
whenExpire = now - (targetAge_ * targetSize_ / cache_.size());
whenExpire = now - (target_age_ * target_size_ / cache_.size());
clock_type::duration const minimumAge(std::chrono::seconds(1));
if (whenExpire > (now - minimumAge))
whenExpire = now - minimumAge;
JLOG(journal_.trace())
<< name_ << " is growing fast " << cache_.size() << " of " << targetSize_
<< " aging at " << (now - whenExpire).count() << " of " << targetAge_.count();
<< name_ << " is growing fast " << cache_.size() << " of " << target_size_
<< " aging at " << (now - whenExpire).count() << " of " << target_age_.count();
}
std::vector<std::thread> workers;
@@ -242,7 +242,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
for (std::thread& worker : workers)
worker.join();
cacheCount_ -= allRemovals;
cache_count_ -= allRemovals;
}
// At this point allStuffToSweep will go out of scope outside the lock
// and decrement the reference count on each strong pointer.
@@ -280,7 +280,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached())
{
--cacheCount_;
--cache_count_;
entry.ptr.convertToWeak();
ret = true;
}
@@ -317,7 +317,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(clock_.now(), data));
++cacheCount_;
++cache_count_;
return false;
}
@@ -366,12 +366,12 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
data = cachedData;
}
++cacheCount_;
++cache_count_;
return true;
}
entry.ptr = data;
++cacheCount_;
++cache_count_;
return false;
}
@@ -477,7 +477,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
auto [it, inserted] = cache_.emplace(
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(now));
if (!inserted)
it->second.lastAccess = now;
it->second.last_access = now;
return inserted;
}
@@ -626,7 +626,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (entry.isCached())
{
// independent of cache size, so not counted as a hit
++cacheCount_;
++cache_count_;
entry.touch(clock_.now());
return entry.ptr.getStrong();
}
@@ -658,7 +658,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
if (total != 0)
hitRate = (hits_ * 100) / total;
}
stats_.hitRate.set(hitRate);
stats_.hit_rate.set(hitRate);
}
}
@@ -706,7 +706,7 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
++cit;
}
}
else if (cit->second.lastAccess <= whenExpire)
else if (cit->second.last_access <= whenExpire)
{
// strong, expired
++cacheRemovals;
@@ -773,12 +773,12 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
auto cit = partition.begin();
while (cit != partition.end())
{
if (cit->second.lastAccess > now)
if (cit->second.last_access > now)
{
cit->second.lastAccess = now;
cit->second.last_access = now;
++cit;
}
else if (cit->second.lastAccess <= whenExpire)
else if (cit->second.last_access <= whenExpire)
{
cit = partition.erase(cit);
}

View File

@@ -24,20 +24,20 @@ namespace xrpl {
template <class EF>
class ScopeExit
{
EF exitFunction_;
bool executeOnDestruction_{true};
EF exit_function_;
bool execute_on_destruction_{true};
public:
~ScopeExit()
{
if (executeOnDestruction_)
exitFunction_();
if (execute_on_destruction_)
exit_function_();
}
ScopeExit(ScopeExit&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, executeOnDestruction_{rhs.executeOnDestruction_}
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
{
rhs.release();
}
@@ -51,7 +51,7 @@ public:
std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, ScopeExit> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exitFunction_{std::forward<EFP>(f)}
: exit_function_{std::forward<EFP>(f)}
{
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
}
@@ -59,7 +59,7 @@ public:
void
release() noexcept
{
executeOnDestruction_ = false;
execute_on_destruction_ = false;
}
};
@@ -69,22 +69,22 @@ ScopeExit(EF) -> ScopeExit<EF>;
template <class EF>
class ScopeFail
{
EF exitFunction_;
bool executeOnDestruction_{true};
int uncaughtOnCreation_{std::uncaught_exceptions()};
EF exit_function_;
bool execute_on_destruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()};
public:
~ScopeFail()
{
if (executeOnDestruction_ && std::uncaught_exceptions() > uncaughtOnCreation_)
exitFunction_();
if (execute_on_destruction_ && std::uncaught_exceptions() > uncaught_on_creation_)
exit_function_();
}
ScopeFail(ScopeFail&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, executeOnDestruction_{rhs.executeOnDestruction_}
, uncaughtOnCreation_{rhs.uncaughtOnCreation_}
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_}
{
rhs.release();
}
@@ -98,7 +98,7 @@ public:
std::enable_if_t<
!std::is_same_v<std::remove_cv_t<EFP>, ScopeFail> &&
std::is_constructible_v<EF, EFP>>* = 0) noexcept
: exitFunction_{std::forward<EFP>(f)}
: exit_function_{std::forward<EFP>(f)}
{
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
}
@@ -106,7 +106,7 @@ public:
void
release() noexcept
{
executeOnDestruction_ = false;
execute_on_destruction_ = false;
}
};
@@ -116,22 +116,22 @@ ScopeFail(EF) -> ScopeFail<EF>;
template <class EF>
class ScopeSuccess
{
EF exitFunction_;
bool executeOnDestruction_{true};
int uncaughtOnCreation_{std::uncaught_exceptions()};
EF exit_function_;
bool execute_on_destruction_{true};
int uncaught_on_creation_{std::uncaught_exceptions()};
public:
~ScopeSuccess() noexcept(noexcept(exitFunction_()))
~ScopeSuccess() noexcept(noexcept(exit_function_()))
{
if (executeOnDestruction_ && std::uncaught_exceptions() <= uncaughtOnCreation_)
exitFunction_();
if (execute_on_destruction_ && std::uncaught_exceptions() <= uncaught_on_creation_)
exit_function_();
}
ScopeSuccess(ScopeSuccess&& rhs) noexcept(
std::is_nothrow_move_constructible_v<EF> || std::is_nothrow_copy_constructible_v<EF>)
: exitFunction_{std::forward<EF>(rhs.exitFunction_)}
, executeOnDestruction_{rhs.executeOnDestruction_}
, uncaughtOnCreation_{rhs.uncaughtOnCreation_}
: exit_function_{std::forward<EF>(rhs.exit_function_)}
, execute_on_destruction_{rhs.execute_on_destruction_}
, uncaught_on_creation_{rhs.uncaught_on_creation_}
{
rhs.release();
}
@@ -146,14 +146,14 @@ public:
!std::is_same_v<std::remove_cv_t<EFP>, ScopeSuccess> &&
std::is_constructible_v<EF, EFP>>* =
0) noexcept(std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>)
: exitFunction_{std::forward<EFP>(f)}
: exit_function_{std::forward<EFP>(f)}
{
}
void
release() noexcept
{
executeOnDestruction_ = false;
execute_on_destruction_ = false;
}
};

View File

@@ -77,8 +77,8 @@ private:
std::ostream& os_;
Results results_;
SuiteResults suiteResults_;
CaseResults caseResults_;
SuiteResults suite_results_;
CaseResults case_results_;
public:
Reporter(Reporter const&) = delete;
@@ -196,22 +196,22 @@ template <class Unused>
void
Reporter<Unused>::onSuiteBegin(SuiteInfo const& info)
{
suiteResults_ = SuiteResults{info.fullName()};
suite_results_ = SuiteResults{info.fullName()};
}
template <class Unused>
void
Reporter<Unused>::onSuiteEnd()
{
results_.add(suiteResults_);
results_.add(suite_results_);
}
template <class Unused>
void
Reporter<Unused>::onCaseBegin(std::string const& name)
{
caseResults_ = CaseResults(name);
os_ << suiteResults_.name << (caseResults_.name.empty() ? "" : (" " + caseResults_.name))
case_results_ = CaseResults(name);
os_ << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name))
<< std::endl;
}
@@ -219,23 +219,23 @@ template <class Unused>
void
Reporter<Unused>::onCaseEnd()
{
suiteResults_.add(caseResults_);
suite_results_.add(case_results_);
}
template <class Unused>
void
Reporter<Unused>::onPass()
{
++caseResults_.total;
++case_results_.total;
}
template <class Unused>
void
Reporter<Unused>::onFail(std::string const& reason)
{
++caseResults_.failed;
++caseResults_.total;
os_ << "#" << caseResults_.total << " failed" << (reason.empty() ? "" : ": ") << reason
++case_results_.failed;
++case_results_.total;
os_ << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ") << reason
<< std::endl;
}

View File

@@ -47,7 +47,7 @@ inline bool
JobQueue::Coro::post()
{
{
std::scoped_lock const lk(mutexRun_);
std::scoped_lock const lk(mutex_run_);
running_ = true;
}
@@ -58,7 +58,7 @@ JobQueue::Coro::post()
}
// The coroutine will not run. Clean up running_.
std::scoped_lock const lk(mutexRun_);
std::scoped_lock const lk(mutex_run_);
running_ = false;
cv_.notify_all();
return false;
@@ -68,7 +68,7 @@ inline void
JobQueue::Coro::resume()
{
{
std::scoped_lock const lk(mutexRun_);
std::scoped_lock const lk(mutex_run_);
running_ = true;
}
{
@@ -92,7 +92,7 @@ JobQueue::Coro::resume()
}
detail::getLocalValues().release();
detail::getLocalValues().reset(saved);
std::scoped_lock const lk(mutexRun_);
std::scoped_lock const lk(mutex_run_);
running_ = false;
cv_.notify_all();
}
@@ -127,7 +127,7 @@ JobQueue::Coro::expectEarlyExit()
inline void
JobQueue::Coro::join()
{
std::unique_lock<std::mutex> lk(mutexRun_);
std::unique_lock<std::mutex> lk(mutex_run_);
cv_.wait(lk, [this]() { return !running_; });
}

View File

@@ -127,7 +127,7 @@ private:
std::function<void()> job_;
std::shared_ptr<LoadEvent> loadEvent_;
std::string name_;
clock_type::time_point queueTime_;
clock_type::time_point queue_time_;
};
using JobCounter = ClosureCounter<void>;

View File

@@ -52,7 +52,7 @@ public:
std::string name_;
bool running_{false};
std::mutex mutex_;
std::mutex mutexRun_;
std::mutex mutex_run_;
std::condition_variable cv_;
boost::coroutines2::coroutine<void>::push_type* yield_{};
boost::coroutines2::coroutine<void>::pull_type coro_;
@@ -246,7 +246,7 @@ private:
// Statistics tracking
perf::PerfLog& perfLog_;
beast::insight::Collector::ptr collector_;
beast::insight::Gauge jobCount_;
beast::insight::Gauge job_count_;
beast::insight::Hook hook_;
std::condition_variable cv_;

View File

@@ -161,7 +161,7 @@ public:
* While the JSON spec doesn't explicitly disallow this, you should avoid
* calling this method twice with the same tag for the same object.
*
* If CHECK_JSON_WRITER is defined, this function throws an exception if
* If CHECK_JSON_WRITER is defined, this function throws an exception if if
* the tag you use has already been used in this object.
*/
template <typename Type>

View File

@@ -9,7 +9,7 @@ class BookDirs
private:
ReadView const* view_ = nullptr;
uint256 const root_;
uint256 const nextQuality_;
uint256 const next_quality_;
uint256 const key_;
std::shared_ptr<SLE const> sle_ = nullptr;
unsigned int entry_ = 0;
@@ -67,15 +67,15 @@ private:
friend class BookDirs;
const_iterator(ReadView const& view, uint256 const& root, uint256 const& dirKey)
: view_(&view), root_(root), key_(dirKey), curKey_(dirKey)
: view_(&view), root_(root), key_(dirKey), cur_key_(dirKey)
{
}
ReadView const* view_ = nullptr;
uint256 root_;
uint256 nextQuality_;
uint256 next_quality_;
uint256 key_;
uint256 curKey_;
uint256 cur_key_;
std::shared_ptr<SLE const> sle_;
unsigned int entry_ = 0;
uint256 index_;

View File

@@ -76,7 +76,7 @@ private:
// monotonic_resource_ must outlive `items_`. Make a pointer so it may be
// easily moved.
std::unique_ptr<boost::container::pmr::monotonic_buffer_resource> monotonicResource_;
std::unique_ptr<boost::container::pmr::monotonic_buffer_resource> monotonic_resource_;
txs_map txs_;
Rules rules_;
LedgerHeader header_;

View File

@@ -19,6 +19,11 @@
namespace xrpl {
// Forward declarations for SLE wrappers
template <typename ViewT>
class AccountRoot;
using RAccountRoot = AccountRoot<ReadView>;
enum class SkipEntry : bool { No = false, Yes };
//------------------------------------------------------------------------------
@@ -57,7 +62,7 @@ isVaultPseudoAccountFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptShare,
std::uint8_t depth);
int depth);
[[nodiscard]] bool
isLPTokenFrozen(
@@ -157,7 +162,7 @@ canWithdraw(
ReadView const& view,
AccountID const& from,
AccountID const& to,
SLE::const_ref toSle,
RAccountRoot const& toWrapped,
STAmount const& amount,
bool hasDestinationTag);

View File

@@ -22,14 +22,14 @@ public:
static constexpr size_t kInitialBufferSize = kilobytes(256);
RawStateTable()
: monotonicResource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize)}
, items_{monotonicResource_.get()} {};
, items_{monotonic_resource_.get()} {};
RawStateTable(RawStateTable const& rhs)
: monotonicResource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize)}
, items_{rhs.items_, monotonicResource_.get()}
, items_{rhs.items_, monotonic_resource_.get()}
, dropsDestroyed_{rhs.dropsDestroyed_} {};
RawStateTable(RawStateTable&&) = default;
@@ -101,7 +101,7 @@ private:
boost::container::pmr::polymorphic_allocator<std::pair<key_type const, SleAction>>>;
// monotonic_resource_ must outlive `items_`. Make a pointer so it may be
// easily moved.
std::unique_ptr<boost::container::pmr::monotonic_buffer_resource> monotonicResource_;
std::unique_ptr<boost::container::pmr::monotonic_buffer_resource> monotonic_resource_;
items_t items_;
XRPAmount dropsDestroyed_{0};

View File

@@ -2,8 +2,10 @@
#include <xrpl/basics/Expected.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/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -15,46 +17,160 @@
namespace xrpl {
/** Check if the issuer has the global freeze flag set.
@param issuer The account to check
@return true if the account has global freeze set
*/
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
// Calculate liquid XRP balance for an account.
// This function may be used to calculate the amount of XRP that
// the holder is able to freely spend. It subtracts reserve requirements.
//
// ownerCountAdj adjusts the owner count in case the caller calculates
// before ledger entries are added or removed. Positive to add, negative
// to subtract.
//
// @param ownerCountAdj positive to add to count, negative to reduce count.
[[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
/** Adjust the owner count up or down. */
void
adjustOwnerCount(
ApplyView& view,
std::shared_ptr<SLE> const& sle,
std::int32_t amount,
beast::Journal j);
/** Returns IOU issuer transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuer The IOU issuer
/**
* View-parameterized wrapper for AccountRoot ledger entries.
*
* AccountRoot<ReadView> — read-only access to account data
* AccountRoot<ApplyView> — read-write access, with insert/update/erase
* and domain-specific write methods
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, AccountID const& issuer);
template <typename ViewT>
class AccountRoot : public SLEBase<ViewT>
{
static constexpr bool kIsWritable = SLEBase<ViewT>::kIsWritable;
AccountID const id_;
public:
/** Constructor for read-only context */
AccountRoot(
AccountID const& id,
ReadView const& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: SLEBase<ViewT>(view.read(keylet::account(id)), view, j), id_(id)
{
}
/** Constructor for writable context */
AccountRoot(
AccountID const& id,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: SLEBase<ViewT>(keylet::account(id), view, j), id_(id)
{
}
/** Converting constructor: writable → read-only. */
template <WritableView OtherViewT>
AccountRoot(AccountRoot<OtherViewT> const& other)
requires(!kIsWritable)
: SLEBase<ViewT>(other), id_(other.id())
{
}
/** Create an AccountRoot backed by a brand-new SLE.
*/
[[nodiscard]] static AccountRoot
makeNew(
AccountID const& id,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
{
return AccountRoot(id, view, j, std::make_shared<SLE>(keylet::account(id)));
}
[[nodiscard]] AccountID const&
id() const
{
return id_;
}
// --- Read-only domain methods (available on both specializations) ---
/** Check if the issuer has the global freeze flag set.
@return true if the account has global freeze set
*/
[[nodiscard]] bool
isGlobalFrozen() const;
/** Returns IOU issuer transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
*/
[[nodiscard]] Rate
transferRate() const;
// Calculate liquid XRP balance for an account.
// This function may be used to calculate the amount of XRP that
// the holder is able to freely spend. It subtracts reserve requirements.
//
// ownerCountAdj adjusts the owner count in case the caller calculates
// before ledger entries are added or removed. Positive to add, negative
// to subtract.
//
// @param ownerCountAdj positive to add to count, negative to reduce count.
[[nodiscard]] XRPAmount
xrpLiquid(std::int32_t ownerCountAdj) const;
/** Checks the destination and tag.
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(bool hasDestinationTag) const;
/** Returns true if and only if sleAcct is a pseudo-account or specific
pseudo-accounts in pseudoFieldFilter.
Returns false if sleAcct is:
- NOT a pseudo-account OR
- NOT a ltACCOUNT_ROOT OR
- null pointer
*/
[[nodiscard]] bool
isPseudoAccount(std::set<SField const*> const& pseudoFieldFilter = {}) const;
[[nodiscard]] bool
operator==(AccountRoot const& other) const
{
return id_ == other.id_;
}
[[nodiscard]] bool
operator==(AccountID const& other) const
{
return id_ == other;
}
// --- Write-only domain methods (compile-time gated) ---
/** Adjust the owner count up or down. */
void
adjustOwnerCount(std::int32_t amount)
requires kIsWritable;
private:
// Private constructor only used by `makeNew`
AccountRoot(AccountID const& id, ApplyView& view, beast::Journal j, std::shared_ptr<SLE> sle)
requires kIsWritable
: SLEBase<ViewT>(std::move(sle), view, j), id_(id)
{
this->insert();
}
};
// CTAD deduction guide — bare AccountRoot(id, view) always deduces read-only.
// For writable access, use WAccountRoot(id, applyView) explicitly.
AccountRoot(AccountID const&, ReadView const&) -> AccountRoot<ReadView>;
AccountRoot(AccountID const&, ReadView const&, beast::Journal) -> AccountRoot<ReadView>;
// Backward-compatible aliases
using RAccountRoot = AccountRoot<ReadView>;
using WAccountRoot = AccountRoot<ApplyView>;
// Explicit instantiation declarations (definitions in .cpp)
extern template class AccountRoot<ReadView>;
extern template class AccountRoot<ApplyView>;
/** Generate a pseudo-account address from a pseudo owner key.
@param pseudoOwnerKey The key to generate the address from
@return The generated account ID
*/
AccountID
[[nodiscard]] AccountID
pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account
@@ -67,29 +183,6 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
/** Returns true if and only if sleAcct is a pseudo-account or specific
pseudo-accounts in pseudoFieldFilter.
Returns false if sleAcct is:
- NOT a pseudo-account OR
- NOT a ltACCOUNT_ROOT OR
- null pointer
*/
[[nodiscard]] bool
isPseudoAccount(
std::shared_ptr<SLE const> sleAcct,
std::set<SField const*> const& pseudoFieldFilter = {});
/** Convenience overload that reads the account from the view. */
[[nodiscard]] inline bool
isPseudoAccount(
ReadView const& view,
AccountID const& accountId,
std::set<SField const*> const& pseudoFieldFilter = {})
{
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
}
/**
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
*
@@ -101,12 +194,4 @@ isPseudoAccount(
[[nodiscard]] Expected<std::shared_ptr<SLE>, TER>
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField);
/** Checks the destination and tag.
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
} // namespace xrpl

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STTx.h>
@@ -69,8 +70,7 @@ verifyDepositPreauth(
STTx const& tx,
ApplyView& view,
AccountID const& src,
AccountID const& dst,
std::shared_ptr<SLE const> const& sleDst,
RAccountRoot const& dst,
beast::Journal j);
} // namespace xrpl

View File

@@ -11,6 +11,8 @@
#include <xrpl/protocol/MPTAmount.h>
#include <xrpl/protocol/Rate.h>
#include <variant>
namespace xrpl {
template <ValidIssueType T>
@@ -18,7 +20,7 @@ TER
escrowUnlockApplyHelper(
ApplyView& view,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
std::variant<std::shared_ptr<SLE>, WAccountRoot> dest,
STAmount const& xrpBalance,
STAmount const& amount,
AccountID const& issuer,
@@ -32,7 +34,7 @@ inline TER
escrowUnlockApplyHelper<Issue>(
ApplyView& view,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
std::variant<std::shared_ptr<SLE>, WAccountRoot> dest,
STAmount const& xrpBalance,
STAmount const& amount,
AccountID const& issuer,
@@ -55,8 +57,14 @@ escrowUnlockApplyHelper<Issue>(
if (!view.exists(trustLineKey) && createAsset)
{
// For backwards compatibility: if dest is not WAccountRoot, return error
if (!std::holds_alternative<WAccountRoot>(dest))
return tefEXCEPTION;
auto& wrappedDest = std::get<WAccountRoot>(dest);
// Can the account cover the trust line's reserve?
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
if (std::uint32_t const ownerCount = {wrappedDest->at(sfOwnerCount)};
xrpBalance < view.fees().accountReserve(ownerCount + 1))
{
JLOG(journal.trace()) << "Trust line does not exist. "
@@ -69,28 +77,28 @@ escrowUnlockApplyHelper<Issue>(
STAmount initialBalance(issue);
initialBalance.get<Issue>().account = noAccount();
if (TER const ter = trustCreate(
view, // payment sandbox
recvLow, // is dest low?
issuer, // source
receiver, // destination
trustLineKey.key, // ledger index
sleDest, // Account to add to
false, // authorize account
!sleDest->isFlag(lsfDefaultRipple), //
false, // freeze trust line
false, // deep freeze trust line
initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero
0, // quality in
0, // quality out
journal); // journal
if (TER const ter = WIOUIssuance(view, issue, journal)
.trustCreate(
recvLow, // is dest low?
issuer, // source
receiver, // destination
trustLineKey.key, // ledger index
wrappedDest, // Account to add to
false, // authorize account
!wrappedDest->isFlag(lsfDefaultRipple),
false, // freeze trust line
false, // deep freeze trust line
initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero
0, // quality in
0, // quality out
journal); // journal
!isTesSuccess(ter))
{
return ter; // LCOV_EXCL_LINE
}
view.update(sleDest);
wrappedDest.update();
}
if (!view.exists(trustLineKey) && !receiverIssuer)
@@ -162,7 +170,7 @@ inline TER
escrowUnlockApplyHelper<MPTIssue>(
ApplyView& view,
Rate lockedRate,
std::shared_ptr<SLE> const& sleDest,
std::variant<std::shared_ptr<SLE>, WAccountRoot> dest,
STAmount const& xrpBalance,
STAmount const& amount,
AccountID const& issuer,
@@ -178,19 +186,26 @@ escrowUnlockApplyHelper<MPTIssue>(
auto const issuanceKey = keylet::mptIssuance(mptID);
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer)
{
if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
// For backwards compatibility: if dest is not WAccountRoot, return error
if (!std::holds_alternative<WAccountRoot>(dest))
return tefEXCEPTION;
auto& wrappedDest = std::get<WAccountRoot>(dest);
if (std::uint32_t const ownerCount = {wrappedDest->at(sfOwnerCount)};
xrpBalance < view.fees().accountReserve(ownerCount + 1))
{
return tecINSUFFICIENT_RESERVE;
}
if (auto const ter = createMPToken(view, mptID, receiver, 0); !isTesSuccess(ter))
if (auto const ter = WMPTokenIssuance(view, mptID, journal).createMPToken(receiver, 0);
!isTesSuccess(ter))
{
return ter; // LCOV_EXCL_LINE
}
// update owner count.
adjustOwnerCount(view, sleDest, 1, journal);
wrappedDest.adjustOwnerCount(1);
}
if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer)
@@ -218,13 +233,13 @@ escrowUnlockApplyHelper<MPTIssue>(
// compute balance to transfer
finalAmt = amount.value() - xferFee;
}
return unlockEscrowMPT(
view,
sender,
receiver,
finalAmt,
view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
journal);
return WMPTokenIssuance(view, amount.get<MPTIssue>(), journal)
.unlockEscrow(
sender,
receiver,
finalAmt,
view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
journal);
}
} // namespace xrpl

View File

@@ -4,38 +4,8 @@
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/st.h>
#include <string_view>
namespace xrpl {
/**
* Broker cover preclaim precision guard (fixCleanup3_2_0).
*
* Prevents a "silent sub-ULP no-op" where a deposit, withdrawal, or clawback
* amount is so small that it rounds to zero at `sfCoverAvailable`'s scale.
* Without this guard, both the pseudo trust-line and `sfCoverAvailable` would
* identically absorb the rounded zero, resulting in a successful transaction
* (tesSUCCESS) where no funds actually moved.
*
* @param view Read view (rules used for amendment gating).
* @param sleBroker The loan broker SLE (read-only).
* @param vaultAsset The underlying vault asset (the broker's cover asset).
* @param amount The effective subtraction/addition amount.
* @param j Journal for logging.
* @param logPrefix Transactor name for log diagnostics.
*
* @return `tecPRECISION_LOSS` if the request rounds to zero at cover scale.
* `tesSUCCESS` if the amendment is disabled or the request is safely supra-ULP.
*/
[[nodiscard]] TER
canApplyToBrokerCover(
ReadView const& view,
SLE::const_ref sleBroker,
Asset const& vaultAsset,
STAmount const& amount,
beast::Journal j,
std::string_view logPrefix);
// Lending protocol has dependencies, so capture them here.
bool
checkLendingProtocolDependencies(Rules const& rules, STTx const& tx);
@@ -203,21 +173,6 @@ getAssetsTotalScale(SLE::const_ref vaultSle)
return scale(vaultSle->at(sfAssetsTotal), vaultSle->at(sfAsset));
}
// Compute the minimum required broker cover, rounded consistently.
// DebtTotal is a broker-level aggregate maintained at vault scale, so the
// rounding must also use vault scale — never an individual loan's scale.
inline Number
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::const_ref vaultSle)
{
XRPL_ASSERT(
vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle");
NumberRoundModeGuard const mg(Number::RoundingMode::Upward);
return roundToAsset(
vaultSle->at(sfAsset),
tenthBipsOfValue(debtTotal, coverRateMinimum),
getAssetsTotalScale(vaultSle));
}
TER
checkLoanGuards(
Asset const& vaultAsset,
@@ -532,4 +487,15 @@ loanMakePayment(
LoanPaymentType const paymentType,
beast::Journal j);
// LoanBroker-specific `adjustOwnerCount` function (temporary, while the Wrapped classes are WIP)
// Assert will check the type, so that we ensure it's not used by anything else
// Order of parameters is different from the old `adjustOwnerCount` function to avoid anything
// accidentally calling this with the wrong type.
void
adjustOwnerCount(
std::shared_ptr<SLE> const& sle,
ApplyView& view,
std::int32_t amount,
beast::Journal j);
} // namespace xrpl

View File

@@ -3,239 +3,303 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <initializer_list>
#include <memory>
#include <optional>
namespace xrpl {
//------------------------------------------------------------------------------
//
// Freeze checking (MPT-specific)
// MPTokenIssuance ledger entry wrapper (view-parameterized)
//
//------------------------------------------------------------------------------
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] bool
isFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
std::uint8_t depth = 0);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
MPTIssue const& mptIssue,
std::uint8_t depth = 0);
//------------------------------------------------------------------------------
//
// Transfer rate (MPT-specific)
//
//------------------------------------------------------------------------------
/** Returns MPT transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, MPTID const& issuanceID);
//------------------------------------------------------------------------------
//
// Holding checks (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
canAddHolding(ReadView const& view, MPTIssue const& mptIssue);
//------------------------------------------------------------------------------
//
// Authorization (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
authorizeMPToken(
ApplyView& view,
XRPAmount const& priorBalance,
MPTID const& mptIssuanceID,
AccountID const& account,
beast::Journal journal,
std::uint32_t flags = 0,
std::optional<AccountID> holderID = std::nullopt);
/** Check if the account lacks required authorization for MPT.
/**
* View-parameterized wrapper for MPTokenIssuance ledger entries.
*
* requireAuth check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
* WeakAuth intentionally allows missing MPTokens under MPToken V2.
* MPTokenIssuance<ReadView> — read-only access to issuance data
* MPTokenIssuance<ApplyView> — read-write access, with insert/update/erase
* and domain-specific write methods
*
* Carries the MPTID and MPTIssue alongside the SLE so callers don't need to
* thread them through every method call.
*/
template <typename ViewT>
class MPTokenIssuance : public SLEBase<ViewT>, public TokenBase<ViewT>
{
static constexpr bool kIsWritable = SLEBase<ViewT>::kIsWritable;
MPTID const mptID_;
MPTIssue const mptIssue_;
public:
/** Constructor for read-only context (MPTIssue) */
MPTokenIssuance(
ReadView const& view,
MPTIssue const& mptIssue,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: SLEBase<ViewT>(view.read(keylet::mptIssuance(mptIssue.getMptID())), view, j)
, mptID_(mptIssue.getMptID())
, mptIssue_(mptIssue)
{
}
/** Constructor for read-only context (MPTID) */
MPTokenIssuance(
ReadView const& view,
MPTID const& mptID,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: SLEBase<ViewT>(view.read(keylet::mptIssuance(mptID)), view, j)
, mptID_(mptID)
, mptIssue_(MPTIssue(mptID))
{
}
/** Constructor for writable context (MPTIssue) */
MPTokenIssuance(
ApplyView& view,
MPTIssue const& mptIssue,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: SLEBase<ViewT>(keylet::mptIssuance(mptIssue.getMptID()), view, j)
, mptID_(mptIssue.getMptID())
, mptIssue_(mptIssue)
{
}
/** Constructor for writable context (MPTID) */
MPTokenIssuance(
ApplyView& view,
MPTID const& mptID,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: SLEBase<ViewT>(keylet::mptIssuance(mptID), view, j)
, mptID_(mptID)
, mptIssue_(MPTIssue(mptID))
{
}
/** Converting constructor: writable → read-only. */
template <WritableView OtherViewT>
MPTokenIssuance(MPTokenIssuance<OtherViewT> const& other)
requires(!kIsWritable)
: SLEBase<ViewT>(other), mptID_(other.getMptID()), mptIssue_(other.getMptIssue())
{
}
/** Create an MPTokenIssuance backed by a brand-new SLE (already inserted into
* the view).
*/
[[nodiscard]] static MPTokenIssuance
makeNew(
MPTID const& mptID,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
{
return MPTokenIssuance(mptID, view, j, std::make_shared<SLE>(keylet::mptIssuance(mptID)));
}
[[nodiscard]] static MPTokenIssuance
makeNew(
std::uint32_t const seq,
AccountID const& issuer,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
{
auto const mptID = makeMptID(seq, issuer);
return MPTokenIssuance(mptID, view, j, std::make_shared<SLE>(keylet::mptIssuance(mptID)));
}
// --- Inline accessors ---
[[nodiscard]] MPTID const&
getMptID() const
{
return mptID_;
}
[[nodiscard]] MPTIssue const&
getMptIssue() const
{
return mptIssue_;
}
[[nodiscard]] AccountID const&
getIssuer() const
{
return mptIssue_.getIssuer();
}
// --- Read-only domain methods (available on both specializations) ---
[[nodiscard]] bool
isGlobalFrozen() const override;
[[nodiscard]] bool
isIndividualFrozen(AccountID const& account) const override;
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to kMaxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] bool
isFrozen(AccountID const& account, int depth = 0) const override;
[[nodiscard]] bool
isAnyFrozen(std::initializer_list<AccountID> const& accounts, int depth = 0) const;
/** Returns MPT transfer fee as Rate. */
[[nodiscard]] Rate
transferRate() const override;
[[nodiscard]] TER
canAddHolding() const;
/** Check if the account lacks required authorization for MPT. */
[[nodiscard]] TER
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
const override;
/** Check if `from` is allowed to send MPT to `to`. */
[[nodiscard]] TER
canTransfer(AccountID const& from, AccountID const& to) const override;
/** Check if the token requires authorization for holders. */
[[nodiscard]] bool
requiresAuth() const override;
/** Check if the token issuer has enabled clawback capability. */
[[nodiscard]] bool
canClawback() const override;
// --- Write-only domain methods (compile-time gated) ---
[[nodiscard]] TER
addEmptyHolding(AccountID const& accountID, XRPAmount priorBalance, beast::Journal journal)
requires kIsWritable;
[[nodiscard]] TER
removeEmptyHolding(AccountID const& accountID, beast::Journal journal)
requires kIsWritable;
[[nodiscard]] TER
authorizeMPToken(
XRPAmount const& priorBalance,
AccountID const& account,
beast::Journal journal,
std::uint32_t flags = 0,
std::optional<AccountID> holderID = std::nullopt)
requires kIsWritable;
/** Enforce account has MPToken to match its authorization.
*
* Called from doApply - it will check for expired (and delete if found any)
* credentials matching DomainID set in MPTokenIssuance. Must be called if
* requireAuth(...) returned tesSUCCESS or tecEXPIRED in preclaim.
*/
[[nodiscard]] TER
enforceMPTokenAuthorization(
AccountID const& account,
XRPAmount const& priorBalance,
beast::Journal j)
requires kIsWritable;
// --- Amount accessors ---
/** Maximum amount allowed for this issuance (sfMaximumAmount or default). */
[[nodiscard]] std::int64_t
maxAmount() const;
/** Available amount = maxAmount - OutstandingAmount.
* Throws if the issuance SLE does not exist.
*/
[[nodiscard]] std::int64_t
availableAmount() const;
/** Funds available for the issuer to sell in an issuer-owned offer. */
[[nodiscard]] STAmount
issuerFundsToSelfIssue() const;
// --- Holder MPToken management (writable) ---
/** Create a holder-side MPToken for `account` under this issuance. */
[[nodiscard]] TER
createMPToken(AccountID const& account, std::uint32_t flags)
requires kIsWritable;
/** Ensure `holder` has an MPToken under this issuance, creating one if needed. */
[[nodiscard]] TER
checkCreateMPT(AccountID const& holder, beast::Journal j)
requires kIsWritable;
/** Notify the view that the issuer self-debited `amount`. */
void
issuerSelfDebitHook(std::uint64_t amount)
requires kIsWritable;
/** Lock MPT for escrow from `sender`. The amount must match this issuance. */
[[nodiscard]] TER
lockEscrow(AccountID const& sender, STAmount const& amount, beast::Journal j)
requires kIsWritable;
/** Unlock previously-escrowed MPT, transferring `netAmount` to `receiver`,
* with `grossAmount - netAmount` accounted as a transfer fee against
* OutstandingAmount.
*/
[[nodiscard]] TER
unlockEscrow(
AccountID const& sender,
AccountID const& receiver,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j)
requires kIsWritable;
private:
// Private constructor only used by `makeNew`.
MPTokenIssuance(MPTID const& mptID, ApplyView& view, beast::Journal j, std::shared_ptr<SLE> sle)
requires kIsWritable
: SLEBase<ViewT>(std::move(sle), view, j), mptID_(mptID), mptIssue_(MPTIssue(mptID))
{
this->insert();
}
};
// CTAD deduction guides — bare MPTokenIssuance(view, ...) always deduces read-only.
// For writable access, use WMPTokenIssuance(view, ...) explicitly.
MPTokenIssuance(ReadView const&, MPTIssue const&) -> MPTokenIssuance<ReadView>;
MPTokenIssuance(ReadView const&, MPTIssue const&, beast::Journal) -> MPTokenIssuance<ReadView>;
MPTokenIssuance(ReadView const&, MPTID const&) -> MPTokenIssuance<ReadView>;
MPTokenIssuance(ReadView const&, MPTID const&, beast::Journal) -> MPTokenIssuance<ReadView>;
// Backward-compatible aliases
using RMPTokenIssuance = MPTokenIssuance<ReadView>;
using WMPTokenIssuance = MPTokenIssuance<ApplyView>;
// Explicit instantiation declarations (definitions in .cpp)
extern template class MPTokenIssuance<ReadView>;
extern template class MPTokenIssuance<ApplyView>;
/** Check if Asset can be traded on DEX. return tecNO_PERMISSION
* if it doesn't and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& account,
AuthType authType = AuthType::Legacy,
std::uint8_t depth = 0);
/** Enforce account has MPToken to match its authorization.
*
* Called from doApply - it will check for expired (and delete if found any)
* credentials matching DomainID set in MPTokenIssuance. Must be called if
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim.
*/
[[nodiscard]] TER
enforceMPTokenAuthorization(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance,
beast::Journal j);
/** Resolve the underlying asset of a vault share.
*
* Reads sfReferenceHolding from @p sleShareIssuance to determine which
* asset the vault wraps. @p sleHolding must be the SLE that
* sfReferenceHolding points to — either an ltMPTOKEN (returns its
* MPTIssue) or an ltRIPPLE_STATE (returns its low/high Issue).
*
* @pre Both SLEs must exist and @p sleHolding must be of type ltMPTOKEN
* or ltRIPPLE_STATE. Passing any other type is undefined behaviour.
* @param sleShareIssuance MPTokenIssuance SLE for the vault share token.
* @param sleHolding SLE referenced by sfReferenceHolding.
* @return The underlying Asset (MPTIssue or Issue).
*/
[[nodiscard]] Asset
assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding);
/** Check whether @p to may receive the given MPT from @p from.
*
* The check passes when any of the following is true:
* - @p waive is WaiveMPTCanTransfer::Yes (recovery-path exemption), or
* - @p from or @p to is the issuer, or
* - lsfMPTCanTransfer is set on the MPTokenIssuance.
*
* For vault shares (MPTokenIssuances that carry sfReferenceHolding) the
* check recurses into the underlying asset's transferability. This
* recursion is defensive; vault-of-vault-shares is rejected at vault
* creation, so in practice depth never exceeds 1.
*
* @param view Ledger state to read from.
* @param mptIssue The MPT issuance being transferred.
* @param from Sending account.
* @param to Receiving account.
* @param waive WaiveMPTCanTransfer::Yes skips the lsfMPTCanTransfer
* check. Use for recovery paths (e.g. unwinding SAV or
* Lending Protocol positions after an issuer revokes
* transferability).
* @param depth Recursion depth; bounded at kMaxAssetCheckDepth.
* @return tesSUCCESS if the transfer is allowed, tecNO_AUTH otherwise.
*/
[[nodiscard]] TER
canTransfer(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& from,
AccountID const& to,
WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No,
std::uint8_t depth = 0);
/** Check whether @p asset may be traded on the DEX.
*
* For IOU assets the check delegates to the existing offer/AMM freeze
* logic. For MPT assets it checks lsfMPTCanTrade on the MPTokenIssuance.
* Vault shares recurse into the underlying asset's tradability via
* sfReferenceHolding; depth is bounded at kMaxAssetCheckDepth.
*
* @param view Ledger state to read from.
* @param asset The asset to check.
* @param depth Recursion depth; bounded at kMaxAssetCheckDepth.
* @return tesSUCCESS if trading is allowed, tecNO_PERMISSION otherwise.
*/
[[nodiscard]] TER
canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth = 0);
/** Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue.
*/
[[nodiscard]] TER
canMPTTradeAndTransfer(
ReadView const& v,
Asset const& asset,
AccountID const& from,
AccountID const& to);
//------------------------------------------------------------------------------
//
// Empty holding operations (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
MPTIssue const& mptIssue,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
MPTIssue const& mptIssue,
beast::Journal journal);
//------------------------------------------------------------------------------
//
// Escrow operations (MPT-specific)
//
//------------------------------------------------------------------------------
TER
lockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
STAmount const& saAmount,
beast::Journal j);
TER
unlockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
AccountID const& uGranteeID,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j);
TER
createMPToken(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
std::uint32_t const flags);
TER
checkCreateMPT(
xrpl::ApplyView& view,
xrpl::MPTIssue const& mptIssue,
xrpl::AccountID const& holder,
beast::Journal j);
canTrade(ReadView const& view, Asset const& asset);
//------------------------------------------------------------------------------
//
@@ -252,9 +316,6 @@ maxMPTAmount(SLE const& sleIssuance);
std::int64_t
availableMPTAmount(SLE const& sleIssuance);
std::int64_t
availableMPTAmount(ReadView const& view, MPTID const& mptID);
/** Checks for two types of OutstandingAmount overflow during a send operation.
* 1. **Direct directSendNoFee (Overflow: No):** A true overflow check when
* `OutstandingAmount > MaximumAmount`. This threshold is used for direct
@@ -270,20 +331,27 @@ isMPTOverflow(
std::int64_t maximumAmount,
AllowMPTOverflow allowOverflow);
/**
* Determine funds available for an issuer to sell in an issuer owned offer.
* Issuing step, which could be either MPTEndPointStep last step or BookStep's
* TakerPays may overflow OutstandingAmount. Redeeming step, in BookStep's
* TakerGets redeems the offer's owner funds, essentially balancing out
* the overflow, unless the offer's owner is the issuer.
/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior
* call to view.peek().
*/
[[nodiscard]] STAmount
issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue);
[[nodiscard]] TER
deleteAMMMPToken(
ApplyView& view,
std::shared_ptr<SLE> sleMPT,
AccountID const& ammAccountID,
beast::Journal j);
/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer.
* See ApplyView::issuerSelfDebitHookMPT().
//------------------------------------------------------------------------------
//
// MPT DEX
//
//------------------------------------------------------------------------------
/* Return true if a transaction is allowed for the specified MPT/account. The
* function checks MPTokenIssuance and MPToken objects flags to determine if the
* transaction is allowed.
*/
void
issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount);
TER
checkMPTTxAllowed(ReadView const& v, TxType tx, Asset const& asset, AccountID const& accountID);
} // namespace xrpl

View File

@@ -0,0 +1,53 @@
# Ledger Entry Helpers (`helpers/`)
## Overview
This folder contains helper classes and free functions for working with **Serialized Ledger Entries (SLEs)**. Its centerpiece is `SLEBase.h`, which defines the template class `SLEBase<ViewT>` — a type-safe, context-aware wrapper around the raw `std::shared_ptr<SLE>` used throughout the rest of the codebase.
## The Problem: Untyped SLE Access
Historically, ledger entries are passed around as bare `std::shared_ptr<SLE>` (or `std::shared_ptr<SLE const>`). This has several drawbacks:
1. **No compile-time entry-type safety.** Any code holding an `std::shared_ptr<SLE>` can read or write _any_ field on _any_ ledger entry type. Nothing prevents you from calling `sle->getFieldU32(sfOwnerCount)` on an Offer SLE, even though Offers don't have that field.
2. **No read/write distinction.** A function that only needs to _read_ an entry still receives a mutable `std::shared_ptr<SLE>`, making it easy to accidentally mutate state. Conversely, a function that _must_ write has no way to express that requirement in its signature.
3. **No association with the view.** The SLE and the `ReadView` / `ApplyView` it came from travel as separate arguments, so callers must manually keep them in sync and remember to call `view.update(sle)` after mutations.
## The Solution: `SLEBase.h`
`SLEBase.h` introduces a single template class `SLEBase<ViewT>` that pairs an SLE with its view context and enforces read/write semantics at compile time via `requires` clauses.
**`SLEBase<ReadView>`** holds a `std::shared_ptr<SLE const>` and a `ReadView const&`. Write-only members are excluded at compile time.
holds a mutable `std::shared_ptr<SLE>`, an `ApplyView&`, and a `Keylet`. It exposes `insert()`, `update()`, `erase()`, and `newSLE()` to keep the SLE and its view in sync automatically.
A converting constructor allows implicit conversion from `SLEBase<ApplyView>` to `SLEBase<ReadView>`, so functions taking a read-only wrapper can accept a writable one without a cast.
### Template Pattern
Each entry type is a single template class parameterized on the view type:
```
AccountRoot<ReadView> — read-only: RAccountRoot
AccountRoot<ApplyView> — writable: WAccountRoot
```
Both specializations share all domain read methods. Write methods on `WAccountRoot` are gated with `requires is_writable` so they are unavailable on `RAccountRoot` at compile time. The `R`/`W` prefix aliases (`RAccountRoot`, `WAccountRoot`) are provided for convenience and backward compatibility.
## Files in This Directory
| File | Description |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SLEBase.h` | Template base class `SLEBase<ViewT>` for read-only and writable SLE wrappers |
| `AccountRootHelpers.h` | `AccountRoot<ViewT>` wrapper (`RAccountRoot`, `WAccountRoot`) and free functions for pseudo-accounts |
| `CredentialHelpers.h` | Free functions for Credential ledger entries |
| `DirectoryHelpers.h` | Free functions for directory traversal (`dirFirst`, `dirNext`, `forEachItem`, etc.) |
| `MPTokenHelpers.h` | Free functions for MPToken ledger entries and MPTokenIssuance<ViewT> wrapper (`RMPTokenIssuance`, `WMPTokenIssuance`) functions for MPTokenIssuance objects |
| `OfferHelpers.h` | Free function `offerDelete` for removing Offer entries |
| `RippleStateHelpers.h` | Free functions for RippleState (trust line) entries: credit, freeze, issuance, authorization |
| `TokenHelpers.h` | Shared token helpers (freeze/auth checks used by both IOU and MPT paths) |
| `VaultHelpers.h` | Free functions for Vault ledger entries |
## Migration Status
This migration is still in progress. New code should prefer the wrapper style where possible; existing free functions will be migrated incrementally.

View File

@@ -3,12 +3,14 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
//------------------------------------------------------------------------------
//
@@ -20,137 +22,218 @@ namespace xrpl {
//------------------------------------------------------------------------------
//
// Credit functions (from Credit.h)
// IOUIssuance ledger entry wrapper (view-parameterized)
//
//------------------------------------------------------------------------------
/** Calculate the maximum amount of IOUs that an account can hold
@param view the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
@return The maximum amount that can be held.
*/
/** @{ */
STAmount
creditLimit(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
/**
* View-parameterized wrapper for an IOU issuance.
*
* Because IOUs have no dedicated ledger entry — issuance properties live on
* the issuer's AccountRoot — IOUIssuance<V> inherits from AccountRoot<V> and
* adds the currency context plus IOU-specific accessors (RequireAuth /
* AllowTrustLineClawback flags, freeze checks against the holder's trust
* line, etc.).
*
* IOUIssuance<ReadView> — read-only
* IOUIssuance<ApplyView> — read-write
*/
template <typename ViewT>
class IOUIssuance : public AccountRoot<ViewT>, public TokenBase<ViewT>
{
static constexpr bool kIsWritable = SLEBase<ViewT>::kIsWritable;
IOUAmount
creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur);
/** @} */
Currency const currency_;
/** Returns the amount of IOUs issued by issuer that are held by an account
@param view the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
*/
/** @{ */
STAmount
creditBalance(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
/** @} */
public:
/** Constructor for read-only context (Issue). */
IOUIssuance(
ReadView const& view,
Issue const& issue,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: AccountRoot<ViewT>(issue.getIssuer(), view, j), currency_(issue.currency)
{
}
/** Constructor for read-only context (issuer + currency). */
IOUIssuance(
ReadView const& view,
AccountID const& issuer,
Currency const& currency,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: AccountRoot<ViewT>(issuer, view, j), currency_(currency)
{
}
/** Constructor for writable context (Issue). */
IOUIssuance(
ApplyView& view,
Issue const& issue,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: AccountRoot<ViewT>(issue.getIssuer(), view, j), currency_(issue.currency)
{
}
/** Constructor for writable context (issuer + currency). */
IOUIssuance(
ApplyView& view,
AccountID const& issuer,
Currency const& currency,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: AccountRoot<ViewT>(issuer, view, j), currency_(currency)
{
}
/** Converting constructor: writable → read-only. */
template <WritableView OtherViewT>
IOUIssuance(IOUIssuance<OtherViewT> const& other)
requires(!kIsWritable)
: AccountRoot<ViewT>(other), currency_(other.getCurrency())
{
}
[[nodiscard]] Currency const&
getCurrency() const
{
return currency_;
}
[[nodiscard]] Issue
getIssue() const
{
return Issue{currency_, this->id()};
}
// --- IOU-specific domain methods ---
/** Returns IOU issuer transfer fee as Rate (delegates to AccountRoot). */
[[nodiscard]] Rate
transferRate() const override
{
return AccountRoot<ViewT>::transferRate();
}
/** Check if the issuer is globally frozen (delegates to AccountRoot). */
[[nodiscard]] bool
isGlobalFrozen() const override
{
return AccountRoot<ViewT>::isGlobalFrozen();
}
/** Check if the issuer requires holder authorization (lsfRequireAuth). */
[[nodiscard]] bool
requiresAuth() const override;
/** Check if the issuer has enabled clawback (lsfAllowTrustLineClawback). */
[[nodiscard]] bool
canClawback() const override;
/** True if the trust line between `account` and the issuer has the
* issuer-side freeze flag set.
*/
[[nodiscard]] bool
isIndividualFrozen(AccountID const& account) const override;
/** True if the issuance is globally frozen or the trust line is frozen.
* `depth` is ignored for IOUs (no vault recursion). */
[[nodiscard]] bool
isFrozen(AccountID const& account, int depth = 0) const override;
/** True if the trust line has the issuer-side deep-freeze flag set. */
[[nodiscard]] bool
isDeepFrozen(AccountID const& account) const;
/** Check if `account` is allowed to hold this IOU.
* `depth` is ignored for IOUs (no vault recursion). */
[[nodiscard]] TER
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
const override;
/** Check if `from` is allowed to send this IOU to `to`. */
[[nodiscard]] TER
canTransfer(AccountID const& from, AccountID const& to) const override;
// --- Credit / balance ---
/** Maximum amount of this IOU that `account` can hold. */
[[nodiscard]] STAmount
creditLimit(AccountID const& account) const;
[[nodiscard]] IOUAmount
creditLimit2(AccountID const& account) const;
/** Amount of this IOU held by `account`. */
[[nodiscard]] STAmount
creditBalance(AccountID const& account) const;
// --- Trust line operations (writable) ---
/** Create a trust line for this IOU's currency. */
[[nodiscard]] TER
trustCreate(
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex,
WAccountRoot& wrappedAcct,
bool const bAuth,
bool const bNoRipple,
bool const bFreeze,
bool bDeepFreeze,
STAmount const& saBalance,
STAmount const& saLimit,
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,
beast::Journal j)
requires kIsWritable;
/** Add an empty trust line for `accountID`. */
[[nodiscard]] TER
addEmptyHolding(AccountID const& accountID, XRPAmount priorBalance, beast::Journal journal)
requires kIsWritable;
/** Remove an empty trust line for `accountID`. */
[[nodiscard]] TER
removeEmptyHolding(AccountID const& accountID, beast::Journal journal)
requires kIsWritable;
/** Issue `amount` of this IOU from the issuer to `account`. */
[[nodiscard]] TER
issue(AccountID const& account, STAmount const& amount, beast::Journal j)
requires kIsWritable;
/** Redeem `amount` of this IOU from `account` back to the issuer. */
[[nodiscard]] TER
redeem(AccountID const& account, STAmount const& amount, beast::Journal j)
requires kIsWritable;
};
// CTAD deduction guides — bare IOUIssuance(view, ...) always deduces read-only.
// For writable access, use WIOUIssuance(view, ...) explicitly.
IOUIssuance(ReadView const&, Issue const&) -> IOUIssuance<ReadView>;
IOUIssuance(ReadView const&, Issue const&, beast::Journal) -> IOUIssuance<ReadView>;
IOUIssuance(ReadView const&, AccountID const&, Currency const&) -> IOUIssuance<ReadView>;
IOUIssuance(ReadView const&, AccountID const&, Currency const&, beast::Journal)
-> IOUIssuance<ReadView>;
// Backward-compatible aliases
using RIOUIssuance = IOUIssuance<ReadView>;
using WIOUIssuance = IOUIssuance<ApplyView>;
// Explicit instantiation declarations (definitions in .cpp)
extern template class IOUIssuance<ReadView>;
extern template class IOUIssuance<ApplyView>;
//------------------------------------------------------------------------------
//
// Freeze checking (IOU-specific)
// Trust line operations (SLE-level, no issuance context)
//
//------------------------------------------------------------------------------
[[nodiscard]] bool
isIndividualFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isIndividualFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] bool
isFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isFrozen(view, account, issue.currency, issue.account);
}
// Overload with depth parameter for uniformity with MPTIssue version.
// The depth parameter is ignored for IOUs since they don't have vault recursion.
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, std::uint8_t /*depth*/)
{
return isFrozen(view, account, issue);
}
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Issue const& issue,
std::uint8_t = 0 /*ignored*/)
{
return isDeepFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] inline TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
}
//------------------------------------------------------------------------------
//
// Trust line operations
//
//------------------------------------------------------------------------------
/** Create a trust line
This can set an initial balance.
*/
[[nodiscard]] TER
trustCreate(
ApplyView& view,
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // --> ripple state entry
SLE::ref sleAccount, // --> the account being set.
bool const bAuth, // --> authorize account.
bool const bNoRipple, // --> others cannot ripple through
bool const bFreeze, // --> funds cannot leave
bool bDeepFreeze, // --> can neither receive nor send funds
STAmount const& saBalance, // --> balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // --> limit for account being set.
// Issuer should be the account being set.
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,
beast::Journal j);
[[nodiscard]] TER
trustDelete(
ApplyView& view,
@@ -159,88 +242,6 @@ trustDelete(
AccountID const& uHighAccountID,
beast::Journal j);
//------------------------------------------------------------------------------
//
// IOU issuance/redemption
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
issueIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
[[nodiscard]] TER
redeemIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
//------------------------------------------------------------------------------
//
// Authorization and transfer checks (IOU-specific)
//
//------------------------------------------------------------------------------
/** Check if the account lacks required authorization.
*
* Return tecNO_AUTH or tecNO_LINE if it does
* and tesSUCCESS otherwise.
*
* If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return
* tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the
* RippleState does exist, and the RippleState is not authorized.
*
* If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the
* RippleState exists, and is not authorized. Return tecNO_LINE if
* lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if
* WeakAuth and lsfRequireAuth is *not* set, this function will return
* tesSUCCESS even if RippleState does *not* exist.
*
* The default "Legacy" auth type is equivalent to WeakAuth.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
Issue const& issue,
AccountID const& account,
AuthType authType = AuthType::Legacy);
/** Check if the destination account is allowed
* to receive IOU. Return terNO_RIPPLE if rippling is
* disabled on both sides and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to);
//------------------------------------------------------------------------------
//
// Empty holding operations (IOU-specific)
//
//------------------------------------------------------------------------------
/// Any transactors that call addEmptyHolding() in doApply must call
/// canAddHolding() in preflight with the same View and Asset
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
Issue const& issue,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
Issue const& issue,
beast::Journal journal);
/** Delete trustline to AMM. The passed `sle` must be obtained from a prior
* call to view.peek(). Fail if neither side of the trustline is AMM or
* if ammAccountID is seated and is not one of the trustline's side.
@@ -252,14 +253,4 @@ deleteAMMTrustLine(
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior
* call to view.peek().
*/
[[nodiscard]] TER
deleteAMMMPToken(
ApplyView& view,
std::shared_ptr<SLE> sleMPT,
AccountID const& ammAccountID,
beast::Journal j);
} // namespace xrpl

View File

@@ -0,0 +1,240 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <concepts>
#include <memory>
#include <stdexcept>
#include <type_traits>
namespace xrpl {
// Concept to distinguish read-only vs writable view types
template <typename V>
concept WritableView = std::derived_from<V, ApplyView>;
/**
* View-parameterized base class for all ledger entry wrappers.
*
* 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 wrappers cannot mutate state.
*
* Derived classes should provide domain-specific accessors that hide
* implementation details of the underlying ledger entry format.
*/
template <typename ViewT>
class SLEBase
{
public:
static constexpr bool kIsWritable = WritableView<ViewT>;
// SLE pointer type: mutable for writable views, const for read-only
using sle_ptr_type = std::conditional_t<kIsWritable, std::shared_ptr<SLE>, SLE::const_pointer>;
// View reference type: ApplyView& for writable, ReadView const& for read-only
using view_ref_type = std::conditional_t<kIsWritable, ApplyView&, ReadView const&>;
virtual ~SLEBase() = default;
SLEBase(SLEBase const&) = default;
SLEBase(SLEBase&&) = default;
SLEBase&
operator=(SLEBase const&) = delete;
SLEBase&
operator=(SLEBase&&) = delete;
SLEBase() = delete;
// --- 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 */
[[nodiscard]] SLE::const_pointer
sle() const
{
return sle_;
}
/** Returns the read view (always available; ApplyView inherits ReadView) */
[[nodiscard]] ReadView const&
readView() const
{
return view_;
}
/** Const dereference operators (always available) */
STLedgerEntry const*
operator->() const
{
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator-> : exists");
return sle_.get();
}
STLedgerEntry const&
operator*() const
{
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator* : exists");
return *sle_;
}
// --- Writable interface (compile-time gated) ---
/** Returns a mutable SLE for write operations */
[[nodiscard]] sle_ptr_type const&
mutableSle() const
requires kIsWritable
{
return sle_;
}
/** Returns true if this wrapper supports write operations */
[[nodiscard]] bool
canModify() const
requires kIsWritable
{
return sle_ != nullptr;
}
/** Returns the apply view for write operations */
[[nodiscard]] ApplyView&
applyView() const
requires kIsWritable
{
return view_;
}
/** Mutable dereference operators */
STLedgerEntry*
operator->()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator-> : can modify");
return sle_.get();
}
STLedgerEntry&
operator*()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator* : can modify");
return *sle_;
}
void
insert()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::insert : can modify");
view_.insert(sle_);
}
void
erase()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::erase : can modify");
view_.erase(sle_);
}
void
update()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::update : can modify");
view_.update(sle_);
}
void
newSLE()
requires kIsWritable
{
XRPL_ASSERT(!canModify(), "xrpl::SLEBase::newSLE : no existing SLE");
sle_ = std::make_shared<SLE>(key_);
}
[[nodiscard]] beast::Journal
journal() const
{
return j_;
}
protected:
/** Constructor for read-only context */
explicit SLEBase(
SLE::const_pointer sle,
ReadView const& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: view_(view), sle_(std::move(sle)), j_(j)
{
}
/** Converting constructor: writable → read-only.
* Enables implicit conversion from SLEBase<ApplyView> to
* SLEBase<ReadView>, so functions taking ReadOnlySLE const& can
* accept WritableSLE.
*/
template <WritableView OtherViewT>
SLEBase(SLEBase<OtherViewT> const& other)
requires(!kIsWritable)
: view_(other.readView()), sle_(other.sle()), j_(other.journal())
{
}
/** Constructor for writable context (from existing SLE) */
explicit SLEBase(
std::shared_ptr<SLE> sle,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: view_(view)
, key_(sle ? Keylet(sle->getType(), sle->key()) : Keylet(ltANY, uint256{}))
, sle_(std::move(sle))
, j_(j)
{
}
/** 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)
{
}
view_ref_type view_;
// Keylet is only meaningful for writable views, but we conditionally
// include it to avoid wasting space in read-only wrappers.
struct Empty
{
};
[[no_unique_address]]
std::conditional_t<kIsWritable, Keylet, Empty> key_{};
sle_ptr_type sle_;
beast::Journal j_;
};
} // namespace xrpl

View File

@@ -34,15 +34,6 @@ enum class WaiveTransferFee : bool { No = false, Yes };
/** Controls whether accountSend is allowed to overflow OutstandingAmount **/
enum class AllowMPTOverflow : bool { No = false, Yes };
/** Controls whether canTransfer enforces lsfMPTCanTransfer on MPTs.
*
* Default is No (enforce). Use Yes at call sites that must remain available
* even when an MPT issuer has cleared lsfMPTCanTransfer - for example,
* unwinding existing positions in SAV or the Lending Protocol. Has no
* effect on the IOU branch of canTransfer.
*/
enum class WaiveMPTCanTransfer : bool { No = false, Yes };
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
* - StrongAuth - before checking if authorization is required
* - WeakAuth
@@ -54,6 +45,50 @@ enum class WaiveMPTCanTransfer : bool { No = false, Yes };
*/
enum class AuthType { StrongAuth, WeakAuth, Legacy };
//------------------------------------------------------------------------------
//
// TokenBase<ViewT>: abstract interface shared by MPTokenIssuance and IOUIssuance.
//
// This is a pure-interface base (no data members), used as a second base class
// alongside SLEBase<V> (for MPT) or AccountRoot<V> (for IOU) so callers can hold
// a polymorphic reference and dispatch without knowing the token kind. Methods
// whose semantics differ slightly between IOU and MPT are unified here with the
// MPT signature (e.g. `int depth`); IOU implementations ignore depth.
//
//------------------------------------------------------------------------------
template <typename ViewT>
class TokenBase
{
public:
virtual ~TokenBase() = default;
[[nodiscard]] virtual bool
isGlobalFrozen() const = 0;
[[nodiscard]] virtual bool
isIndividualFrozen(AccountID const& account) const = 0;
[[nodiscard]] virtual bool
isFrozen(AccountID const& account, int depth = 0) const = 0;
[[nodiscard]] virtual Rate
transferRate() const = 0;
[[nodiscard]] virtual TER
requireAuth(AccountID const& account, AuthType authType = AuthType::Legacy, int depth = 0)
const = 0;
[[nodiscard]] virtual TER
canTransfer(AccountID const& from, AccountID const& to) const = 0;
[[nodiscard]] virtual bool
canClawback() const = 0;
[[nodiscard]] virtual bool
requiresAuth() const = 0;
};
//------------------------------------------------------------------------------
//
// Freeze checking (Asset-based dispatchers)
@@ -63,26 +98,16 @@ enum class AuthType { StrongAuth, WeakAuth, Legacy };
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, Asset const& asset);
[[nodiscard]] TER
checkGlobalFrozen(ReadView const& view, Asset const& asset);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
[[nodiscard]] TER
checkIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] bool
isFrozen(
ReadView const& view,
AccountID const& account,
Asset const& asset,
std::uint8_t depth = 0);
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
[[nodiscard]] TER
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue);
@@ -104,14 +129,14 @@ isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
Asset const& asset,
std::uint8_t depth = 0);
int depth = 0);
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
std::uint8_t depth = 0);
int depth = 0);
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
@@ -119,11 +144,7 @@ isDeepFrozen(
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Asset const& asset,
std::uint8_t depth = 0);
isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
[[nodiscard]] TER
checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
@@ -139,10 +160,10 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& ass
// Returns the amount an account can spend.
//
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
// If SpendableHandling::SimpleBalance is specified, this is the amount the account can spend
// without going into debt.
//
// If shFULL_BALANCE is specified, this is the amount the account can spend
// If SpendableHandling::FullBalance is specified, this is the amount the account can spend
// total. Specifically:
// * The account can go into debt if using a trust line, and the other side has
// a non-zero limit.
@@ -257,13 +278,7 @@ requireAuth(
AuthType authType = AuthType::Legacy);
[[nodiscard]] TER
canTransfer(
ReadView const& view,
Asset const& asset,
AccountID const& from,
AccountID const& to,
WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No,
std::uint8_t depth = 0);
canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to);
//------------------------------------------------------------------------------
//
@@ -274,7 +289,7 @@ canTransfer(
// Direct send w/o fees:
// - Redeeming IOUs and/or sending sender's own IOUs.
// - Create trust line of needed.
// --> bCheckIssuer : normally require issuer to be involved.
// bCheckIssuer : normally require issuer to be involved.
// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles.
/** Calls static directSendNoFeeIOU if saAmount represents Issue.

View File

@@ -21,13 +21,13 @@ public:
bool sslVerify,
beast::Journal j,
boost::asio::ssl::context_base::method method = boost::asio::ssl::context::sslv23)
: sslContext_{method}, j_(j), verify_{sslVerify}
: ssl_context_{method}, j_(j), verify_{sslVerify}
{
boost::system::error_code ec;
if (sslVerifyFile.empty())
{
registerSSLCerts(sslContext_, ec, j_);
registerSSLCerts(ssl_context_, ec, j_);
if (ec && sslVerifyDir.empty())
{
@@ -37,12 +37,12 @@ public:
}
else
{
sslContext_.load_verify_file(sslVerifyFile);
ssl_context_.load_verify_file(sslVerifyFile);
}
if (!sslVerifyDir.empty())
{
sslContext_.add_verify_path(sslVerifyDir, ec);
ssl_context_.add_verify_path(sslVerifyDir, ec);
if (ec)
{
@@ -55,7 +55,7 @@ public:
boost::asio::ssl::context&
context()
{
return sslContext_;
return ssl_context_;
}
[[nodiscard]] bool
@@ -153,7 +153,7 @@ public:
}
private:
boost::asio::ssl::context sslContext_;
boost::asio::ssl::context ssl_context_;
beast::Journal const j_;
bool const verify_;
};

View File

@@ -83,6 +83,10 @@ public:
virtual Status
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) = 0;
/** Fetch a batch synchronously. */
virtual std::pair<std::vector<std::shared_ptr<NodeObject>>, Status>
fetchBatch(std::vector<uint256> const& hashes) = 0;
/** Store a single object.
Depending on the implementation this may happen immediately
or deferred using a scheduled task.

View File

@@ -67,6 +67,9 @@ public:
backend_->sync();
}
std::vector<std::shared_ptr<NodeObject>>
fetchBatch(std::vector<uint256> const& hashes);
void
asyncFetch(
uint256 const& hash,

View File

@@ -140,8 +140,8 @@ private:
using issue_hasher = std::hash<xrpl::Issue>;
using mptissue_hasher = std::hash<xrpl::MPTIssue>;
issue_hasher mIssueHasher_;
mptissue_hasher mMptissueHasher_;
issue_hasher m_issue_hasher_;
mptissue_hasher m_mptissue_hasher_;
public:
explicit hash() = default;
@@ -151,11 +151,11 @@ public:
{
return asset.visit(
[&](xrpl::Issue const& issue) {
value_type const result(mIssueHasher_(issue));
value_type const result(m_issue_hasher_(issue));
return result;
},
[&](xrpl::MPTIssue const& issue) {
value_type const result(mMptissueHasher_(issue));
value_type const result(m_mptissue_hasher_(issue));
return result;
});
}
@@ -170,8 +170,8 @@ private:
using asset_hasher = std::hash<xrpl::Asset>;
using uint256_hasher = xrpl::uint256::hasher;
asset_hasher issueHasher_;
uint256_hasher uint256Hasher_;
asset_hasher issue_hasher_;
uint256_hasher uint256_hasher_;
public:
hash() = default;
@@ -182,11 +182,11 @@ public:
value_type
operator()(argument_type const& value) const
{
value_type result(issueHasher_(value.in));
boost::hash_combine(result, issueHasher_(value.out));
value_type result(issue_hasher_(value.in));
boost::hash_combine(result, issue_hasher_(value.out));
if (value.domain)
boost::hash_combine(result, uint256Hasher_(*value.domain));
boost::hash_combine(result, uint256_hasher_(*value.domain));
return result;
}

View File

@@ -172,24 +172,24 @@ struct ErrorInfo
{
// Default ctor needed to produce an empty std::array during constexpr eval.
constexpr ErrorInfo()
: code(RpcUnknown), token("unknown"), message("An unknown error code."), httpStatus(200)
: code(RpcUnknown), token("unknown"), message("An unknown error code."), http_status(200)
{
}
constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message)
: code(code), token(token), message(message), httpStatus(200)
: code(code), token(token), message(message), http_status(200)
{
}
constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message, int httpStatus)
: code(code), token(token), message(message), httpStatus(httpStatus)
: code(code), token(token), message(message), http_status(httpStatus)
{
}
ErrorCodeI code;
json::StaticString token;
json::StaticString message;
int httpStatus;
int http_status;
};
/** Returns an ErrorInfo that reflects the error code. */

View File

@@ -287,7 +287,7 @@ credential(uint256 const& key) noexcept
}
Keylet
mptIssuance(std::uint32_t seq, AccountID const& issuer) noexcept;
mptIssuance(std::uint32_t const seq, AccountID const& issuer) noexcept;
Keylet
mptIssuance(MPTID const& issuanceID) noexcept;

View File

@@ -365,8 +365,8 @@ using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
#define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SField const sfName;
#define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SF_##stiSuffix const sfName;
extern SField const sfInvalid; // NOLINT(readability-identifier-naming)
extern SField const sfGeneric; // NOLINT(readability-identifier-naming)
extern SField const kSfInvalid;
extern SField const kSfGeneric;
#include <xrpl/protocol/detail/sfields.macro>

View File

@@ -3,13 +3,11 @@
#include <xrpl/basics/CountedObject.h>
#include <xrpl/basics/LocalValue.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/MPTAmount.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STBase.h>
#include <xrpl/protocol/Serializer.h>
@@ -186,24 +184,6 @@ public:
[[nodiscard]] STAmount const&
value() const noexcept;
/**
* Checks if this amount evaluates to zero when constrained to a specific
* accounting scale.
*
* For XRP and MPT `roundToScale` is a no-op, returns true only when the amount itself is zero.
* The `scale` argument is ignored in that case.
* For IOU, the amount is rounded to the given scale using Number::RoundingMode::ToNearest mode
* and the result is checked for zero; if `scale <= exponent()`, `roundToScale` short-circuits
* and returns the value unchanged, so this returns false for any non-zero amount.
*
* @param scale The target accounting scale to evaluate against.
* @return `true` if this amount rounds to zero at the given scale, `false` otherwise.
*
* @see roundToScale
*/
[[nodiscard]] bool
isZeroAtScale(int scale) const;
//--------------------------------------------------------------------------
//
// Operators
@@ -595,25 +575,12 @@ STAmount::value() const noexcept
return *this;
}
[[nodiscard]] inline bool
inline bool
isLegalNet(STAmount const& value)
{
return !value.native() || (value.mantissa() <= STAmount::kMaxNativeN);
}
[[nodiscard]] inline bool
isLegalMPT(STAmount const& value)
{
return !value.holds<MPTIssue>() ||
(!value.negative() && value.exponent() == 0 && value.mantissa() <= kMaxMpTokenAmount);
}
/* Check recursively if an object has invalid MPTAmount or XRPAmount in STAmount field.
* Calls isLegalNet() and isLegalMPT().
*/
[[nodiscard]] bool
hasInvalidAmount(STBase const& field, beast::Journal j);
//------------------------------------------------------------------------------
//
// Operators

View File

@@ -24,7 +24,7 @@ public:
STBlob(SField const& f, void const* data, std::size_t size);
STBlob(SField const& f, Buffer&& b);
STBlob(SField const& n);
STBlob(SerialIter&, SField const& name = sfGeneric);
STBlob(SerialIter&, SField const& name = kSfGeneric);
[[nodiscard]] std::size_t
size() const;

View File

@@ -21,8 +21,8 @@ class STPathElement final : public CountedObject<STPathElement>
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
bool is_offer_;
std::size_t hash_value_;
public:
// Bitwise values (typeCurrency | typeMPT)
@@ -235,9 +235,9 @@ private:
// ------------ STPathElement ------------
inline STPathElement::STPathElement() : type_(TypeNone), isOffer_(true)
inline STPathElement::STPathElement() : type_(TypeNone), is_offer_(true)
{
hashValue_ = getHash(*this);
hash_value_ = getHash(*this);
}
inline STPathElement::STPathElement(
@@ -248,11 +248,11 @@ inline STPathElement::STPathElement(
{
if (!account)
{
isOffer_ = true;
is_offer_ = true;
}
else
{
isOffer_ = false;
is_offer_ = false;
accountID_ = *account;
type_ |= TypeAccount;
XRPL_ASSERT(
@@ -272,7 +272,7 @@ inline STPathElement::STPathElement(
XRPL_ASSERT(issuerID_ != noAccount(), "xrpl::STPathElement::STPathElement : issuer is set");
}
hashValue_ = getHash(*this);
hash_value_ = getHash(*this);
}
inline STPathElement::STPathElement(
@@ -284,9 +284,9 @@ inline STPathElement::STPathElement(
, accountID_(account)
, assetID_(asset)
, issuerID_(issuer)
, isOffer_(isXRP(accountID_))
, is_offer_(isXRP(accountID_))
{
if (!isOffer_)
if (!is_offer_)
type_ |= TypeAccount;
if (forceAsset || !isXRP(assetID_))
@@ -295,7 +295,7 @@ inline STPathElement::STPathElement(
if (!isXRP(issuer))
type_ |= TypeIssuer;
hashValue_ = getHash(*this);
hash_value_ = getHash(*this);
}
inline STPathElement::STPathElement(
@@ -307,12 +307,12 @@ inline STPathElement::STPathElement(
, accountID_(account)
, assetID_(asset)
, issuerID_(issuer)
, isOffer_(isXRP(accountID_))
, is_offer_(isXRP(accountID_))
{
assetID_.visit(
[&](Currency const&) { type_ = type_ & (~Type::TypeMpt); },
[&](MPTID const&) { type_ = type_ & (~Type::TypeCurrency); });
hashValue_ = getHash(*this);
hash_value_ = getHash(*this);
}
inline auto
@@ -324,7 +324,7 @@ STPathElement::getNodeType() const
inline bool
STPathElement::isOffer() const
{
return isOffer_;
return is_offer_;
}
inline bool
@@ -404,7 +404,7 @@ STPathElement::getIssuerID() const
inline bool
STPathElement::operator==(STPathElement const& t) const
{
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ &&
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hash_value_ == t.hash_value_ &&
accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_;
}

View File

@@ -27,7 +27,7 @@ enum class TxnSql : char {
class STTx final : public STObject, public CountedObject<STTx>
{
uint256 tid_;
TxType txType_;
TxType tx_type_;
public:
static constexpr std::size_t kMinMultiSigners = 1;
@@ -187,7 +187,7 @@ inline STTx::STTx(SerialIter&& sit) // NOLINT(cppcoreguidelines-rvalue-referenc
inline TxType
STTx::getTxnType() const
{
return txType_;
return tx_type_;
}
inline Blob

View File

@@ -112,7 +112,7 @@ LEDGER_ENTRY(ltSIGNER_LIST, 0x0053, SignerList, signer_list, ({
/** A ledger object which describes a ticket.
\sa keylet::kTicket
\sa keylet::kTICKET
*/
LEDGER_ENTRY(ltTICKET, 0x0054, Ticket, ticket, ({
{sfAccount, SoeRequired},
@@ -400,7 +400,6 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfPreviousTxnLgrSeq, SoeRequired},
{sfDomainID, SoeOptional},
{sfMutableFlags, SoeDefault},
{sfReferenceHolding, SoeOptional},
}))
/** A ledger object which tracks MPToken
@@ -592,7 +591,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
// LoanBroker.ManagementFeeRate
// The unrounded true total fee still owed to the broker.
//
// Note the "True" values may differ significantly from the tracked
// Note the the "True" values may differ significantly from the tracked
// rounded values.
{sfPaymentRemaining, SoeDefault},
{sfPeriodicPayment, SoeRequired},

View File

@@ -205,7 +205,6 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36)
TYPED_SFIELD(sfLoanBrokerID, UINT256, 37,
SField::kSmdPseudoAccount | SField::kSmdDefault)
TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfReferenceHolding, UINT256, 39)
// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)

View File

@@ -688,7 +688,6 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
({
{sfLedgerFixType, SoeRequired},
{sfOwner, SoeOptional},
{sfBookDirectory, SoeOptional},
}))
/** This transaction type creates a MPTokensIssuance instance */

View File

@@ -278,30 +278,6 @@ public:
{
return this->sle_->isFieldPresent(sfMutableFlags);
}
/**
* @brief Get sfReferenceHolding (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getReferenceHolding() const
{
if (hasReferenceHolding())
return this->sle_->at(sfReferenceHolding);
return std::nullopt;
}
/**
* @brief Check if sfReferenceHolding is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasReferenceHolding() const
{
return this->sle_->isFieldPresent(sfReferenceHolding);
}
};
/**
@@ -493,17 +469,6 @@ public:
return *this;
}
/**
* @brief Set sfReferenceHolding (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setReferenceHolding(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfReferenceHolding] = value;
return *this;
}
/**
* @brief Build and return the completed MPTokenIssuance wrapper.
* @param index The ledger entry index.

View File

@@ -83,32 +83,6 @@ public:
{
return this->tx_->isFieldPresent(sfOwner);
}
/**
* @brief Get sfBookDirectory (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_UINT256::type::value_type>
getBookDirectory() const
{
if (hasBookDirectory())
{
return this->tx_->at(sfBookDirectory);
}
return std::nullopt;
}
/**
* @brief Check if sfBookDirectory is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasBookDirectory() const
{
return this->tx_->isFieldPresent(sfBookDirectory);
}
};
/**
@@ -175,17 +149,6 @@ public:
return *this;
}
/**
* @brief Set sfBookDirectory (SoeOptional)
* @return Reference to this builder for method chaining.
*/
LedgerStateFixBuilder&
setBookDirectory(std::decay_t<typename SF_UINT256::type::value_type> const& value)
{
object_[sfBookDirectory] = value;
return *this;
}
/**
* @brief Build and return the LedgerStateFix wrapper.
* @param publicKey The public key for signing.

View File

@@ -21,7 +21,7 @@ struct Entry : public beast::List<Entry>::Node
@param now Construction time of Entry.
*/
explicit Entry(clock_type::time_point const now)
: refcount(0), localBalance(now), remoteBalance(0)
: refcount(0), local_balance(now), remote_balance(0)
{
}
@@ -46,7 +46,7 @@ struct Entry : public beast::List<Entry>::Node
int
balance(clock_type::time_point const now)
{
return localBalance.value(now) + remoteBalance;
return local_balance.value(now) + remote_balance;
}
// Add a charge and return normalized balance
@@ -54,7 +54,7 @@ struct Entry : public beast::List<Entry>::Node
int
add(int charge, clock_type::time_point const now)
{
return localBalance.add(charge, now) + remoteBalance;
return local_balance.add(charge, now) + remote_balance;
}
// The public key of the peer
@@ -67,10 +67,10 @@ struct Entry : public beast::List<Entry>::Node
int refcount;
// Exponentially decaying balance of resource consumption
DecayingSample<kDecayWindowSeconds, clock_type> localBalance;
DecayingSample<kDecayWindowSeconds, clock_type> local_balance;
// Normalized balance contribution from imports
int remoteBalance;
int remote_balance;
// Time of the last warning
clock_type::time_point lastWarningTime;

View File

@@ -25,11 +25,11 @@ struct Key
std::size_t
operator()(Key const& v) const
{
return addrHash_(v.address);
return addr_hash_(v.address);
}
private:
beast::Uhash<> addrHash_;
beast::Uhash<> addr_hash_;
};
struct KeyEqual

View File

@@ -194,34 +194,34 @@ public:
for (auto& inboundEntry : inbound_)
{
int const localBalance = inboundEntry.localBalance.value(now);
if ((localBalance + inboundEntry.remoteBalance) >= threshold)
int const localBalance = inboundEntry.local_balance.value(now);
if ((localBalance + inboundEntry.remote_balance) >= threshold)
{
json::Value& entry = (ret[inboundEntry.toString()] = json::ValueType::Object);
entry[jss::local] = localBalance;
entry[jss::remote] = inboundEntry.remoteBalance;
entry[jss::remote] = inboundEntry.remote_balance;
entry[jss::type] = "inbound";
}
}
for (auto& outboundEntry : outbound_)
{
int const localBalance = outboundEntry.localBalance.value(now);
if ((localBalance + outboundEntry.remoteBalance) >= threshold)
int const localBalance = outboundEntry.local_balance.value(now);
if ((localBalance + outboundEntry.remote_balance) >= threshold)
{
json::Value& entry = (ret[outboundEntry.toString()] = json::ValueType::Object);
entry[jss::local] = localBalance;
entry[jss::remote] = outboundEntry.remoteBalance;
entry[jss::remote] = outboundEntry.remote_balance;
entry[jss::type] = "outbound";
}
}
for (auto& adminEntry : admin_)
{
int const localBalance = adminEntry.localBalance.value(now);
if ((localBalance + adminEntry.remoteBalance) >= threshold)
int const localBalance = adminEntry.local_balance.value(now);
if ((localBalance + adminEntry.remote_balance) >= threshold)
{
json::Value& entry = (ret[adminEntry.toString()] = json::ValueType::Object);
entry[jss::local] = localBalance;
entry[jss::remote] = adminEntry.remoteBalance;
entry[jss::remote] = adminEntry.remote_balance;
entry[jss::type] = "admin";
}
}
@@ -242,7 +242,7 @@ public:
for (auto& inboundEntry : inbound_)
{
Gossip::Item item;
item.balance = inboundEntry.localBalance.value(now);
item.balance = inboundEntry.local_balance.value(now);
if (item.balance >= kMinimumGossipBalance)
{
item.address = inboundEntry.key->address;
@@ -278,7 +278,7 @@ public:
Import::Item item;
item.balance = gossipItem.balance;
item.consumer = newInboundEndpoint(gossipItem.address);
item.consumer.entry().remoteBalance += item.balance;
item.consumer.entry().remote_balance += item.balance;
next.items.push_back(item);
}
}
@@ -295,14 +295,14 @@ public:
Import::Item item;
item.balance = gossipItem.balance;
item.consumer = newInboundEndpoint(gossipItem.address);
item.consumer.entry().remoteBalance += item.balance;
item.consumer.entry().remote_balance += item.balance;
next.items.push_back(item);
}
Import& prev(resultIt->second);
for (auto& item : prev.items)
{
item.consumer.entry().remoteBalance -= item.balance;
item.consumer.entry().remote_balance -= item.balance;
}
std::swap(next, prev);
@@ -345,7 +345,7 @@ public:
for (auto itemIter(import.items.begin()); itemIter != import.items.end();
++itemIter)
{
itemIter->consumer.entry().remoteBalance -= itemIter->balance;
itemIter->consumer.entry().remote_balance -= itemIter->balance;
}
iter = importTable_.erase(iter);
@@ -520,8 +520,8 @@ public:
item["count"] = entry.refcount;
item["name"] = entry.toString();
item["balance"] = entry.balance(now);
if (entry.remoteBalance != 0)
item["remote_balance"] = entry.remoteBalance;
if (entry.remote_balance != 0)
item["remote_balance"] = entry.remote_balance;
}
}

View File

@@ -21,7 +21,7 @@ struct Handoff
bool moved = false;
// If response is set, this determines the keep alive
bool keepAlive = false;
bool keep_alive = false;
// When set, this will be sent back
std::shared_ptr<Writer> response;

View File

@@ -30,19 +30,19 @@ struct Port
boost::asio::ip::address ip;
std::uint16_t port = 0;
std::set<std::string, boost::beast::iless> protocol;
std::vector<boost::asio::ip::network_v4> adminNetsV4;
std::vector<boost::asio::ip::network_v6> adminNetsV6;
std::vector<boost::asio::ip::network_v4> secureGatewayNetsV4;
std::vector<boost::asio::ip::network_v6> secureGatewayNetsV6;
std::vector<boost::asio::ip::network_v4> admin_nets_v4;
std::vector<boost::asio::ip::network_v6> admin_nets_v6;
std::vector<boost::asio::ip::network_v4> secure_gateway_nets_v4;
std::vector<boost::asio::ip::network_v6> secure_gateway_nets_v6;
std::string user;
std::string password;
std::string adminUser;
std::string adminPassword;
std::string sslKey;
std::string sslCert;
std::string sslChain;
std::string sslCiphers;
boost::beast::websocket::permessage_deflate pmdOptions;
std::string admin_user;
std::string admin_password;
std::string ssl_key;
std::string ssl_cert;
std::string ssl_chain;
std::string ssl_ciphers;
boost::beast::websocket::permessage_deflate pmd_options;
std::shared_ptr<boost::asio::ssl::context> context;
// How many incoming connections are allowed on this
@@ -50,7 +50,7 @@ struct Port
int limit = 0;
// Websocket disconnects if send queue exceeds this limit
std::uint16_t wsQueueLimit{};
std::uint16_t ws_queue_limit{};
// Returns `true` if any websocket protocols are specified
[[nodiscard]] bool
@@ -78,22 +78,22 @@ struct ParsedPort
std::set<std::string, boost::beast::iless> protocol;
std::string user;
std::string password;
std::string adminUser;
std::string adminPassword;
std::string sslKey;
std::string sslCert;
std::string sslChain;
std::string sslCiphers;
boost::beast::websocket::permessage_deflate pmdOptions;
std::string admin_user;
std::string admin_password;
std::string ssl_key;
std::string ssl_cert;
std::string ssl_chain;
std::string ssl_ciphers;
boost::beast::websocket::permessage_deflate pmd_options;
int limit = 0;
std::uint16_t wsQueueLimit{};
std::uint16_t ws_queue_limit{};
std::optional<boost::asio::ip::address> ip;
std::optional<std::uint16_t> port;
std::vector<boost::asio::ip::network_v4> adminNetsV4;
std::vector<boost::asio::ip::network_v6> adminNetsV6;
std::vector<boost::asio::ip::network_v4> secureGatewayNetsV4;
std::vector<boost::asio::ip::network_v6> secureGatewayNetsV6;
std::vector<boost::asio::ip::network_v4> admin_nets_v4;
std::vector<boost::asio::ip::network_v6> admin_nets_v6;
std::vector<boost::asio::ip::network_v4> secure_gateway_nets_v4;
std::vector<boost::asio::ip::network_v6> secure_gateway_nets_v6;
};
void

View File

@@ -58,13 +58,13 @@ protected:
Handler& handler_;
boost::asio::executor_work_guard<boost::asio::executor> work_;
boost::asio::strand<boost::asio::executor> strand_;
endpoint_type remoteAddress_;
endpoint_type remote_address_;
beast::Journal const journal_;
std::string id_;
std::size_t nid_;
boost::asio::streambuf readBuf_;
boost::asio::streambuf read_buf_;
http_request_type message_;
std::vector<Buffer> wq_;
std::vector<Buffer> wq2_;
@@ -73,9 +73,9 @@ protected:
bool complete_ = false;
boost::system::error_code ec_;
int requestCount_ = 0;
std::size_t bytesIn_ = 0;
std::size_t bytesOut_ = 0;
int request_count_ = 0;
std::size_t bytes_in_ = 0;
std::size_t bytes_out_ = 0;
//--------------------------------------------------------------------------
@@ -151,7 +151,7 @@ protected:
beast::IP::Endpoint
remoteAddress() override
{
return beast::IPAddressConversion::fromAsio(remoteAddress_);
return beast::IPAddressConversion::fromAsio(remote_address_);
}
http_request_type&
@@ -191,23 +191,23 @@ BaseHTTPPeer<Handler, Impl>::BaseHTTPPeer(
, handler_(handler)
, work_(boost::asio::make_work_guard(executor))
, strand_(boost::asio::make_strand(executor))
, remoteAddress_(std::move(remoteAddress))
, remote_address_(std::move(remoteAddress))
, journal_(journal)
{
readBuf_.commit(
boost::asio::buffer_copy(readBuf_.prepare(boost::asio::buffer_size(buffers)), buffers));
read_buf_.commit(
boost::asio::buffer_copy(read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers));
static std::atomic<int> kSid;
nid_ = ++kSid;
id_ = std::string("#") + std::to_string(nid_) + " ";
JLOG(journal_.trace()) << id_ << "accept: " << remoteAddress_.address();
JLOG(journal_.trace()) << id_ << "accept: " << remote_address_.address();
}
template <class Handler, class Impl>
BaseHTTPPeer<Handler, Impl>::~BaseHTTPPeer()
{
handler_.onClose(session(), ec_);
JLOG(journal_.trace()) << id_ << "destroyed: " << requestCount_
<< ((requestCount_ == 1) ? " request" : " requests");
JLOG(journal_.trace()) << id_ << "destroyed: " << request_count_
<< ((request_count_ == 1) ? " request" : " requests");
}
template <class Handler, class Impl>
@@ -245,7 +245,7 @@ BaseHTTPPeer<Handler, Impl>::startTimer()
boost::beast::get_lowest_layer(impl().stream_)
.expires_after(
std::chrono::seconds(
remoteAddress_.address().is_loopback() ? kTimeoutSecondsLocal : kTimeoutSeconds));
remote_address_.address().is_loopback() ? kTimeoutSecondsLocal : kTimeoutSeconds));
}
// Convenience for discarding the error code
@@ -274,7 +274,7 @@ BaseHTTPPeer<Handler, Impl>::doRead(yield_context doYield)
complete_ = false;
error_code ec;
startTimer();
boost::beast::http::async_read(impl().stream_, readBuf_, message_, doYield[ec]);
boost::beast::http::async_read(impl().stream_, read_buf_, message_, doYield[ec]);
cancelTimer();
if (ec == boost::beast::http::error::end_of_stream)
return doClose();
@@ -296,7 +296,7 @@ BaseHTTPPeer<Handler, Impl>::onWrite(error_code const& ec, std::size_t bytesTran
return onTimer();
if (ec)
return fail(ec, "write");
bytesOut_ += bytesTransferred;
bytes_out_ += bytesTransferred;
{
std::scoped_lock const lock(mutex_);
wq2_.clear();

View File

@@ -27,7 +27,7 @@ protected:
Port const& port_;
Handler& handler_;
endpoint_type remoteAddress_;
endpoint_type remote_address_;
beast::WrappedSink sink_;
beast::Journal const j_;
@@ -65,7 +65,7 @@ BasePeer<Handler, Impl>::BasePeer(
beast::Journal journal)
: port_(port)
, handler_(handler)
, remoteAddress_(std::move(remoteAddress))
, remote_address_(std::move(remoteAddress))
, sink_(
journal.sink(),
[] {

View File

@@ -42,15 +42,15 @@ private:
/// The socket has been closed, or will close after the next write
/// finishes. Do not do any more writes, and don't try to close
/// again.
bool doClose_ = false;
bool do_close_ = false;
boost::beast::websocket::close_reason cr_;
waitable_timer timer_;
bool closeOnTimer_ = false;
bool pingActive_ = false;
bool close_on_timer_ = false;
bool ping_active_ = false;
boost::beast::websocket::ping_data payload_;
error_code ec_;
std::function<void(boost::beast::websocket::frame_type, boost::beast::string_view)>
controlCallback_;
control_callback_;
public:
template <class Body, class Headers>
@@ -85,7 +85,7 @@ public:
[[nodiscard]] boost::asio::ip::tcp::endpoint const&
remoteEndpoint() const override
{
return this->remoteAddress_;
return this->remote_address_;
}
void
@@ -173,14 +173,14 @@ BaseWSPeer<Handler, Impl>::run()
{
if (!strand_.running_in_this_thread())
return post(strand_, std::bind(&BaseWSPeer::run, impl().shared_from_this()));
impl().ws_.set_option(port().pmdOptions);
impl().ws_.set_option(port().pmd_options);
// Must manage the control callback memory outside of the `control_callback`
// function
controlCallback_ =
control_callback_ =
std::bind(&BaseWSPeer::onPingPong, this, std::placeholders::_1, std::placeholders::_2);
impl().ws_.control_callback(controlCallback_);
impl().ws_.control_callback(control_callback_);
startTimer();
closeOnTimer_ = true;
close_on_timer_ = true;
impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) {
res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString());
}));
@@ -198,9 +198,9 @@ BaseWSPeer<Handler, Impl>::send(std::shared_ptr<WSMsg> w)
{
if (!strand_.running_in_this_thread())
return post(strand_, std::bind(&BaseWSPeer::send, impl().shared_from_this(), std::move(w)));
if (doClose_)
if (do_close_)
return;
if (wq_.size() > port().wsQueueLimit)
if (wq_.size() > port().ws_queue_limit)
{
cr_.code = safeCast<decltype(cr_.code)>(boost::beast::websocket::close_code::policy_error);
cr_.reason = "Policy error: client is too slow.";
@@ -227,9 +227,9 @@ BaseWSPeer<Handler, Impl>::close(boost::beast::websocket::close_reason const& re
{
if (!strand_.running_in_this_thread())
return post(strand_, [self = impl().shared_from_this(), reason] { self->close(reason); });
if (doClose_)
if (do_close_)
return;
doClose_ = true;
do_close_ = true;
if (wq_.empty())
{
impl().ws_.async_close(
@@ -260,7 +260,7 @@ BaseWSPeer<Handler, Impl>::onWsHandshake(error_code const& ec)
{
if (ec)
return fail(ec, "on_ws_handshake");
closeOnTimer_ = false;
close_on_timer_ = false;
doRead();
}
@@ -313,7 +313,7 @@ BaseWSPeer<Handler, Impl>::onWriteFin(error_code const& ec)
if (ec)
return fail(ec, "write_fin");
wq_.pop_front();
if (doClose_)
if (do_close_)
{
impl().ws_.async_close(
cr_,
@@ -409,7 +409,7 @@ BaseWSPeer<Handler, Impl>::onPing(error_code const& ec)
{
if (ec == boost::asio::error::operation_aborted)
return;
pingActive_ = false;
ping_active_ = false;
if (!ec)
return;
fail(ec, "on_ping");
@@ -426,7 +426,7 @@ BaseWSPeer<Handler, Impl>::onPingPong(
boost::beast::string_view const p(payload_.begin());
if (payload == p)
{
closeOnTimer_ = false;
close_on_timer_ = false;
JLOG(this->j_.trace()) << "got matching pong";
}
else
@@ -444,11 +444,11 @@ BaseWSPeer<Handler, Impl>::onTimer(error_code ec)
return;
if (!ec)
{
if (!closeOnTimer_ || !pingActive_)
if (!close_on_timer_ || !ping_active_)
{
startTimer();
closeOnTimer_ = true;
pingActive_ = true;
close_on_timer_ = true;
ping_active_ = true;
// cryptographic is probably overkill..
beast::rngfill(payload_.begin(), payload_.size(), cryptoPrng());
impl().ws_.async_ping(

View File

@@ -23,6 +23,7 @@
#include <sys/resource.h>
#include <dirent.h>
#include <unistd.h>
#endif
#include <algorithm>
@@ -60,7 +61,7 @@ private:
boost::asio::io_context& ioc_;
stream_type stream_;
socket_type& socket_;
endpoint_type remoteAddress_;
endpoint_type remote_address_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
beast::Journal const j_;
@@ -89,19 +90,16 @@ private:
acceptor_type acceptor_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
bool ssl_{
port_.protocol.contains("https") || port_.protocol.contains("wss") ||
port_.protocol.contains("wss2") || port_.protocol.contains("peer")};
port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 ||
port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0};
bool plain_{
port_.protocol.contains("http") || port_.protocol.contains("ws") ||
(port_.protocol.contains("ws2"))};
port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 ||
(port_.protocol.count("ws2") != 0u)};
static constexpr std::chrono::milliseconds kInitialAcceptDelay{50};
static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000};
std::chrono::milliseconds acceptDelay_{kInitialAcceptDelay};
boost::asio::steady_timer backoffTimer_;
static constexpr std::uint64_t kMaxUsedFdPercent = 70;
static constexpr std::chrono::milliseconds kFdSampleInterval{250};
clock_type::time_point fdSampleAt_;
bool cachedThrottle_{false};
std::chrono::milliseconds accept_delay_{kInitialAcceptDelay};
boost::asio::steady_timer backoff_timer_;
static constexpr double kFreeFdThreshold = 0.70;
struct FDStats
{
@@ -166,7 +164,7 @@ Door<Handler>::Detector::Detector(
, ioc_(ioc)
, stream_(std::move(stream))
, socket_(stream_.socket())
, remoteAddress_(std::move(remoteAddress))
, remote_address_(std::move(remoteAddress))
, strand_(boost::asio::make_strand(ioc_))
, j_(j)
{
@@ -201,18 +199,18 @@ Door<Handler>::Detector::doDetect(boost::asio::yield_context doYield)
if (ssl)
{
if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
port_, handler_, ioc_, j_, remoteAddress_, buf.data(), std::move(stream_)))
port_, handler_, ioc_, j_, remote_address_, buf.data(), std::move(stream_)))
sp->run();
return;
}
if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
port_, handler_, ioc_, j_, remoteAddress_, buf.data(), std::move(stream_)))
port_, handler_, ioc_, j_, remote_address_, buf.data(), std::move(stream_)))
sp->run();
return;
}
if (ec != boost::asio::error::operation_aborted)
{
JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from " << remoteAddress_;
JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from " << remote_address_;
}
}
@@ -281,8 +279,7 @@ Door<Handler>::Door(
, ioc_(ioContext)
, acceptor_(ioContext)
, strand_(boost::asio::make_strand(ioContext))
, backoffTimer_(ioContext)
, fdSampleAt_(clock_type::now() - kFdSampleInterval)
, backoff_timer_(ioContext)
{
reOpen();
}
@@ -305,7 +302,7 @@ Door<Handler>::close()
return boost::asio::post(
strand_, std::bind(&Door<Handler>::close, this->shared_from_this()));
}
backoffTimer_.cancel();
backoff_timer_.cancel();
error_code ec;
acceptor_.close(ec);
}
@@ -341,11 +338,11 @@ Door<Handler>::doAccept(boost::asio::yield_context doYield)
{
if (shouldThrottleForFds())
{
JLOG(j_.warn()) << "Throttling do_accept for " << acceptDelay_.count() << "ms.";
backoffTimer_.expires_after(acceptDelay_);
backoff_timer_.expires_after(accept_delay_);
boost::system::error_code tec;
backoffTimer_.async_wait(doYield[tec]);
acceptDelay_ = std::min(acceptDelay_ * 2, kMaxAcceptDelay);
backoff_timer_.async_wait(doYield[tec]);
accept_delay_ = std::min(accept_delay_ * 2, kMaxAcceptDelay);
JLOG(j_.warn()) << "Throttling do_accept for " << accept_delay_.count() << "ms.";
continue;
}
@@ -362,17 +359,14 @@ Door<Handler>::doAccept(boost::asio::yield_context doYield)
if (ec == boost::asio::error::no_descriptors ||
ec == boost::asio::error::no_buffer_space)
{
char const* const cause = (ec == boost::asio::error::no_descriptors)
? "too many open files"
: "kernel buffer space exhausted";
JLOG(j_.warn()) << "accept: " << cause << ". Pausing for " << acceptDelay_.count()
<< "ms.";
JLOG(j_.warn()) << "accept: Too many open files. Pausing for "
<< accept_delay_.count() << "ms.";
backoffTimer_.expires_after(acceptDelay_);
backoff_timer_.expires_after(accept_delay_);
boost::system::error_code tec;
backoffTimer_.async_wait(doYield[tec]);
backoff_timer_.async_wait(doYield[tec]);
acceptDelay_ = std::min(acceptDelay_ * 2, kMaxAcceptDelay);
accept_delay_ = std::min(accept_delay_ * 2, kMaxAcceptDelay);
}
else
{
@@ -381,7 +375,7 @@ Door<Handler>::doAccept(boost::asio::yield_context doYield)
continue;
}
acceptDelay_ = kInitialAcceptDelay;
accept_delay_ = kInitialAcceptDelay;
if (ssl_ && plain_)
{
@@ -434,15 +428,14 @@ Door<Handler>::shouldThrottleForFds()
#if BOOST_OS_WINDOWS
return false;
#else
auto const now = clock_type::now();
if (now - fdSampleAt_ < kFdSampleInterval)
return cachedThrottle_;
fdSampleAt_ = now;
auto const stats = queryFdStats();
cachedThrottle_ =
stats && stats->limit > 0 && stats->used * 100 > stats->limit * kMaxUsedFdPercent;
return cachedThrottle_;
if (!stats || stats->limit == 0)
return false;
auto const& s = *stats;
auto const free = (s.limit > s.used) ? (s.limit - s.used) : 0ull;
double const freeRatio = static_cast<double>(free) / static_cast<double>(s.limit);
return freeRatio < kFreeFdThreshold;
#endif
}

View File

@@ -82,7 +82,7 @@ template <class Handler>
void
PlainHTTPPeer<Handler>::run()
{
if (!this->handler_.onAccept(this->session(), this->remoteAddress_))
if (!this->handler_.onAccept(this->session(), this->remote_address_))
{
util::spawn(this->strand_, std::bind(&PlainHTTPPeer::doClose, this->shared_from_this()));
return;
@@ -103,7 +103,7 @@ PlainHTTPPeer<Handler>::websocketUpgrade()
auto ws = this->ios().template emplace<PlainWSPeer<Handler>>(
this->port_,
this->handler_,
this->remoteAddress_,
this->remote_address_,
std::move(this->message_),
std::move(stream_),
this->journal_);
@@ -114,20 +114,20 @@ template <class Handler>
void
PlainHTTPPeer<Handler>::doRequest()
{
++this->requestCount_;
++this->request_count_;
auto const what =
this->handler_.onHandoff(this->session(), std::move(this->message_), this->remoteAddress_);
this->handler_.onHandoff(this->session(), std::move(this->message_), this->remote_address_);
if (what.moved)
return;
boost::system::error_code ec;
if (what.response)
{
// half-close on Connection: close
if (!what.keepAlive)
if (!what.keep_alive)
socket_.shutdown(socket_type::shutdown_receive, ec);
if (ec)
return this->fail(ec, "request");
return this->write(what.response, what.keepAlive);
return this->write(what.response, what.keep_alive);
}
// Perform half-close when Connection: close and not SSL

View File

@@ -26,7 +26,7 @@ private:
using yield_context = boost::asio::yield_context;
using error_code = boost::system::error_code;
std::unique_ptr<stream_type> streamPtr_;
std::unique_ptr<stream_type> stream_ptr_;
stream_type& stream_;
socket_type& socket_;
@@ -80,8 +80,8 @@ SSLHTTPPeer<Handler>::SSLHTTPPeer(
journal,
remoteAddress,
buffers)
, streamPtr_(std::make_unique<stream_type>(middle_type(std::move(stream)), *port.context))
, stream_(*streamPtr_)
, stream_ptr_(std::make_unique<stream_type>(middle_type(std::move(stream)), *port.context))
, stream_(*stream_ptr_)
, socket_(stream_.next_layer().socket())
{
}
@@ -91,7 +91,7 @@ template <class Handler>
void
SSLHTTPPeer<Handler>::run()
{
if (!this->handler_.onAccept(this->session(), this->remoteAddress_))
if (!this->handler_.onAccept(this->session(), this->remote_address_))
{
util::spawn(this->strand_, std::bind(&SSLHTTPPeer::doClose, this->shared_from_this()));
return;
@@ -110,9 +110,9 @@ SSLHTTPPeer<Handler>::websocketUpgrade()
auto ws = this->ios().template emplace<SSLWSPeer<Handler>>(
this->port_,
this->handler_,
this->remoteAddress_,
this->remote_address_,
std::move(this->message_),
std::move(this->streamPtr_),
std::move(this->stream_ptr_),
this->journal_);
return ws;
}
@@ -124,8 +124,8 @@ SSLHTTPPeer<Handler>::doHandshake(yield_context doYield)
boost::system::error_code ec;
stream_.set_verify_mode(boost::asio::ssl::verify_none);
this->startTimer();
this->readBuf_.consume(
stream_.async_handshake(stream_type::server, this->readBuf_.data(), doYield[ec]));
this->read_buf_.consume(
stream_.async_handshake(stream_type::server, this->read_buf_.data(), doYield[ec]));
this->cancelTimer();
if (ec == boost::beast::error::timeout)
return this->onTimer();
@@ -148,13 +148,13 @@ template <class Handler>
void
SSLHTTPPeer<Handler>::doRequest()
{
++this->requestCount_;
++this->request_count_;
auto const what = this->handler_.onHandoff(
this->session(), std::move(streamPtr_), std::move(this->message_), this->remoteAddress_);
this->session(), std::move(stream_ptr_), std::move(this->message_), this->remote_address_);
if (what.moved)
return;
if (what.response)
return this->write(what.response, what.keepAlive);
return this->write(what.response, what.keep_alive);
// legacy
this->handler_.onRequest(this->session());
}

View File

@@ -28,7 +28,7 @@ class SSLWSPeer : public BaseWSPeer<Handler, SSLWSPeer<Handler>>,
using stream_type = boost::beast::ssl_stream<socket_type>;
using waitable_timer = boost::asio::basic_waitable_timer<clock_type>;
std::unique_ptr<stream_type> streamPtr_;
std::unique_ptr<stream_type> stream_ptr_;
boost::beast::websocket::stream<stream_type&> ws_;
public:
@@ -61,8 +61,8 @@ SSLWSPeer<Handler>::SSLWSPeer(
remoteEndpoint,
std::move(request),
journal)
, streamPtr_(std::move(streamPtr))
, ws_(*streamPtr_)
, stream_ptr_(std::move(streamPtr))
, ws_(*stream_ptr_)
{
}

View File

@@ -66,7 +66,7 @@ private:
Handler& handler_;
beast::Journal const j_;
boost::asio::io_context& ioContext_;
boost::asio::io_context& io_context_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> work_;
@@ -104,7 +104,7 @@ public:
boost::asio::io_context&
getIoContext()
{
return ioContext_;
return io_context_;
}
bool
@@ -122,9 +122,9 @@ ServerImpl<Handler>::ServerImpl(
beast::Journal journal)
: handler_(handler)
, j_(journal)
, ioContext_(ioContext)
, strand_(boost::asio::make_strand(ioContext_))
, work_(std::in_place, boost::asio::make_work_guard(ioContext_))
, io_context_(ioContext)
, strand_(boost::asio::make_strand(io_context_))
, work_(std::in_place, boost::asio::make_work_guard(io_context_))
{
}
@@ -150,7 +150,7 @@ ServerImpl<Handler>::ports(std::vector<Port> const& ports)
{
ports_.push_back(port);
auto& internalPort = ports_.back();
if (auto sp = ios_.emplace<Door<Handler>>(handler_, ioContext_, internalPort, j_))
if (auto sp = ios_.emplace<Door<Handler>>(handler_, io_context_, internalPort, j_))
{
list_.push_back(sp);

View File

@@ -231,7 +231,7 @@ The `fetchNodeNT()` method goes through three phases:
will be 0.
2. If the node is not in the TreeNodeCache, we attempt to locate the node
in the historic data stored by the data base. The call to
in the historic data stored by the data base. The call to to
`fetchNodeFromDB(hash)` does that work for us.
3. Finally if a filter exists, we check if it can supply the node. This is

View File

@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/WrappedSink.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
@@ -116,6 +117,7 @@ protected:
beast::Journal const j_;
AccountID const accountID_;
WAccountRoot account_;
XRPAmount preFeeBalance_{}; // Balance before fees.
public:
@@ -398,15 +400,6 @@ private:
static NotTEC
preflight2(PreflightContext const& ctx);
/** Universal validations
- Valid MPTAmount and XRPAmount
Do not try to call preflightUniversal from preflight() in derived classes. See
the description of invokePreflight for details.
*/
static NotTEC
preflightUniversal(PreflightContext const& ctx);
/** Check transaction-specific invariants only.
*
* Walks every modified ledger entry via visitInvariantEntry, then
@@ -472,9 +465,6 @@ Transactor::invokePreflight(PreflightContext const& ctx)
if (auto const ret = preflight1(ctx, T::getFlagsMask(ctx)))
return ret;
if (auto const ret = preflightUniversal(ctx))
return ret;
if (auto const ret = T::preflight(ctx))
return ret;

View File

@@ -1,27 +0,0 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <memory>
namespace xrpl {
class ValidBookDirectory
{
bool badBookDirectory_ = false;
hash_set<uint256> rootIndexes_;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
} // namespace xrpl

View File

@@ -6,7 +6,6 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/tx/invariants/AMMInvariant.h>
#include <xrpl/tx/invariants/DirectoryInvariant.h>
#include <xrpl/tx/invariants/FreezeInvariant.h>
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
#include <xrpl/tx/invariants/LoanInvariant.h>
@@ -373,21 +372,6 @@ public:
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
/** Verify that MPT/XRP STAmounts are canonical in any ledger entries left after the
* transaction applies.
*/
class ValidAmounts
{
std::vector<std::shared_ptr<SLE const>> afterEntries_;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
};
// additional invariant checks can be declared above and then added to this
// tuple
using InvariantChecks = std::tuple<
@@ -409,16 +393,13 @@ using InvariantChecks = std::tuple<
ValidMPTIssuance,
ValidPermissionedDomain,
ValidPermissionedDEX,
ValidBookDirectory,
ValidAMM,
NoModifiedUnmodifiableFields,
ValidPseudoAccounts,
ValidLoanBroker,
ValidLoan,
ValidVault,
ValidMPTPayment,
ValidAmounts,
ValidMPTTransfer>;
ValidMPTPayment>;
/**
* @brief get a tuple of all invariant checks

View File

@@ -2,13 +2,10 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
#include <memory>
#include <vector>
namespace xrpl {
@@ -23,18 +20,6 @@ class ValidMPTIssuance
// MPToken by an issuer
bool mptCreatedByIssuer_ = false;
/// sfReferenceHolding is intended to be set exactly once at vault
/// creation and immutable thereafter; true when that rule was violated.
bool referenceHoldingSetOnCreate_ = false;
/// True when sfReferenceHolding was mutated on an existing MPTokenIssuance.
bool referenceHoldingMutated_ = false;
/// MPTokens and RippleStates deleted during apply. finalize() checks each
/// holder's AccountRoot to detect vault pseudo-account holdings deleted
/// outside VaultDelete. All these checks are gated on fixCleanup3_2_0.
std::vector<std::shared_ptr<SLE const>> deletedHoldings_;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
@@ -71,42 +56,4 @@ public:
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
class ValidMPTTransfer
{
struct Value
{
std::optional<std::uint64_t> amtBefore;
std::optional<std::uint64_t> amtAfter;
};
// MPTID: {holder: Value}
hash_map<uint192, hash_map<AccountID, Value>> amount_;
// Deleted MPToken
// MPToken key: true if MPTAuthorized is set
hash_map<uint256, bool> deletedAuthorized_;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
private:
/**
* @brief Check whether a holder is authorized to send or receive an MPToken.
*
* Deleted MPToken SLEs are no longer present in the view by the time
* finalize() runs, so their authorization state is captured during
* visitEntry() and stored in deletedAuthorized_. For deleted MPTokens,
* returns true if reqAuth is false or lsfMPTAuthorized was set at deletion.
* For existing MPTokens, returns the result of requireAuth()
*/
[[nodiscard]] bool
isAuthorized(
ReadView const& view,
MPTID const& mptid,
AccountID const& holder,
bool requireAuth) const;
};
} // namespace xrpl

View File

@@ -10,10 +10,9 @@ namespace xrpl {
class ValidPermissionedDEX
{
bool regularOffersOld_ = false; // pre-fixCleanup3_2_0: also flags deleted offers
bool regularOffers_ = false; // post-fixCleanup3_2_0: excludes deleted offers
bool badHybridsOld_ = false; // pre-fixCleanup3_1_3: missing field/domain or size > 1
bool badHybrids_ = false; // post-fixCleanup3_1_3: also catches size == 0 (size != 1)
bool regularOffers_ = false;
bool badHybridsOld_ = false; // pre-fixCleanup3_1_3: missing field/domain or size > 1
bool badHybrids_ = false; // post-fixCleanup3_1_3: also catches size == 0 (size != 1)
hash_set<uint256> domains_;
public:

View File

@@ -21,7 +21,7 @@ class TOffer
private:
SLE::pointer entry_;
Quality quality_{};
AccountID accountID_;
AccountID account_;
Asset assetIn_;
Asset assetOut_;
@@ -53,7 +53,7 @@ public:
[[nodiscard]] AccountID const&
owner() const
{
return accountID_;
return account_;
}
/** Returns the in and out amounts.
@@ -122,7 +122,7 @@ public:
isFunded() const
{
// Offer owner is issuer; they have unlimited funds if IOU
return accountID_ == assetOut_.getIssuer() && assetOut_.holds<Issue>();
return account_ == assetOut_.getIssuer() && assetOut_.holds<Issue>();
}
static std::pair<std::uint32_t, std::uint32_t>
@@ -159,7 +159,7 @@ public:
template <StepAmount TIn, StepAmount TOut>
TOffer<TIn, TOut>::TOffer(SLE::pointer entry, Quality quality)
: entry_(std::move(entry)), quality_(quality), accountID_(entry_->getAccountID(sfAccount))
: entry_(std::move(entry)), quality_(quality), account_(entry_->getAccountID(sfAccount))
{
auto const tp = entry_->getFieldAmount(sfTakerPays);
auto const tg = entry_->getFieldAmount(sfTakerGets);

View File

@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/UintTypes.h>
@@ -19,9 +20,10 @@ checkFreeze(
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
// check freeze
if (auto sle = view.read(keylet::account(dst)))
AccountRoot const acctDst(dst, view);
if (acctDst)
{
if (sle->isFlag(lsfGlobalFreeze))
if (acctDst->isFlag(lsfGlobalFreeze))
{
return terNO_LINE;
}
@@ -43,10 +45,9 @@ checkFreeze(
if (view.rules().enabled(fixFrozenLPTokenTransfer))
{
if (auto const sleDst = view.read(keylet::account(dst));
sleDst && sleDst->isFieldPresent(sfAMMID))
if (AccountRoot const acctDst2(dst, view); acctDst2 && acctDst2->isFieldPresent(sfAMMID))
{
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
auto const sleAmm = view.read(keylet::amm(acctDst2->at(sfAMMID)));
if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE

View File

@@ -22,7 +22,7 @@ public:
private:
// Tx account owner is required to get the AMM trading fee in BookStep
AccountID accountID_;
AccountID account_;
// true if payment has multiple paths
bool multiPath_{false};
// Is true if AMM offer is consumed during a payment engine iteration.
@@ -31,8 +31,7 @@ private:
std::uint16_t ammIters_{0};
public:
AMMContext(AccountID const& account, bool multiPath)
: accountID_(account), multiPath_(multiPath)
AMMContext(AccountID const& account, bool multiPath) : account_(account), multiPath_(multiPath)
{
}
~AMMContext() = default;
@@ -81,7 +80,7 @@ public:
[[nodiscard]] AccountID
account() const
{
return accountID_;
return account_;
}
/** Strand execution may fail. Reset the flag at the start

View File

@@ -85,7 +85,6 @@ private:
Keylet const& offerIndex,
STAmount const& saTakerPays,
STAmount const& saTakerGets,
std::uint64_t openRate,
std::function<void(SLE::ref, std::optional<uint256>)> const& setDir);
};

View File

@@ -16,9 +16,6 @@ public:
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static bool
checkExtraFeatures(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);

View File

@@ -9,7 +9,6 @@ class LedgerStateFix : public Transactor
public:
enum class FixType : std::uint16_t {
NfTokenPageLink = 1,
BookExchangeRate = 2,
};
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;

View File

@@ -6,28 +6,23 @@
namespace xrpl {
// NOLINTBEGIN(readability-redundant-member-init)
struct MPTCreateArgs
{
std::optional<XRPAmount> priorBalance;
AccountID const& account;
std::uint32_t sequence = 0;
std::uint32_t flags = 0;
std::optional<std::uint64_t> maxAmount = std::nullopt;
std::optional<std::uint8_t> assetScale = std::nullopt;
std::optional<std::uint16_t> transferFee = std::nullopt;
std::optional<std::uint64_t> maxAmount =
std::nullopt; // NOLINT(readability-redundant-member-init)
std::optional<std::uint8_t> assetScale =
std::nullopt; // NOLINT(readability-redundant-member-init)
std::optional<std::uint16_t> transferFee =
std::nullopt; // NOLINT(readability-redundant-member-init)
std::optional<Slice> const& metadata{};
std::optional<uint256> domainId = std::nullopt;
std::optional<std::uint32_t> mutableFlags = std::nullopt;
// Set only by callers that issue an MPT representing a wrapped asset
// (e.g. VaultCreate's share token). The keylet must point to an
// existing MPToken or RippleState owned by `account`. Surfaces on
// the resulting MPTokenIssuance via the optional sfReferenceHolding
// field. Used by readers (canTransfer, canTrade, freezing) to
// inherit the underlying asset's transferability.
std::optional<uint256> referenceHolding = std::nullopt;
std::optional<uint256> domainId = std::nullopt; // NOLINT(readability-redundant-member-init)
std::optional<std::uint32_t> mutableFlags =
std::nullopt; // NOLINT(readability-redundant-member-init)
};
// NOLINTEND(readability-redundant-member-init)
class MPTokenIssuanceCreate : public Transactor
{

View File

@@ -1,175 +0,0 @@
# Linux Packaging
This directory contains all files needed to build RPM and Debian packages for `xrpld`.
## Directory layout
```
package/
build_pkg.sh Staging and build script (called by CMake targets and CI)
rpm/
xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define)
debian/ Debian control files (control, rules, install, links, conffiles, ...)
shared/
xrpld.service systemd unit file (used by both RPM and DEB)
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
xrpld.tmpfiles tmpfiles.d config (used by both RPM and DEB)
xrpld.logrotate logrotate config (installed to /etc/logrotate.d/xrpld)
update-xrpld auto-update script (installed to /usr/libexec/xrpld/, run by update-xrpld.timer)
```
## Prerequisites
Packaging targets and their container images are declared in
[`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json)
via a `"package": true` field on specific os entries. Today only
`linux/amd64` is emitted; the architecture is hardcoded in `generate.py`
and the workflow runner. The package format
(deb or rpm) is inferred at build time from the container's package manager
(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as
`ghcr.io/xrplf/ci/{distro}-{version}:{compiler}-{cver}-sha-{image_sha}`
the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in
`linux.json` and both CI and local builds pick up the new image with no
workflow edits.
| Package type | Image (derived from `linux.json`) | Tool required |
| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- |
| RPM | `ghcr.io/xrplf/ci/rhel-9:gcc-12-sha-<git_sha>` | `rpmbuild` |
| DEB | `ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12-sha-<git_sha>` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` |
To print the exact image tags for the current `linux.json`:
```bash
./.github/scripts/strategy-matrix/generate.py --packaging --config=.github/scripts/strategy-matrix/linux.json
```
## Building packages
### Via CI
Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call
`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of
`{artifact_name, os}` entries, then fan out to
`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld`
binary artifact, detects the package format from the container, and calls
`build_pkg.sh` directly — no CMake configure or build step is needed inside
the packaging job.
### Locally (mirrors CI)
With an `xrpld` binary already built at `build/xrpld`, run the packaging step
inside the same container CI uses. The image tag is derived from `linux.json`
so you don't need to hardcode a SHA.
```bash
# From the repo root. Pick any image flagged with `"package": true` in
# linux.json; the package format is inferred from the container's package
# manager. Example for the rpm-producing image:
IMAGE=$(jq -r '
.os | map(select(.package == true))[0] |
"ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)"
' .github/scripts/strategy-matrix/linux.json)
VERSION=2.4.0-local
PKG_RELEASE=1
docker run --rm \
-v "$(pwd):/src" \
-w /src \
"$IMAGE" \
./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE"
# Output:
# build/debbuild/*.deb (DEB + dbgsym .ddeb)
# build/rpmbuild/RPMS/x86_64/*.rpm
```
### Via CMake (host-side target)
If you run CMake configure on a host that has `rpmbuild` or `dpkg-buildpackage`
installed natively, you can use the CMake target directly — no container
needed, but the host toolchain replaces the pinned CI image:
```bash
cmake \
-Dxrpld=ON \
-Dxrpld_version=2.4.0-local \
-Dtests=OFF \
..
cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL
```
The `cmake/XrplPackaging.cmake` module defines the target only if at least one
of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the
package format from the host's package manager. The packaging script installs
to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of
`CMAKE_INSTALL_PREFIX`.
## How `build_pkg.sh` works
`build_pkg.sh` accepts long-form flags, each of which can also be set via an
environment variable. Flags override env vars; env vars override the built-in
defaults. Run `./package/build_pkg.sh --help` for the same table:
| Flag | Env var | Default | Purpose |
| -------------------------- | ------------------- | ----------------------------- | ----------------------------------- |
| `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root |
| `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` |
| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` |
| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number |
| `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp |
The package format (`deb` or `rpm`) is inferred from the host's package
manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those
fail early.
Flags are for explicit invocation; environment variables are intended for
CMake/systemd/CI integration. The CI workflow and the CMake `package` target
both invoke `build_pkg.sh` with no flags, configuring it entirely via env
(see `cmake/XrplPackaging.cmake`).
It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls
`stage_common()` to copy the binary, config files, and shared support files
into the staging area, and invokes the platform build tool.
### RPM
1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory.
2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`.
3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files.
4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm`
### DEB
1. Creates a staging source tree at `debbuild/source/` inside the build directory.
2. Stages the binary, configs, `README.md`, and `LICENSE.md`.
3. Copies `package/debian/` control files into `debbuild/source/debian/`.
4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically.
5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`).
6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands.
7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package)
## Post-build verification
```bash
# DEB
dpkg-deb -c debbuild/*.deb | grep -E 'systemd|sysusers|tmpfiles'
lintian -I debbuild/*.deb
# RPM
rpm -qlp rpmbuild/RPMS/x86_64/*.rpm
```
## Reproducibility
The following environment variables improve build reproducibility. They are not
set automatically by `build_pkg.sh`; set them manually if needed:
```bash
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
export TZ=UTC
export LC_ALL=C.UTF-8
export GZIP=-n
export DEB_BUILD_OPTIONS="noautodbgsym reproducible=+fixfilepath"
```

View File

@@ -1,196 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Build an RPM or Debian package from a pre-built xrpld binary.
#
# Flags override env vars; env vars override defaults. Env vars are intended
# for CMake/systemd/CI integration; flags are for explicit invocation.
usage() {
cat <<'EOF'
Usage: build_pkg.sh [options]
Options (each can also be set via the env var shown):
--src-dir DIR repo root [SRC_DIR; default: $PWD]
--build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build]
--pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version]
--pkg-release N package release number [PKG_RELEASE; default: 1]
--source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime]
-h, --help show this help and exit
EOF
}
need_arg() {
if [[ $# -lt 2 || "$2" == --* ]]; then
echo "Missing value for $1" >&2
exit 2
fi
}
# Seed from env. CLI parsing below overrides these directly.
SRC_DIR="${SRC_DIR:-}"
BUILD_DIR="${BUILD_DIR:-}"
PKG_VERSION="${PKG_VERSION:-}"
PKG_RELEASE="${PKG_RELEASE:-}"
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--src-dir) need_arg "$@"; SRC_DIR="$2"; shift 2 ;;
--build-dir) need_arg "$@"; BUILD_DIR="$2"; shift 2 ;;
--pkg-version) need_arg "$@"; PKG_VERSION="$2"; shift 2 ;;
--pkg-release) need_arg "$@"; PKG_RELEASE="$2"; shift 2 ;;
--source-date-epoch) need_arg "$@"; SOURCE_DATE_EPOCH="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done
SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)"
BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)"
PKG_RELEASE="${PKG_RELEASE:-1}"
if [[ -z "${PKG_VERSION}" ]]; then
PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')"
fi
if [[ -z "${PKG_VERSION}" ]]; then
echo "PKG_VERSION is empty (not provided and could not be derived)." >&2
exit 1
fi
VERSION="${PKG_VERSION}"
if command -v apt-get >/dev/null 2>&1; then
pkg_type=deb
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
pkg_type=rpm
else
echo "Cannot infer pkg_type: no apt-get, dnf, or yum on PATH." >&2
exit 1
fi
if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then
if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)"
else
SOURCE_DATE_EPOCH="$(date +%s)"
fi
fi
export SOURCE_DATE_EPOCH
CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")"
# Split VERSION at the first '-' into base and optional pre-release suffix.
# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1").
VER_BASE="${VERSION%%-*}"
VER_SUFFIX="${VERSION#*-}"
[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX=""
# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). The RPM
# Release field forbids '-', and the convention here is single-token suffixes
# like b1 or rc2. Fail early with a clear message rather than letting either
# rpmbuild blow up or silently mangling dashes into dots.
if [[ "${VER_SUFFIX}" == *-* ]]; then
echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2
echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2
exit 1
fi
SHARED="${SRC_DIR}/package/shared"
DEBIAN_DIR="${SRC_DIR}/package/debian"
# Stage files that both packaging systems consume using the same filenames.
stage_common() {
local dest="$1"
mkdir -p "${dest}"
cp "${BUILD_DIR}/xrpld" "${dest}/xrpld"
cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg"
cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt"
cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md"
cp "${SRC_DIR}/README.md" "${dest}/README.md"
cp "${SHARED}/xrpld.service" "${dest}/xrpld.service"
cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles"
cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
cp "${SHARED}/update-xrpld" "${dest}/update-xrpld"
cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service"
cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer"
cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset"
}
build_rpm() {
local topdir="${BUILD_DIR}/rpmbuild"
rm -rf "${topdir}"
mkdir -p "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
stage_common "${topdir}/SOURCES"
# RPM Version can't contain '-'. A pre-release goes in Release with a
# leading "0." so 3.2.0-b1 sorts before the final 3.2.0-<pkg_release>.
# The order is "0.<pkg_release>.<suffix>" (e.g. 0.1.b6) — the Fedora/EPEL
# convention. Reversing to "0.<suffix>.<pkg_release>" (e.g. 0.b6.1) breaks
# rpmvercmp against the former because numeric segments outrank alphabetic
# ones, so "0.1.b5" would sort newer than "0.b6.1".
local rpm_release="${PKG_RELEASE}"
[[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${PKG_RELEASE}.${VER_SUFFIX}"
set -x
rpmbuild -bb \
--define "_topdir ${topdir}" \
--define "xrpld_version ${VER_BASE}" \
--define "xrpld_release ${rpm_release}" \
"${topdir}/SPECS/xrpld.spec"
}
build_deb() {
local staging="${BUILD_DIR}/debbuild/source"
rm -rf "${staging}"
mkdir -p "${staging}"
stage_common "${staging}"
cp -r "${DEBIAN_DIR}" "${staging}/debian"
# Debhelper auto-discovers these only from debian/.
cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service"
cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers"
cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate"
cp "${staging}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service"
cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer"
# Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0.
local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}"
# Derive release channel from the version suffix:
# (none) -> stable (tagged release)
# b0 -> develop (develop-branch build)
# b<N>, rc<N> -> unstable (pre-release)
local deb_distribution
case "${VER_SUFFIX}" in
"") deb_distribution="stable" ;;
b0) deb_distribution="develop" ;;
*) deb_distribution="unstable" ;;
esac
cat > "${staging}/debian/changelog" <<EOF
xrpld (${deb_full_version}) ${deb_distribution}; urgency=medium
* Release ${VERSION}.
-- XRPL Foundation <contact@xrplf.org> ${CHANGELOG_DATE}
EOF
chmod +x "${staging}/debian/rules"
set -x
( cd "${staging}" && dpkg-buildpackage -b --no-sign -d )
}
"build_${pkg_type}"

View File

@@ -1,23 +0,0 @@
Source: xrpld
Section: net
Priority: optional
Maintainer: XRPL Foundation <contact@xrplf.org>
Rules-Requires-Root: no
Build-Depends:
debhelper-compat (= 13)
Standards-Version: 4.7.0
Homepage: https://github.com/XRPLF/rippled
Vcs-Git: https://github.com/XRPLF/rippled.git
Vcs-Browser: https://github.com/XRPLF/rippled
Package: xrpld
Section: net
Priority: optional
Architecture: any
Depends:
${shlibs:Depends},
${misc:Depends}
Description: XRP Ledger daemon
Reference implementation of the XRP Ledger protocol.
Participates in the peer-to-peer network, processes transactions,
and maintains a local ledger copy.

View File

@@ -1,18 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: rippled
Source: https://github.com/XRPLF/rippled
Files: *
Copyright: 2011-present, the XRP Ledger developers
License: ISC
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,27 +0,0 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1
%:
dh $@
override_dh_auto_configure override_dh_auto_build override_dh_auto_test:
@:
override_dh_installsystemd:
dh_installsystemd --no-stop-on-upgrade xrpld.service
dh_installsystemd --name=update-xrpld --no-start update-xrpld.service update-xrpld.timer
execute_before_dh_installtmpfiles:
dh_installsysusers
override_dh_installsysusers:
override_dh_install:
install -D -m 0755 xrpld debian/xrpld/usr/bin/xrpld
install -D -m 0644 xrpld.cfg debian/xrpld/etc/xrpld/xrpld.cfg
install -D -m 0644 validators.txt debian/xrpld/etc/xrpld/validators.txt
install -D -m 0755 update-xrpld debian/xrpld/usr/libexec/xrpld/update-xrpld
override_dh_dwz:
@:

View File

@@ -1 +0,0 @@
3.0 (quilt)

View File

@@ -1,2 +0,0 @@
README.md
LICENSE.md

View File

@@ -1,2 +0,0 @@
# Legacy compat symlinks (remove next major release)
usr/bin/xrpld usr/local/bin/rippled

View File

@@ -1,100 +0,0 @@
Name: xrpld
Version: %{xrpld_version}
Release: %{xrpld_release}%{?dist}
Summary: XRP Ledger daemon
License: ISC
URL: https://github.com/XRPLF/rippled
ExclusiveArch: x86_64 aarch64
BuildRequires: systemd-rpm-macros
%undefine _debugsource_packages
%debug_package
%build_mtime_policy clamp_to_source_date_epoch
%{?systemd_requires}
%{?sysusers_requires_compat}
%description
xrpld is the reference implementation of the XRP Ledger protocol. It
participates in the peer-to-peer XRP Ledger network, processes
transactions, and maintains the ledger database.
%prep
:
%build
:
%install
install -Dm0755 %{_sourcedir}/xrpld %{buildroot}%{_bindir}/%{name}
install -Dm0644 %{_sourcedir}/xrpld.cfg %{buildroot}%{_sysconfdir}/%{name}/xrpld.cfg
install -Dm0644 %{_sourcedir}/validators.txt %{buildroot}%{_sysconfdir}/%{name}/validators.txt
# systemd units, sysusers, tmpfiles, preset
install -Dm0644 %{_sourcedir}/xrpld.service %{buildroot}%{_unitdir}/xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.service %{buildroot}%{_unitdir}/update-xrpld.service
install -Dm0644 %{_sourcedir}/update-xrpld.timer %{buildroot}%{_unitdir}/update-xrpld.timer
install -Dm0644 %{_sourcedir}/xrpld.sysusers %{buildroot}%{_sysusersdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/xrpld.tmpfiles %{buildroot}%{_tmpfilesdir}/xrpld.conf
install -Dm0644 %{_sourcedir}/50-xrpld.preset %{buildroot}%{_presetdir}/50-xrpld.preset
# Logrotate config
install -Dm0644 %{_sourcedir}/xrpld.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
# Update helper
install -Dm0755 %{_sourcedir}/update-xrpld %{buildroot}%{_libexecdir}/%{name}/update-xrpld
# Docs
install -Dm0644 %{_sourcedir}/LICENSE.md %{buildroot}%{_docdir}/%{name}/LICENSE.md
install -Dm0644 %{_sourcedir}/README.md %{buildroot}%{_docdir}/%{name}/README.md
# Legacy compatibility for pre-FHS package layouts.
# TODO: remove after rippled fully deprecated.
install -d %{buildroot}/usr/local/bin
ln -s %{_bindir}/%{name} %{buildroot}/usr/local/bin/rippled
%pre
%sysusers_create_package %{name} %{_sourcedir}/xrpld.sysusers
%post
systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%systemd_post xrpld.service update-xrpld.timer
%preun
%systemd_preun xrpld.service update-xrpld.timer
%postun
%systemd_postun_with_restart xrpld.service
%files
%license %{_docdir}/%{name}/LICENSE.md
%doc %{_docdir}/%{name}/README.md
%dir %{_sysconfdir}/%{name}
%dir %{_libexecdir}/%{name}
%{_bindir}/%{name}
%config(noreplace) %{_sysconfdir}/%{name}/xrpld.cfg
%config(noreplace) %{_sysconfdir}/%{name}/validators.txt
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
%{_libexecdir}/%{name}/update-xrpld
%{_unitdir}/xrpld.service
%{_unitdir}/update-xrpld.service
%{_unitdir}/update-xrpld.timer
%{_presetdir}/50-xrpld.preset
%{_sysusersdir}/xrpld.conf
%{_tmpfilesdir}/xrpld.conf
%ghost %dir /var/lib/%{name}
%ghost %dir /var/log/%{name}
# Legacy compatibility for pre-FHS package layouts.
# TODO: remove after rippled fully deprecated.
/usr/local/bin/rippled

View File

@@ -1,4 +0,0 @@
# /usr/lib/systemd/system-preset/50-xrpld.preset
enable xrpld.service
# Don't enable automatic updates
disable update-xrpld.timer

View File

@@ -1,152 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Optional: also write logs to a legacy file in addition to journald.
# By default, this script logs to systemd/journald, viewable via:
# journalctl -t update-xrpld
#
# Uncomment the line below if you need a flat file for compatibility with
# external tooling, manual inspection, or environments where journald logs
# are not persisted or easily accessible.
#
# Note: This duplicates all output (stdout/stderr) to both journald and the file.
# It is generally not needed on modern systems and may cause log file growth
# if left enabled long-term.
#
# Requires /var/log/xrpld/ to exist and be writable by the service (root).
#
# exec > >(tee -a /var/log/xrpld/update.log) 2>&1
PATH=/usr/sbin:/usr/bin:/sbin:/bin
PKG_NAME=${PKG_NAME:-xrpld}
log() {
# If running under systemd/journald, let it handle timestamps.
if [[ -n "${JOURNAL_STREAM:-}" ]]; then
printf '%s\n' "$*"
else
printf '%s %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*"
fi
}
require_root() {
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
log "RESULT: failed reason=not-root"
exit 1
fi
}
get_installed_version() {
if command -v dpkg-query >/dev/null 2>&1; then
dpkg-query -W -f='${Version}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
elif command -v rpm >/dev/null 2>&1; then
rpm -q --qf '%{VERSION}-%{RELEASE}' "$PKG_NAME" 2>/dev/null || printf 'unknown'
else
printf 'unknown'
fi
}
trap 'log "RESULT: failed reason=script-error exit_code=$?"' ERR
apt_can_update() {
apt-get update -qq
apt-get -s --only-upgrade install "$PKG_NAME" 2>/dev/null | grep -q "^Inst ${PKG_NAME}\b"
}
apt_apply_update() {
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
"$PKG_NAME"
}
get_rpm_pm() {
if command -v dnf >/dev/null 2>&1; then
printf 'dnf\n'
elif command -v yum >/dev/null 2>&1; then
printf 'yum\n'
else
return 1
fi
}
rpm_refresh_metadata() {
local pm=$1
if [[ "$pm" == "dnf" ]]; then
dnf makecache --refresh -q >/dev/null
else
yum clean expire-cache -q >/dev/null
fi
}
rpm_can_update() {
local pm=$1
rpm_refresh_metadata "$pm"
local rc=0
set +e
"$pm" check-update -q "$PKG_NAME" >/dev/null 2>&1
rc=$?
set -e
if [[ $rc -eq 100 ]]; then
return 0
elif [[ $rc -eq 0 ]]; then
return 1
else
log "$pm check-update failed with exit code ${rc}."
exit 1
fi
}
rpm_apply_update() {
local pm=$1
"$pm" update -y "$PKG_NAME"
}
restart_service() {
# Preserve the operator's prior service state: if xrpld was intentionally
# stopped before the update, don't bring it back up just because the
# auto-update timer fired.
if systemctl is-active --quiet "${PKG_NAME}.service"; then
systemctl restart "${PKG_NAME}.service"
log "${PKG_NAME} service restarted successfully."
else
log "${PKG_NAME} service was not running; skipping restart to preserve prior state."
fi
}
main() {
require_root
if command -v apt-get >/dev/null 2>&1; then
log "Checking for ${PKG_NAME} updates via apt"
if apt_can_update; then
log "Update available; installing."
apt_apply_update
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
local rpm_pm=""
if rpm_pm="$(get_rpm_pm)"; then
log "Checking for ${PKG_NAME} updates via ${rpm_pm}"
if rpm_can_update "$rpm_pm"; then
log "Update available; installing"
rpm_apply_update "$rpm_pm"
restart_service
log "RESULT: updated ${PKG_NAME}=$(get_installed_version)"
else
log "RESULT: no-update ${PKG_NAME}=$(get_installed_version)"
fi
return
fi
log "RESULT: failed reason=no-package-manager"
exit 1
}
main "$@"

View File

@@ -1,16 +0,0 @@
[Unit]
Description=Check for and install xrpld package updates
Documentation=man:systemd.service(5)
Wants=network-online.target
After=network-online.target
ConditionPathExists=/usr/libexec/xrpld/update-xrpld
ConditionPathExists=/usr/bin/xrpld
[Service]
Type=oneshot
ExecStart=/usr/bin/flock -n /run/lock/xrpld-update.lock /usr/libexec/xrpld/update-xrpld
StandardOutput=journal
StandardError=journal
SyslogIdentifier=update-xrpld
TimeoutStartSec=30min
PrivateTmp=true

View File

@@ -1,10 +0,0 @@
[Unit]
Description=Daily xrpld update check
[Timer]
OnCalendar=*-*-* 00:00:00
RandomizedDelaySec=24h
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -1,19 +0,0 @@
/var/log/xrpld/*.log {
daily
minsize 200M
rotate 7
nocreate
missingok
notifempty
compress
compresscmd /usr/bin/gzip
compressext .gz
postrotate
# Only signal the daemon if it's actually running; otherwise the RPC
# call returns a transport error and logrotate marks the rotation as
# failed, generating recurring errors on stopped nodes.
if systemctl is-active --quiet xrpld; then
/usr/bin/xrpld --conf /etc/xrpld/xrpld.cfg logrotate
fi
endscript
}

View File

@@ -1,22 +0,0 @@
[Unit]
Description=XRP Ledger Daemon
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=simple
ExecStart=/usr/bin/xrpld --net --silent --conf /etc/xrpld/xrpld.cfg
Restart=always
RestartSec=5s
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
User=xrpld
Group=xrpld
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

View File

@@ -1 +0,0 @@
u xrpld - "XRP Ledger daemon" /var/lib/xrpld /sbin/nologin

View File

@@ -1,2 +0,0 @@
d /var/lib/xrpld 0750 xrpld xrpld -
d /var/log/xrpld 0750 xrpld xrpld -

View File

@@ -108,7 +108,7 @@ public:
beast::Journal journal;
boost::asio::io_context& ioContext;
boost::asio::io_context& io_context;
boost::asio::strand<boost::asio::io_context::executor_type> strand;
boost::asio::ip::tcp::resolver resolver;
@@ -116,7 +116,7 @@ public:
std::mutex mut;
bool asyncHandlersCompleted{true};
std::atomic<bool> stopCalled;
std::atomic<bool> stop_called;
std::atomic<bool> stopped;
// Represents a unit of work for the resolver to do
@@ -138,10 +138,10 @@ public:
ResolverAsioImpl(boost::asio::io_context& ioContext, beast::Journal journal)
: journal(journal)
, ioContext(ioContext)
, io_context(ioContext)
, strand(boost::asio::make_strand(ioContext))
, resolver(ioContext)
, stopCalled(false)
, stop_called(false)
, stopped(true)
{
}
@@ -172,7 +172,7 @@ public:
start() override
{
XRPL_ASSERT(stopped == true, "xrpl::ResolverAsioImpl::start : stopped");
XRPL_ASSERT(stopCalled == false, "xrpl::ResolverAsioImpl::start : not stopping");
XRPL_ASSERT(stop_called == false, "xrpl::ResolverAsioImpl::start : not stopping");
if (stopped.exchange(false))
{
@@ -187,10 +187,10 @@ public:
void
stopAsync() override
{
if (!stopCalled.exchange(true))
if (!stop_called.exchange(true))
{
boost::asio::dispatch(
ioContext,
io_context,
boost::asio::bind_executor(
strand, std::bind(&ResolverAsioImpl::doStop, this, CompletionCounter(this))));
@@ -213,13 +213,13 @@ public:
void
resolve(std::vector<std::string> const& names, HandlerType const& handler) override
{
XRPL_ASSERT(stopCalled == false, "xrpl::ResolverAsioImpl::resolve : not stopping");
XRPL_ASSERT(stop_called == false, "xrpl::ResolverAsioImpl::resolve : not stopping");
XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::resolve : names non-empty");
// TODO NIKB use rvalue references to construct and move
// reducing cost.
boost::asio::dispatch(
ioContext,
io_context,
boost::asio::bind_executor(
strand,
std::bind(
@@ -231,7 +231,7 @@ public:
void
doStop(CompletionCounter)
{
XRPL_ASSERT(stopCalled == true, "xrpl::ResolverAsioImpl::doStop : stopping");
XRPL_ASSERT(stop_called == true, "xrpl::ResolverAsioImpl::doStop : stopping");
if (!stopped.exchange(true))
{
@@ -270,7 +270,7 @@ public:
handler(name, addresses);
boost::asio::post(
ioContext,
io_context,
boost::asio::bind_executor(
strand, std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this))));
}
@@ -324,7 +324,7 @@ public:
void
doWork(CompletionCounter)
{
if (stopCalled)
if (stop_called)
return;
// We don't have any work to do at this time
@@ -346,7 +346,7 @@ public:
JLOG(journal.error()) << "Unable to parse '" << name << "'";
boost::asio::post(
ioContext,
io_context,
boost::asio::bind_executor(
strand, std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this))));
@@ -371,7 +371,7 @@ public:
{
XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::doResolve : names non-empty");
if (!stopCalled)
if (!stop_called)
{
work.emplace_back(names, handler);
@@ -381,7 +381,7 @@ public:
if (!work.empty())
{
boost::asio::post(
ioContext,
io_context,
boost::asio::bind_executor(
strand,
std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this))));

View File

@@ -164,7 +164,7 @@ public:
private:
std::shared_ptr<StatsDCollectorImp> impl_;
std::string name_;
GaugeImpl::value_type lastValue_{0};
GaugeImpl::value_type last_value_{0};
GaugeImpl::value_type value_{0};
bool dirty_{false};
};
@@ -209,7 +209,7 @@ private:
Journal journal_;
IP::Endpoint address_;
std::string prefix_;
boost::asio::io_context ioContext_;
boost::asio::io_context io_context_;
std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> work_;
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
boost::asio::basic_waitable_timer<std::chrono::steady_clock> timer_;
@@ -232,10 +232,10 @@ public:
: journal_(journal)
, address_(std::move(address))
, prefix_(std::move(prefix))
, work_(boost::asio::make_work_guard(ioContext_))
, strand_(boost::asio::make_strand(ioContext_))
, timer_(ioContext_)
, socket_(ioContext_)
, work_(boost::asio::make_work_guard(io_context_))
, strand_(boost::asio::make_strand(io_context_))
, timer_(io_context_)
, socket_(io_context_)
, thread_(&StatsDCollectorImp::run, this)
{
}
@@ -306,7 +306,7 @@ public:
boost::asio::io_context&
getIoContext()
{
return ioContext_;
return io_context_;
}
std::string const&
@@ -325,7 +325,7 @@ public:
postBuffer(std::string&& buffer)
{
boost::asio::dispatch(
ioContext_,
io_context_,
boost::asio::bind_executor(
strand_, std::bind(&StatsDCollectorImp::doPostBuffer, this, std::move(buffer))));
}
@@ -465,14 +465,14 @@ public:
setTimer();
ioContext_.run();
io_context_.run();
// NOLINTNEXTLINE(bugprone-unused-return-value)
socket_.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec);
socket_.close();
ioContext_.poll();
io_context_.poll();
}
};
@@ -628,9 +628,9 @@ StatsDGaugeImpl::doSet(GaugeImpl::value_type value)
{
value_ = value;
if (value_ != lastValue_)
if (value_ != last_value_)
{
lastValue_ = value_;
last_value_ = value_;
dirty_ = true;
}
}

View File

@@ -25,7 +25,7 @@ Job::Job(
std::uint64_t index,
LoadMonitor& lm,
std::function<void()> const& job)
: type_(type), jobIndex_(index), job_(job), name_(name), queueTime_(clock_type::now())
: type_(type), jobIndex_(index), job_(job), name_(name), queue_time_(clock_type::now())
{
loadEvent_ = std::make_shared<LoadEvent>(std::ref(lm), name, false);
}
@@ -39,7 +39,7 @@ Job::getType() const
Job::clock_type::time_point const&
Job::queueTime() const
{
return queueTime_;
return queue_time_;
}
void

View File

@@ -36,7 +36,7 @@ JobQueue::JobQueue(
JLOG(journal_.info()) << "Using " << threadCount << " threads";
hook_ = collector_->makeHook(std::bind(&JobQueue::collect, this));
jobCount_ = collector_->makeGauge("job_count");
job_count_ = collector_->makeGauge("job_count");
{
std::scoped_lock const lock(mutex_);
@@ -66,7 +66,7 @@ void
JobQueue::collect()
{
std::scoped_lock const lock(mutex_);
jobCount_ = jobSet_.size();
job_count_ = jobSet_.size();
}
bool

View File

@@ -14,8 +14,8 @@ namespace xrpl {
BookDirs::BookDirs(ReadView const& view, Book const& book)
: view_(&view)
, root_(keylet::page(getBookBase(book)).key)
, nextQuality_(getQualityNext(root_))
, key_(view_->succ(root_, nextQuality_).value_or(beast::kZero))
, next_quality_(getQualityNext(root_))
, key_(view_->succ(root_, next_quality_).value_or(beast::kZero))
{
XRPL_ASSERT(root_ != beast::kZero, "xrpl::BookDirs::BookDirs : nonzero root");
if (key_ != beast::kZero)
@@ -35,7 +35,7 @@ BookDirs::begin() const -> BookDirs::const_iterator
auto it = BookDirs::const_iterator(*view_, root_, key_);
if (key_ != beast::kZero)
{
it.nextQuality_ = nextQuality_;
it.next_quality_ = next_quality_;
it.sle_ = sle_;
it.entry_ = entry_;
it.index_ = index_;
@@ -59,7 +59,7 @@ BookDirs::const_iterator::operator==(BookDirs::const_iterator const& other) cons
view_ == other.view_ && root_ == other.root_,
"xrpl::BookDirs::const_iterator::operator== : views and roots are "
"matching");
return entry_ == other.entry_ && curKey_ == other.curKey_ && index_ == other.index_;
return entry_ == other.entry_ && cur_key_ == other.cur_key_ && index_ == other.index_;
}
BookDirs::const_iterator::reference
@@ -78,18 +78,18 @@ BookDirs::const_iterator::operator++()
using beast::kZero;
XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index");
if (!cdirNext(*view_, curKey_, sle_, entry_, index_))
if (!cdirNext(*view_, cur_key_, sle_, entry_, index_))
{
if (index_ == 0)
curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero);
cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(kZero);
if (index_ != 0 || curKey_ == kZero)
if (index_ != 0 || cur_key_ == kZero)
{
curKey_ = key_;
cur_key_ = key_;
entry_ = 0;
index_ = kZero;
}
else if (!cdirFirst(*view_, curKey_, sle_, entry_, index_))
else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_))
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty");

View File

@@ -78,9 +78,9 @@ public:
OpenView::OpenView(OpenView const& rhs)
: ReadView(rhs)
, TxsRawView(rhs)
, monotonicResource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
, monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
kInitialBufferSize)}
, txs_{rhs.txs_, monotonicResource_.get()}
, txs_{rhs.txs_, monotonic_resource_.get()}
, rules_{rhs.rules_}
, header_{rhs.header_}
, base_{rhs.base_}
@@ -89,9 +89,9 @@ OpenView::OpenView(OpenView const& rhs)
, open_{rhs.open_} {};
OpenView::OpenView(OpenLedgerT, ReadView const* base, Rules rules, std::shared_ptr<void const> hold)
: monotonicResource_{
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(kInitialBufferSize)}
, txs_{monotonicResource_.get()}
, txs_{monotonic_resource_.get()}
, rules_(std::move(rules))
, header_(base->header())
, base_(base)
@@ -105,9 +105,9 @@ OpenView::OpenView(OpenLedgerT, ReadView const* base, Rules rules, std::shared_p
}
OpenView::OpenView(ReadView const* base, std::shared_ptr<void const> hold)
: monotonicResource_{
: monotonic_resource_{
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(kInitialBufferSize)}
, txs_{monotonicResource_.get()}
, txs_{monotonic_resource_.get()}
, rules_(base->rules())
, header_(base->header())
, base_(base)

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