mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-06 10:16:45 +00:00
Compare commits
103 Commits
pratik/ope
...
mvadari/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eab60b9cb | ||
|
|
ac794f695d | ||
|
|
d38f45701f | ||
|
|
b557403ab6 | ||
|
|
8c80a6aad5 | ||
|
|
9b1406ad46 | ||
|
|
1306d35e4d | ||
|
|
144835ca5d | ||
|
|
554c0baa0a | ||
|
|
7661483c4c | ||
|
|
6cb5842a05 | ||
|
|
bcd26397fb | ||
|
|
ffaf51043d | ||
|
|
cedebd5a86 | ||
|
|
700cd6a2b1 | ||
|
|
3d2e0788cc | ||
|
|
d7b6c8e098 | ||
|
|
d10bb71eea | ||
|
|
5dbbd9ba8d | ||
|
|
fb37cc2f42 | ||
|
|
fabf328485 | ||
|
|
dc2430ea86 | ||
|
|
0de37c2cad | ||
|
|
3d9eba73e8 | ||
|
|
dae0a53c20 | ||
|
|
ea3ca12894 | ||
|
|
7b27096aac | ||
|
|
c00491baa1 | ||
|
|
d616aff4e2 | ||
|
|
be9913f16f | ||
|
|
b99a7666ab | ||
|
|
683ff84ee0 | ||
|
|
9c9281e5d9 | ||
|
|
2d6450904d | ||
|
|
8269964c76 | ||
|
|
945b4f77cd | ||
|
|
03863d91ad | ||
|
|
737d128cca | ||
|
|
97629e7477 | ||
|
|
b0ca6adf7a | ||
|
|
e2238b13de | ||
|
|
27e9d0d695 | ||
|
|
b3829e5e61 | ||
|
|
50968111da | ||
|
|
fa77338bdc | ||
|
|
ffd96e354f | ||
|
|
e9287812ef | ||
|
|
819d3fcdfc | ||
|
|
dcdc5e1b52 | ||
|
|
c0895c6e2e | ||
|
|
5b0b1ff1f6 | ||
|
|
e9c4ed6a74 | ||
|
|
deaa23f4e5 | ||
|
|
638477c824 | ||
|
|
9b42e178a2 | ||
|
|
f41c02d486 | ||
|
|
a423813098 | ||
|
|
56c173a097 | ||
|
|
7900fa9ead | ||
|
|
0ffb3e2227 | ||
|
|
add3d7e68d | ||
|
|
c24432f43a | ||
|
|
1ccd84e43a | ||
|
|
1cc7424934 | ||
|
|
b5562cc81e | ||
|
|
e6369c0faa | ||
|
|
43caa7d47a | ||
|
|
e0f487bb2e | ||
|
|
a8987cf271 | ||
|
|
4157e3684c | ||
|
|
659f455335 | ||
|
|
3bc460951c | ||
|
|
d1dae53097 | ||
|
|
733dd51720 | ||
|
|
9d67db1843 | ||
|
|
29bb59698f | ||
|
|
3b2dcefb81 | ||
|
|
4e92ec2daf | ||
|
|
6afcf51dee | ||
|
|
7e62ecb63b | ||
|
|
ad2c359f21 | ||
|
|
96d4a69a37 | ||
|
|
14fbdd5a1c | ||
|
|
3c5e87888e | ||
|
|
8e218a6285 | ||
|
|
8a1adc4ec9 | ||
|
|
c9db124015 | ||
|
|
7056b43c5e | ||
|
|
ee370fefee | ||
|
|
e68d74cda4 | ||
|
|
89a3a6c379 | ||
|
|
f3a6d17b5b | ||
|
|
45531b1eaf | ||
|
|
fa8de2b47f | ||
|
|
6fcda8802e | ||
|
|
0cf42b150c | ||
|
|
74b010a905 | ||
|
|
265deccabe | ||
|
|
ea1146d413 | ||
|
|
5fa9bb53a8 | ||
|
|
d9bf263543 | ||
|
|
1f73da466c | ||
|
|
e9eb5e53c9 |
@@ -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)$"
|
||||
|
||||
|
||||
0
.github/scripts/levelization/generate.py
vendored
Executable file → Normal file
0
.github/scripts/levelization/generate.py
vendored
Executable file → Normal file
64
.github/scripts/strategy-matrix/generate.py
vendored
64
.github/scripts/strategy-matrix/generate.py
vendored
@@ -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")
|
||||
)
|
||||
|
||||
6
.github/scripts/strategy-matrix/linux.json
vendored
6
.github/scripts/strategy-matrix/linux.json
vendored
@@ -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",
|
||||
|
||||
12
.github/workflows/on-pr.yml
vendored
12
.github/workflows/on-pr.yml
vendored
@@ -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
|
||||
|
||||
23
.github/workflows/on-tag.yml
vendored
23
.github/workflows/on-tag.yml
vendored
@@ -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
|
||||
|
||||
9
.github/workflows/on-trigger.yml
vendored
9
.github/workflows/on-trigger.yml
vendored
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
99
.github/workflows/reusable-package.yml
vendored
99
.github/workflows/reusable-package.yml
vendored
@@ -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
|
||||
9
BUILD.md
9
BUILD.md
@@ -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:
|
||||
|
||||
@@ -23,11 +23,7 @@ include(CompilationEnv)
|
||||
|
||||
if(is_gcc)
|
||||
# GCC-specific fixes
|
||||
add_compile_options(
|
||||
-Wno-unknown-pragmas
|
||||
-Wno-subobject-linkage
|
||||
-Wno-error=deprecated-declarations
|
||||
)
|
||||
add_compile_options(-Wno-unknown-pragmas -Wno-subobject-linkage)
|
||||
# -Wno-subobject-linkage can be removed when we upgrade GCC version to at least 13.3
|
||||
elseif(is_clang)
|
||||
# Clang-specific fixes
|
||||
@@ -85,11 +81,7 @@ if(only_docs)
|
||||
endif()
|
||||
|
||||
include(deps/Boost)
|
||||
find_package(OpenSSL 3.5.6 REQUIRED)
|
||||
set_target_properties(
|
||||
OpenSSL::SSL
|
||||
PROPERTIES INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
|
||||
)
|
||||
|
||||
add_subdirectory(external/antithesis-sdk)
|
||||
find_package(date REQUIRED)
|
||||
find_package(ed25519 REQUIRED)
|
||||
@@ -142,7 +134,6 @@ endif()
|
||||
include(XrplCore)
|
||||
include(XrplProtocolAutogen)
|
||||
include(XrplInstall)
|
||||
include(XrplPackaging)
|
||||
include(XrplValidatorKeys)
|
||||
|
||||
if(tests)
|
||||
|
||||
@@ -168,13 +168,7 @@ def main():
|
||||
if not os.environ.get("TIDY"):
|
||||
return 0
|
||||
|
||||
repo_root = Path(
|
||||
subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
cwd=Path(__file__).parent,
|
||||
text=True,
|
||||
).strip()
|
||||
)
|
||||
repo_root = Path(__file__).parent.parent
|
||||
files = staged_files(repo_root)
|
||||
if not files:
|
||||
return 0
|
||||
|
||||
@@ -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]
|
||||
#
|
||||
|
||||
@@ -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 = ::
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -10,7 +10,7 @@
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
|
||||
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
|
||||
"openssl/3.5.6#13f8f30151479797ca8a7c014856f849%1775635548.661",
|
||||
"openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
|
||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
@@ -58,9 +58,6 @@
|
||||
],
|
||||
"lz4/[>=1.9.4 <2]": [
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
|
||||
],
|
||||
"sqlite3/3.44.2": [
|
||||
"sqlite3/3.49.1"
|
||||
]
|
||||
},
|
||||
"config_requires": []
|
||||
|
||||
@@ -4,8 +4,6 @@ from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
from conan import ConanFile
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
class Xrpl(ConanFile):
|
||||
name = "xrpl"
|
||||
@@ -33,7 +31,7 @@ class Xrpl(ConanFile):
|
||||
"grpc/1.78.1",
|
||||
"libarchive/3.8.7",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.5.6",
|
||||
"openssl/3.6.2",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.2",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_; });
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
53
include/xrpl/ledger/helpers/README.md
Normal file
53
include/xrpl/ledger/helpers/README.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
240
include/xrpl/ledger/helpers/SLEBase.h
Normal file
240
include/xrpl/ledger/helpers/SLEBase.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
backend_->sync();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NodeObject>>
|
||||
fetchBatch(std::vector<uint256> const& hashes);
|
||||
|
||||
void
|
||||
asyncFetch(
|
||||
uint256 const& hash,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -688,7 +688,6 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
({
|
||||
{sfLedgerFixType, SoeRequired},
|
||||
{sfOwner, SoeOptional},
|
||||
{sfBookDirectory, SoeOptional},
|
||||
}))
|
||||
|
||||
/** This transaction type creates a MPTokensIssuance instance */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
[] {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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_)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@ public:
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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}"
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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:
|
||||
@:
|
||||
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
||||
@@ -1,2 +0,0 @@
|
||||
README.md
|
||||
LICENSE.md
|
||||
@@ -1,2 +0,0 @@
|
||||
# Legacy compat symlinks (remove next major release)
|
||||
usr/bin/xrpld usr/local/bin/rippled
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
# /usr/lib/systemd/system-preset/50-xrpld.preset
|
||||
enable xrpld.service
|
||||
# Don't enable automatic updates
|
||||
disable update-xrpld.timer
|
||||
@@ -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 "$@"
|
||||
@@ -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
|
||||
@@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Daily xrpld update check
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 00:00:00
|
||||
RandomizedDelaySec=24h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
u xrpld - "XRP Ledger daemon" /var/lib/xrpld /sbin/nologin
|
||||
@@ -1,2 +0,0 @@
|
||||
d /var/lib/xrpld 0750 xrpld xrpld -
|
||||
d /var/log/xrpld 0750 xrpld xrpld -
|
||||
@@ -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))));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user