Compare commits

...

30 Commits

Author SHA1 Message Date
Michael Legleux
8bfc19d0e1 PR comments and more tweaks 2026-04-02 12:01:00 -07:00
Michael Legleux
08a42f5d52 pr comments 2026-03-31 12:35:01 -07:00
Michael Legleux
cf38fc54db more tweaks 2026-03-31 00:54:26 -07:00
Michael Legleux
9776d466ba cp lic/readme 2026-03-31 00:54:26 -07:00
Michael Legleux
b66178f6fc more fixes/updates 2026-03-31 00:54:26 -07:00
Michael Legleux
587f5d2f5e update download-artifacts version 2026-03-31 00:54:26 -07:00
Michael Legleux
cb94f83c7f handle tmpfiles in rpm 2026-03-31 00:54:25 -07:00
Michael Legleux
a9a4446674 address review comments 2026-03-31 00:54:25 -07:00
Michael Legleux
c001f703dc feat: Build Linux packages in GitHub 2026-03-31 00:54:25 -07:00
Ayaz Salikhov
5c8dfe5456 ci: Only publish docs in public repos (#6687) 2026-03-30 17:15:40 +00:00
Alex Kremer
ab8c168e3b chore: Enable remaining clang-tidy performance checks (#6648)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-30 17:08:47 +00:00
Jingchen
3a477e4d01 refactor: Address PR comments after the modularisation PRs (#6389)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-03-30 15:22:38 +00:00
Alex Kremer
96bfc32fe2 chore: Fix clang-tidy header filter (#6686) 2026-03-30 14:59:53 +00:00
dependabot[bot]
de671863e2 ci: [DEPENDABOT] bump actions/deploy-pages from 4.0.5 to 5.0.0 (#6684)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 14:09:57 +00:00
dependabot[bot]
e0cabb9f8c ci: [DEPENDABOT] bump codecov/codecov-action from 5.5.3 to 6.0.0 (#6685)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 13:57:32 +00:00
Pratik Mankawde
3d9c545f59 fix: Guard Coro::resume() against completed coroutines (#6608)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 18:52:18 +00:00
Vito Tumas
9b944ee8c2 refactor: Split LoanInvariant into LoanBrokerInvariant and LoanInvariant (#6674) 2026-03-27 18:35:42 +00:00
Ayaz Salikhov
509677abfd ci: Don't publish docs on release branches (#6673) 2026-03-26 14:11:37 +00:00
Jingchen
addc1e8e25 refactor: Make function naming in ServiceRegistry consistent (#6390)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-03-26 14:11:16 +00:00
Valentin Balaschenko
faf69da4b0 chore: Shorten job names to stay within Linux 15-char thread limit (#6669) 2026-03-26 14:10:51 +00:00
Vito Tumas
76e3b4fb0f fix: Improve loan invariant message (#6668) 2026-03-26 12:40:26 +00:00
Ayaz Salikhov
e8bdbf975a ci: Upload artifacts only in public repositories (#6670) 2026-03-26 12:37:37 +00:00
Ayaz Salikhov
2c765f6eb0 ci: Add conflicting-pr workflow (#6656)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-03-26 00:18:17 +00:00
Mayukha Vadari
a9269fa846 chore: Add more AI tools to .gitignore (#6658)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-03-25 23:55:50 +00:00
Jingchen
15fd9feae5 chore: Show warning message if user may need to connect to VPN (#6619)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2026-03-25 23:54:49 +00:00
Vito Tumas
b9d07730f3 feat: Add placeholder amendment for assorted bug fixes (#6652) 2026-03-25 23:54:33 +00:00
Ayaz Salikhov
85b65c8e9a chore: Update sqlite3->3.51.0, protobuf->6.33.5, openssl->3.6.1, grpc->1.78.1 (#6653) 2026-03-25 18:22:50 +00:00
Jingchen
8f182e825a refactor: Modularise ledger (#6536)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
Co-authored-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-03-25 16:32:45 +00:00
Ayaz Salikhov
cd78569d94 chore: Use unpatched version of soci (#6649) 2026-03-25 16:06:31 +00:00
Mayukha Vadari
2c7af360c2 fix: Remove unused/unreachable transactor code (#6612) 2026-03-25 16:02:14 +00:00
286 changed files with 3095 additions and 1751 deletions

View File

@@ -1,4 +1,6 @@
---
# This entire group of checks was applied to all cpp files but not all header files.
# ---
Checks: "-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
@@ -8,26 +10,26 @@ Checks: "-*,
bugprone-chained-comparison,
bugprone-compare-pointer-to-member-virtual-function,
bugprone-copy-constructor-init,
bugprone-crtp-constructor-accessibility,
# bugprone-crtp-constructor-accessibility, # has issues
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-empty-catch,
# bugprone-empty-catch, # has issues
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-inaccurate-erase,
bugprone-inc-dec-in-conditions,
bugprone-incorrect-enable-if,
bugprone-incorrect-roundings,
bugprone-infinite-loop,
bugprone-integer-division,
# bugprone-forward-declaration-namespace, # has issues
# bugprone-inaccurate-erase,
# bugprone-inc-dec-in-conditions,
# bugprone-incorrect-enable-if,
# bugprone-incorrect-roundings,
# bugprone-infinite-loop,
# bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
# bugprone-macro-parentheses, # has issues
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multi-level-implicit-pointer-conversion,
# bugprone-multi-level-implicit-pointer-conversion, # has issues
bugprone-multiple-new-in-one-expression,
bugprone-multiple-statement-macro,
bugprone-no-escape,
@@ -37,13 +39,13 @@ Checks: "-*,
bugprone-pointer-arithmetic-on-polymorphic-object,
bugprone-posix-return,
bugprone-redundant-branch-condition,
bugprone-reserved-identifier,
bugprone-return-const-ref-from-parameter,
# bugprone-reserved-identifier, # has issues
# bugprone-return-const-ref-from-parameter, # has issues
bugprone-shared-ptr-array-mismatch,
bugprone-signal-handler,
bugprone-signed-char-misuse,
bugprone-sizeof-container,
bugprone-sizeof-expression,
# bugprone-sizeof-expression, # has issues
bugprone-spuriously-wake-up-functions,
bugprone-standalone-empty,
bugprone-string-constructor,
@@ -60,7 +62,7 @@ Checks: "-*,
bugprone-suspicious-string-compare,
bugprone-suspicious-stringview-data-usage,
bugprone-swapped-arguments,
bugprone-switch-missing-default-case,
# bugprone-switch-missing-default-case, # has issues
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
@@ -71,61 +73,66 @@ Checks: "-*,
bugprone-unhandled-self-assignment,
bugprone-unique-ptr-array-mismatch,
bugprone-unsafe-functions,
bugprone-use-after-move,
# bugprone-use-after-move, # has issues
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-unused-local-non-trivial-variable,
bugprone-virtual-near-miss,
cppcoreguidelines-init-variables,
cppcoreguidelines-misleading-capture-default-by-value,
# cppcoreguidelines-init-variables, # has issues
# cppcoreguidelines-misleading-capture-default-by-value, # has issues
cppcoreguidelines-no-suspend-with-lock,
cppcoreguidelines-pro-type-member-init,
# cppcoreguidelines-pro-type-member-init, # has issues
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-rvalue-reference-param-not-moved,
cppcoreguidelines-use-default-member-init,
cppcoreguidelines-virtual-class-destructor,
# cppcoreguidelines-rvalue-reference-param-not-moved, # has issues
# cppcoreguidelines-use-default-member-init, # has issues
# cppcoreguidelines-virtual-class-destructor, # has issues
hicpp-ignored-remove-result,
misc-definitions-in-headers,
# misc-definitions-in-headers, # has issues
misc-header-include-cycle,
misc-misplaced-const,
misc-static-assert,
misc-throw-by-value-catch-by-reference,
# misc-throw-by-value-catch-by-reference, # has issues
misc-unused-alias-decls,
misc-unused-using-decls,
modernize-deprecated-headers,
modernize-make-shared,
modernize-make-unique,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
performance-trivially-destructible,
readability-avoid-nested-conditional-operator,
readability-avoid-return-with-void-value,
readability-braces-around-statements,
readability-const-return-type,
readability-container-contains,
readability-container-size-empty,
readability-convert-member-functions-to-static,
# readability-avoid-nested-conditional-operator, # has issues
# readability-avoid-return-with-void-value, # has issues
# readability-braces-around-statements, # has issues
# readability-const-return-type, # has issues
# readability-container-contains, # has issues
# readability-container-size-empty, # has issues
# readability-convert-member-functions-to-static, # has issues
readability-duplicate-include,
readability-else-after-return,
readability-enum-initial-value,
readability-implicit-bool-conversion,
readability-make-member-function-const,
readability-math-missing-parentheses,
# readability-else-after-return, # has issues
# readability-enum-initial-value, # has issues
# readability-implicit-bool-conversion, # has issues
# readability-make-member-function-const, # has issues
# readability-math-missing-parentheses, # has issues
readability-misleading-indentation,
readability-non-const-parameter,
readability-redundant-casting,
readability-redundant-declaration,
readability-redundant-inline-specifier,
readability-redundant-member-init,
# readability-redundant-casting, # has issues
# readability-redundant-declaration, # has issues
# readability-redundant-inline-specifier, # has issues
# readability-redundant-member-init, # has issues
readability-redundant-string-init,
readability-reference-to-constructed-temporary,
readability-simplify-boolean-expr,
readability-static-definition-in-anonymous-namespace,
readability-suspicious-call-argument,
# readability-simplify-boolean-expr, # has issues
# readability-static-definition-in-anonymous-namespace, # has issues
# readability-suspicious-call-argument, # has issues
readability-use-std-min-max
"
# ---
# checks that have some issues that need to be resolved:
# other checks that have issues that need to be resolved:
#
# llvm-namespace-comment,
# misc-const-correctness,
@@ -134,7 +141,7 @@ Checks: "-*,
#
# readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names
# readability-static-accessed-through-instance, # this check is probably unnecessary. it makes the code less readable
# readability-identifier-naming,
# readability-identifier-naming, # https://github.com/XRPLF/rippled/pull/6571
#
# modernize-concat-nested-namespaces,
# modernize-pass-by-value,
@@ -148,12 +155,6 @@ Checks: "-*,
# modernize-use-starts-ends-with,
# modernize-use-std-numbers,
# modernize-use-using,
#
# performance-faster-string-find,
# performance-for-range-copy,
# performance-inefficient-vector-operation,
# performance-move-const-arg,
# performance-no-automatic-move,
# ---
#
CheckOptions:
@@ -195,5 +196,6 @@ CheckOptions:
bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc
# misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp'
#
# HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$'
HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp)$'
ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$'
WarningsAsErrors: "*"

View File

@@ -21,6 +21,7 @@ libxrpl.protocol > xrpl.json
libxrpl.protocol > xrpl.protocol
libxrpl.protocol_autogen > xrpl.protocol_autogen
libxrpl.rdb > xrpl.basics
libxrpl.rdb > xrpl.core
libxrpl.rdb > xrpl.rdb
libxrpl.resource > xrpl.basics
libxrpl.resource > xrpl.json
@@ -90,6 +91,7 @@ test.core > xrpl.server
test.csf > xrpl.basics
test.csf > xrpld.consensus
test.csf > xrpl.json
test.csf > xrpl.ledger
test.csf > xrpl.protocol
test.json > test.jtx
test.json > xrpl.json
@@ -108,7 +110,6 @@ test.jtx > xrpl.tx
test.ledger > test.jtx
test.ledger > test.toplevel
test.ledger > xrpl.basics
test.ledger > xrpld.app
test.ledger > xrpld.core
test.ledger > xrpl.ledger
test.ledger > xrpl.protocol
@@ -125,6 +126,7 @@ test.overlay > xrpl.basics
test.overlay > xrpld.app
test.overlay > xrpld.overlay
test.overlay > xrpld.peerfinder
test.overlay > xrpl.ledger
test.overlay > xrpl.nodestore
test.overlay > xrpl.protocol
test.overlay > xrpl.shamap
@@ -183,7 +185,6 @@ xrpl.conditions > xrpl.basics
xrpl.conditions > xrpl.protocol
xrpl.core > xrpl.basics
xrpl.core > xrpl.json
xrpl.core > xrpl.ledger
xrpl.core > xrpl.protocol
xrpl.json > xrpl.basics
xrpl.ledger > xrpl.basics
@@ -234,6 +235,7 @@ xrpld.app > xrpl.shamap
xrpld.app > xrpl.tx
xrpld.consensus > xrpl.basics
xrpld.consensus > xrpl.json
xrpld.consensus > xrpl.ledger
xrpld.consensus > xrpl.protocol
xrpld.core > xrpl.basics
xrpld.core > xrpl.core

View File

@@ -99,14 +99,15 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
continue
# RHEL:
# - 9 using GCC 12: Debug on linux/amd64.
# - 9 using GCC 12: Debug and Release on linux/amd64
# (Release is required for RPM packaging).
# - 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 == "Debug"
and build_type in ["Debug", "Release"]
and architecture["platform"] == "linux/amd64"
):
skip = False
@@ -121,7 +122,8 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
continue
# Ubuntu:
# - Jammy using GCC 12: Debug on linux/arm64.
# - Jammy using GCC 12: Debug on linux/arm64, Release on
# linux/amd64 (Release is required for DEB packaging).
# - Noble using GCC 14: Release on linux/amd64.
# - Noble using Clang 18: Debug on linux/amd64.
# - Noble using Clang 19: Release on linux/arm64.
@@ -134,6 +136,12 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
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"

25
.github/workflows/conflicting-pr.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Label PRs with merge conflicts
on:
# So that PRs touching the same files as the push are updated.
push:
# So that the `dirtyLabel` is removed if conflicts are resolved.
# We recommend `pull_request_target` so that github secrets are available.
# In `pull_request` we wouldn't be able to change labels of fork PRs.
pull_request_target:
types: [synchronize]
permissions:
pull-requests: write
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Check if PRs are dirty
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
with:
dirtyLabel: "PR: has conflicts"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
commentOnDirty: "This PR has conflicts, please resolve them in order for the PR to be reviewed."
commentOnClean: "All conflicts have been resolved. Assigned reviewers can now start or resume their review."

View File

@@ -67,6 +67,7 @@ jobs:
.github/workflows/reusable-build-test.yml
.github/workflows/reusable-clang-tidy.yml
.github/workflows/reusable-clang-tidy-files.yml
.github/workflows/reusable-package.yml
.github/workflows/reusable-strategy-matrix.yml
.github/workflows/reusable-test.yml
.github/workflows/reusable-upload-recipe.yml
@@ -81,6 +82,8 @@ jobs:
CMakeLists.txt
conanfile.py
conan.lock
package/**
- 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
@@ -137,6 +140,39 @@ jobs:
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
generate-version:
needs: should-run
if: ${{ needs.should-run.outputs.go == 'true' }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package-deb:
needs: [should-run, build-test, generate-version]
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: deb
artifact_name: xrpld-ubuntu-jammy-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12
package-rpm:
needs: [should-run, build-test, generate-version]
if: ${{ needs.should-run.outputs.go == 'true' }}
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: rpm
artifact_name: xrpld-rhel-9-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/rhel-9:gcc-12
upload-recipe:
needs:
- should-run

View File

@@ -1,5 +1,5 @@
# This workflow uploads the libxrpl recipe to the Conan remote when a versioned
# tag is pushed.
# This workflow uploads the libxrpl recipe to the Conan remote and builds
# release packages when a versioned tag is pushed.
name: Tag
on:
@@ -22,3 +22,49 @@ 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 }}
generate-version:
if: ${{ github.repository == 'XRPLF/rippled' }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package-deb:
needs: [build-test, generate-version]
if: ${{ github.repository == 'XRPLF/rippled' }}
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: deb
artifact_name: xrpld-ubuntu-jammy-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12
package-rpm:
needs: [build-test, generate-version]
if: ${{ github.repository == 'XRPLF/rippled' }}
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: rpm
artifact_name: xrpld-rhel-9-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/rhel-9:gcc-12

View File

@@ -38,6 +38,8 @@ on:
- "CMakeLists.txt"
- "conanfile.py"
- "conan.lock"
- "package/**"
- ".github/workflows/reusable-package.yml"
# 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
@@ -98,3 +100,32 @@ jobs:
secrets:
remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
generate-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Generate version
id: version
uses: ./.github/actions/generate-version
package-deb:
needs: [build-test, generate-version]
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: deb
artifact_name: xrpld-ubuntu-jammy-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12
package-rpm:
needs: [build-test, generate-version]
uses: ./.github/workflows/reusable-package.yml
with:
pkg_type: rpm
artifact_name: xrpld-rhel-9-gcc-12-amd64-release
version: ${{ needs.generate-version.outputs.version }}
container_image: ghcr.io/xrplf/ci/rhel-9:gcc-12

View File

@@ -6,7 +6,6 @@ on:
push:
branches:
- "develop"
- "release*"
paths:
- ".github/workflows/publish-docs.yml"
- "*.md"
@@ -82,13 +81,13 @@ jobs:
cmake --build . --target docs --parallel ${BUILD_NPROC}
- name: Create documentation artifact
if: ${{ github.event_name == 'push' }}
if: ${{ github.event.repository.visibility == 'public' && github.event_name == 'push' }}
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: ${{ env.BUILD_DIR }}/docs/html
deploy:
if: ${{ github.event_name == 'push' }}
if: ${{ github.event.repository.visibility == 'public' && github.event_name == 'push' }}
needs: build
runs-on: ubuntu-latest
permissions:
@@ -100,4 +99,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0

View File

@@ -199,7 +199,7 @@ jobs:
fi
- name: Upload the binary (Linux)
if: ${{ github.repository == 'XRPLF/rippled' && runner.os == 'Linux' }}
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: xrpld-${{ inputs.config_name }}
@@ -298,7 +298,7 @@ jobs:
- name: Upload coverage report
if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }}
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
disable_search: true
disable_telem: true

View File

@@ -83,7 +83,7 @@ jobs:
run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" ${TARGETS} 2>&1 | tee clang-tidy-output.txt
- name: Upload clang-tidy output
if: steps.run_clang_tidy.outcome != 'success'
if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: clang-tidy-results

83
.github/workflows/reusable-package.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
# Build a Linux package (DEB or RPM) from a pre-built binary artifact.
name: Package
on:
workflow_call:
inputs:
pkg_type:
description: "Package type to build: deb or rpm."
required: true
type: string
artifact_name:
description: "Name of the pre-built binary artifact to download."
required: true
type: string
version:
description: "Version string used for naming the output artifact."
required: true
type: string
pkg_release:
description: "Package release number. Increment when repackaging the same executable."
required: false
type: string
default: "1"
container_image:
description: "Container image to use for packaging."
required: true
type: string
defaults:
run:
shell: bash
env:
BUILD_DIR: build
jobs:
package:
name: ${{ inputs.pkg_type }} (${{ inputs.version }})
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
container: ${{ inputs.container_image }}
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: ${{ inputs.artifact_name }}
path: ${{ env.BUILD_DIR }}
- name: Make binary executable
run: chmod +x "${BUILD_DIR}/xrpld"
- name: Generate RPM spec from template
if: ${{ inputs.pkg_type == 'rpm' }}
env:
PKG_VERSION: ${{ inputs.version }}
PKG_RELEASE: ${{ inputs.pkg_release }}
run: |
mkdir -p "${BUILD_DIR}/package/rpm"
sed -e "s/@xrpld_version@/${PKG_VERSION}/" \
-e "s/@pkg_release@/${PKG_RELEASE}/" \
package/rpm/xrpld.spec.in > "${BUILD_DIR}/package/rpm/xrpld.spec"
- name: Build package
env:
PKG_TYPE: ${{ inputs.pkg_type }}
PKG_VERSION: ${{ inputs.version }}
PKG_RELEASE: ${{ inputs.pkg_release }}
run: |
./package/build_pkg.sh "$PKG_TYPE" . "$BUILD_DIR" "$PKG_VERSION" "$PKG_RELEASE"
- name: Upload package artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: xrpld-${{ inputs.pkg_type }}-${{ inputs.version }}
path: |
${{ env.BUILD_DIR }}/debbuild/*.deb
${{ env.BUILD_DIR }}/debbuild/*.ddeb
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.rpm
if-no-files-found: error

2
.gitignore vendored
View File

@@ -71,6 +71,8 @@ DerivedData
/.zed/
# AI tools.
/.agent
/.agents
/.augment
/.claude
/CLAUDE.md

View File

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

View File

@@ -12,14 +12,14 @@ if(is_root_project AND TARGET xrpld)
install(
FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}"
RENAME xrpld.cfg
COMPONENT runtime
)
install(
FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}"
RENAME validators.txt
COMPONENT runtime
)

142
cmake/XrplPackaging.cmake Normal file
View File

@@ -0,0 +1,142 @@
#[===================================================================[
Linux packaging support: RPM and Debian targets + install tests
#]===================================================================]
if(NOT CMAKE_INSTALL_PREFIX STREQUAL "/opt/xrpld")
message(
STATUS
"Packaging targets require -DCMAKE_INSTALL_PREFIX=/opt/xrpld "
"(current: '${CMAKE_INSTALL_PREFIX}'); skipping."
)
return()
endif()
if(NOT DEFINED pkg_release)
set(pkg_release 1)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/package/rpm/xrpld.spec.in
${CMAKE_BINARY_DIR}/package/rpm/xrpld.spec
@ONLY
)
find_program(RPMBUILD_EXECUTABLE rpmbuild)
if(RPMBUILD_EXECUTABLE)
add_custom_target(
package-rpm
COMMAND
${CMAKE_SOURCE_DIR}/package/build_pkg.sh rpm ${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Building RPM package"
VERBATIM
)
else()
message(STATUS "rpmbuild not found; 'package-rpm' target not available")
endif()
find_program(DPKG_BUILDPACKAGE_EXECUTABLE dpkg-buildpackage)
if(DPKG_BUILDPACKAGE_EXECUTABLE)
add_custom_target(
package-deb
COMMAND
${CMAKE_SOURCE_DIR}/package/build_pkg.sh deb ${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR} ${xrpld_version}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Building Debian package"
VERBATIM
)
else()
message(
STATUS
"dpkg-buildpackage not found; 'package-deb' target not available"
)
endif()
#[===================================================================[
CTest fixtures for package install verification (requires docker)
#]===================================================================]
find_program(DOCKER_EXECUTABLE docker)
if(NOT DOCKER_EXECUTABLE)
message(STATUS "docker not found; package install tests not available")
return()
endif()
set(DEB_TEST_IMAGE "geerlingguy/docker-ubuntu2204-ansible:latest")
set(RPM_TEST_IMAGE "geerlingguy/docker-rockylinux9-ansible:latest")
foreach(PKG deb rpm)
if(PKG STREQUAL "deb")
set(IMAGE ${DEB_TEST_IMAGE})
else()
set(IMAGE ${RPM_TEST_IMAGE})
endif()
# This image runs systemd for full testing xrpld.service
add_test(
NAME ${PKG}_container_start
COMMAND
sh -c
"docker rm -f xrpld_${PKG}_install_test 2>/dev/null || true && \
docker run -d \
--name xrpld_${PKG}_install_test \
--cgroupns host \
--volume '${CMAKE_SOURCE_DIR}:/root:ro' \
--volume /sys/fs/cgroup:/sys/fs/cgroup:rw \
--tmpfs /tmp --tmpfs /run \
--tmpfs /run/lock \
${IMAGE} \
/usr/sbin/init"
)
set_tests_properties(
${PKG}_container_start
PROPERTIES FIXTURES_SETUP ${PKG}_container LABELS packaging
)
# On CI: always stop. Locally: leave running on failure for diagnosis.
add_test(
NAME ${PKG}_container_stop
COMMAND
sh -c
"if [ -n \"$CI\" ] || ! docker exec xrpld_${PKG}_install_test test -f /tmp/test_failed 2>/dev/null; then \
docker rm -f xrpld_${PKG}_install_test; \
else \
echo 'Tests failed — leaving xrpld_${PKG}_install_test running for diagnosis'; \
echo 'Clean up with: docker rm -f xrpld_${PKG}_install_test'; \
fi"
)
set_tests_properties(
${PKG}_container_stop
PROPERTIES FIXTURES_CLEANUP ${PKG}_container LABELS packaging
)
add_test(
NAME ${PKG}_install
COMMAND
docker exec -w /root xrpld_${PKG}_install_test bash
/root/package/test/smoketest.sh local
)
set_tests_properties(
${PKG}_install
PROPERTIES
FIXTURES_REQUIRED ${PKG}_container
FIXTURES_SETUP ${PKG}_installed
LABELS packaging
TIMEOUT 600
)
add_test(
NAME ${PKG}_install_paths
COMMAND
docker exec -w /root xrpld_${PKG}_install_test sh
/root/package/test/check_install_paths.sh
)
set_tests_properties(
${PKG}_install_paths
PROPERTIES
FIXTURES_REQUIRED "${PKG}_container;${PKG}_installed"
LABELS packaging
TIMEOUT 60
)
endforeach()

View File

@@ -140,6 +140,28 @@ function(setup_protocol_autogen)
)
endif()
# Check pip index URL configuration
execute_process(
COMMAND ${VENV_PIP} config get global.index-url
OUTPUT_VARIABLE PIP_INDEX_URL
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
# Default PyPI URL
set(DEFAULT_PIP_INDEX "https://pypi.org/simple")
# Show warning if using non-default index
if(PIP_INDEX_URL AND NOT PIP_INDEX_URL STREQUAL "")
if(NOT PIP_INDEX_URL STREQUAL DEFAULT_PIP_INDEX)
message(
WARNING
"Private pip index URL detected: ${PIP_INDEX_URL}\n"
"You may need to connect to VPN to access this URL."
)
endif()
endif()
message(STATUS "Installing Python dependencies...")
execute_process(
COMMAND ${VENV_PIP} install --upgrade pip

View File

@@ -1,16 +1,16 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1765284699.337",
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1774439233.809",
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
"sqlite3/3.51.0#66aa11eabd0e34954c5c1c061ad44abe%1763899256.358",
"soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
"secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1772560729.95",
"protobuf/6.32.1#b54f00da2e0f61d821330b5b638b0f80%1768401317.762",
"openssl/3.5.5#e6399de266349245a4542fc5f6c71552%1774367199.56",
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1773224203.27",
"openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139",
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
@@ -18,24 +18,23 @@
"libarchive/3.8.1#ffee18995c706e02bf96e7a2f7042e0d%1765850144.736",
"jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244",
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
"grpc/1.72.0#aaade9421980b2d926dbfb613d56c38a%1774376249.106",
"grpc/1.78.1#b1a9e74b145cc471bed4dc64dc6eb2c1%1772623605.068",
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1766500685.317",
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1769454080.269",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
],
"build_requires": [
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1765284699.337",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1751971032.423",
"protobuf/6.32.1#b54f00da2e0f61d821330b5b638b0f80%1768401317.762",
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1774439233.809",
"strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
"protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1773224203.27",
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
"msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
"m4/1.4.19#5d7a4994e5875d76faf7acf3ed056036%1774365463.87",
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1773780142.26",
"cmake/3.31.11#f325c933f618a1fcebc1e1c0babfd1ba%1769622857.944",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1766594659.866",
"cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
@@ -47,13 +46,13 @@
"boost/1.90.0"
],
"protobuf/[>=5.27.0 <7]": [
"protobuf/6.32.1"
"protobuf/6.33.5"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"sqlite3/[>=3.44 <4]": [
"sqlite3/3.49.1"
"sqlite3/3.51.0"
],
"boost/1.83.0": [
"boost/1.90.0"

View File

@@ -4,7 +4,6 @@ import re
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan import ConanFile
from conan import __version__ as conan_version
class Xrpl(ConanFile):
@@ -30,10 +29,10 @@ class Xrpl(ConanFile):
requires = [
"ed25519/2015.03",
"grpc/1.72.0",
"grpc/1.78.1",
"libarchive/3.8.1",
"nudb/2.0.9",
"openssl/3.5.5",
"openssl/3.6.1",
"secp256k1/0.7.1",
"soci/4.0.3",
"zlib/1.3.1",
@@ -44,7 +43,7 @@ class Xrpl(ConanFile):
]
tool_requires = [
"protobuf/6.32.1",
"protobuf/6.33.5",
]
default_options = {
@@ -137,20 +136,16 @@ class Xrpl(ConanFile):
self.default_options["fPIC"] = False
def requirements(self):
# Conan 2 requires transitive headers to be specified
transitive_headers_opt = (
{"transitive_headers": True} if conan_version.split(".")[0] == "2" else {}
)
self.requires("boost/1.90.0", force=True, **transitive_headers_opt)
self.requires("date/3.0.4", **transitive_headers_opt)
self.requires("boost/1.90.0", force=True, transitive_headers=True)
self.requires("date/3.0.4", transitive_headers=True)
self.requires("lz4/1.10.0", force=True)
self.requires("protobuf/6.32.1", force=True)
self.requires("sqlite3/3.49.1", force=True)
self.requires("protobuf/6.33.5", force=True)
self.requires("sqlite3/3.51.0", force=True)
if self.options.jemalloc:
self.requires("jemalloc/5.3.0")
if self.options.rocksdb:
self.requires("rocksdb/10.5.1")
self.requires("xxhash/0.8.3", **transitive_headers_opt)
self.requires("xxhash/0.8.3", transitive_headers=True)
exports_sources = (
"CMakeLists.txt",

View File

@@ -93,11 +93,14 @@ words:
- desync
- desynced
- determ
- disablerepo
- distro
- doxyfile
- dxrpl
- enablerepo
- endmacro
- exceptioned
- EXPECT_STREQ
- Falco
- fcontext
- finalizers
@@ -151,6 +154,7 @@ words:
- Merkle
- Metafuncton
- misprediction
- missingok
- mptbalance
- MPTDEX
- mptflags
@@ -181,7 +185,9 @@ words:
- NOLINT
- NOLINTNEXTLINE
- nonxrp
- noreplace
- noripple
- notifempty
- nudb
- nullptr
- nunl
@@ -201,6 +207,7 @@ words:
- preauthorize
- preauthorizes
- preclaim
- preun
- protobuf
- protos
- ptrs
@@ -235,12 +242,14 @@ words:
- sfields
- shamap
- shamapitem
- shlibs
- sidechain
- SIGGOOD
- sle
- sles
- soci
- socidb
- SRPMS
- sslws
- statsd
- STATSDCOLLECTOR
@@ -268,8 +277,8 @@ words:
- txn
- txns
- txs
- UBSAN
- ubsan
- UBSAN
- umant
- unacquired
- unambiguity
@@ -306,7 +315,6 @@ words:
- xbridge
- xchain
- ximinez
- EXPECT_STREQ
- XMACRO
- xrpkuwait
- xrpl

View File

@@ -70,14 +70,24 @@ JobQueue::Coro::resume()
running_ = true;
}
{
std::lock_guard lock(jq_.m_mutex);
std::lock_guard lk(jq_.m_mutex);
--jq_.nSuspend_;
}
auto saved = detail::getLocalValues().release();
detail::getLocalValues().reset(&lvs_);
std::lock_guard lock(mutex_);
XRPL_ASSERT(static_cast<bool>(coro_), "xrpl::JobQueue::Coro::resume : is runnable");
coro_();
// A late resume() can arrive after the coroutine has already completed.
// This is an expected (if rare) outcome of the race condition documented
// in JobQueue.h:354-377 where post() schedules a resume job before the
// coroutine yields — the mutex serializes access, but by the time this
// resume() acquires the lock the coroutine may have already run to
// completion. Calling operator() on a completed boost::coroutine2 is
// undefined behavior, so we must check and skip invoking the coroutine
// body if it has already completed.
if (coro_)
{
coro_();
}
detail::getLocalValues().release();
detail::getLocalValues().reset(saved);
std::lock_guard lk(mutex_run_);

View File

@@ -99,8 +99,8 @@ public:
Effects:
The coroutine continues execution from where it last left off
using this same thread.
Undefined behavior if called after the coroutine has completed
with a return (as opposed to a yield()).
If the coroutine has already completed, returns immediately
(handles the documented post-before-yield race condition).
Undefined behavior if resume() or post() called consecutively
without a corresponding yield.
*/
@@ -357,8 +357,10 @@ private:
If the post() job were to be executed before yield(), undefined behavior
would occur. The lock ensures that coro_ is not called again until we exit
the coroutine. At which point a scheduled resume() job waiting on the lock
would gain entry, harmlessly call coro_ and immediately return as we have
already completed the coroutine.
would gain entry. resume() checks if the coroutine has already completed
(coro_ converts to false) and, if so, skips invoking operator() since
calling operator() on a completed boost::coroutine2 pull_type is undefined
behavior.
The race condition occurs as follows:

View File

@@ -3,7 +3,6 @@
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/SHAMapHash.h>
#include <xrpl/basics/TaggedCache.h>
#include <xrpl/ledger/CachedSLEs.h>
#include <boost/asio.hpp>
@@ -23,6 +22,20 @@ class PerfLog;
// This is temporary until we migrate all code to use ServiceRegistry.
class Application;
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
class TaggedCache;
class STLedgerEntry;
using SLE = STLedgerEntry;
using CachedSLEs = TaggedCache<uint256, SLE const>;
// Forward declarations
class AcceptedLedger;
class AmendmentTable;
@@ -45,7 +58,7 @@ class NetworkIDService;
class OpenLedger;
class OrderBookDB;
class Overlay;
class PathRequests;
class PathRequestManager;
class PeerReservationTable;
class PendingSaves;
class RelationalDatabase;
@@ -89,7 +102,7 @@ public:
getNodeFamily() = 0;
virtual TimeKeeper&
timeKeeper() = 0;
getTimeKeeper() = 0;
virtual JobQueue&
getJobQueue() = 0;
@@ -98,7 +111,7 @@ public:
getTempNodeCache() = 0;
virtual CachedSLEs&
cachedSLEs() = 0;
getCachedSLEs() = 0;
virtual NetworkIDService&
getNetworkIDService() = 0;
@@ -120,26 +133,26 @@ public:
getValidations() = 0;
virtual ValidatorList&
validators() = 0;
getValidators() = 0;
virtual ValidatorSite&
validatorSites() = 0;
getValidatorSites() = 0;
virtual ManifestCache&
validatorManifests() = 0;
getValidatorManifests() = 0;
virtual ManifestCache&
publisherManifests() = 0;
getPublisherManifests() = 0;
// Network services
virtual Overlay&
overlay() = 0;
getOverlay() = 0;
virtual Cluster&
cluster() = 0;
getCluster() = 0;
virtual PeerReservationTable&
peerReservations() = 0;
getPeerReservations() = 0;
virtual Resource::Manager&
getResourceManager() = 0;
@@ -174,13 +187,13 @@ public:
getLedgerReplayer() = 0;
virtual PendingSaves&
pendingSaves() = 0;
getPendingSaves() = 0;
virtual OpenLedger&
openLedger() = 0;
getOpenLedger() = 0;
virtual OpenLedger const&
openLedger() const = 0;
getOpenLedger() const = 0;
// Transaction and operation services
virtual NetworkOPs&
@@ -195,8 +208,8 @@ public:
virtual TxQ&
getTxQ() = 0;
virtual PathRequests&
getPathRequests() = 0;
virtual PathRequestManager&
getPathRequestManager() = 0;
// Server services
virtual ServerHandler&
@@ -210,16 +223,16 @@ public:
isStopping() const = 0;
virtual beast::Journal
journal(std::string const& name) = 0;
getJournal(std::string const& name) = 0;
virtual boost::asio::io_context&
getIOContext() = 0;
virtual Logs&
logs() = 0;
getLogs() = 0;
virtual std::optional<uint256> const&
trapTxID() const = 0;
getTrapTxID() const = 0;
/** Retrieve the "wallet database" */
virtual DatabaseCon&
@@ -228,7 +241,7 @@ public:
// Temporary: Get the underlying Application for functions that haven't
// been migrated yet. This should be removed once all code is migrated.
virtual Application&
app() = 0;
getApp() = 0;
};
} // namespace xrpl

View File

@@ -5,7 +5,7 @@
namespace xrpl {
enum class StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
enum class StartUpType { Fresh, Normal, Load, LoadFile, Replay, Network };
inline std::ostream&
operator<<(std::ostream& os, StartUpType const& type)

View File

@@ -1,13 +1,12 @@
#pragma once
#include <xrpld/core/Config.h>
#include <xrpld/core/TimeKeeper.h>
#include <xrpl/basics/CountedObject.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/CachedView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxMeta.h>
@@ -15,7 +14,7 @@
namespace xrpl {
class Application;
class ServiceRegistry;
class Job;
class TransactionMaster;
@@ -83,21 +82,26 @@ public:
*/
Ledger(
create_genesis_t,
Config const& config,
Rules const& rules,
Fees const& fees,
std::vector<uint256> const& amendments,
Family& family);
Ledger(LedgerHeader const& info, Config const& config, Family& family);
Ledger(LedgerHeader const& info, Rules const& rules, Family& family);
/** Used for ledgers loaded from JSON files
@param acquire If true, acquires the ledger if not found locally
@note The fees parameter provides default values, but setup() may
override them from the ledger state if fee-related SLEs exist.
*/
Ledger(
LedgerHeader const& info,
bool& loaded,
bool acquire,
Config const& config,
Rules const& rules,
Fees const& fees,
Family& family,
beast::Journal j);
@@ -113,7 +117,8 @@ public:
Ledger(
std::uint32_t ledgerSeq,
NetClock::time_point closeTime,
Config const& config,
Rules const& rules,
Fees const& fees,
Family& family);
~Ledger() = default;
@@ -322,7 +327,7 @@ public:
walkLedger(beast::Journal j, bool parallel = false) const;
bool
assertSensible(beast::Journal ledgerJ) const;
isSensible() const;
void
invariants() const;
@@ -379,8 +384,26 @@ private:
bool
setup();
void
defaultFees(Config const& config);
/** @brief Deserialize a SHAMapItem containing a single STTx.
*
* @param item The SHAMapItem to deserialize.
* @return A shared pointer to the deserialized transaction.
* @throw May throw on deserialization error.
*/
static std::shared_ptr<STTx const>
deserializeTx(SHAMapItem const& item);
/** @brief Deserialize a SHAMapItem containing STTx + STObject metadata.
*
* The SHAMapItem must contain two variable length serialization objects.
*
* @param item The SHAMapItem to deserialize.
* @return A pair containing shared pointers to the deserialized transaction
* and metadata.
* @throw May throw on deserialization error.
*/
static std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>>
deserializeTxPlusMeta(SHAMapItem const& item);
bool mImmutable;
@@ -402,54 +425,4 @@ private:
/** A ledger wrapped in a CachedView. */
using CachedLedger = CachedView<Ledger>;
//------------------------------------------------------------------------------
//
// API
//
//------------------------------------------------------------------------------
extern bool
pendSaveValidated(
Application& app,
std::shared_ptr<Ledger const> const& ledger,
bool isSynchronous,
bool isCurrent);
std::shared_ptr<Ledger>
loadLedgerHelper(LedgerHeader const& sinfo, Application& app, bool acquire);
std::shared_ptr<Ledger>
loadByIndex(std::uint32_t ledgerIndex, Application& app, bool acquire = true);
std::shared_ptr<Ledger>
loadByHash(uint256 const& ledgerHash, Application& app, bool acquire = true);
// Fetch the ledger with the highest sequence contained in the database
extern std::tuple<std::shared_ptr<Ledger>, std::uint32_t, uint256>
getLatestLedger(Application& app);
/** Deserialize a SHAMapItem containing a single STTx
Throw:
May throw on deserializaton error
*/
std::shared_ptr<STTx const>
deserializeTx(SHAMapItem const& item);
/** Deserialize a SHAMapItem containing STTx + STObject metadata
The SHAMap must contain two variable length
serialization objects.
Throw:
May throw on deserializaton error
*/
std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>>
deserializeTxPlusMeta(SHAMapItem const& item);
uint256
calculateLedgerHash(LedgerHeader const& info);
} // namespace xrpl

View File

@@ -76,16 +76,33 @@ public:
@return true if a book from this issue to XRP exists
*/
virtual bool
isBookToXRP(Issue const& issue, std::optional<Domain> domain = std::nullopt) = 0;
isBookToXRP(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
/**
* Process a transaction for order book tracking.
* @param ledger The ledger the transaction was applied to
* @param alTx The transaction to process
* @param jvObj The JSON object of the transaction
*/
virtual void
processTxn(
std::shared_ptr<ReadView const> const& ledger,
AcceptedLedgerTx const& alTx,
MultiApiJson const& jvObj) = 0;
/**
* Get the book listeners for a book.
* @param book The book to get the listeners for
* @return The book listeners for the book
*/
virtual BookListeners::pointer
getBookListeners(Book const&) = 0;
/**
* Create a new book listeners for a book.
* @param book The book to create the listeners for
* @return The new book listeners for the book
*/
virtual BookListeners::pointer
makeBookListeners(Book const&) = 0;
};

View File

@@ -4,6 +4,10 @@
namespace xrpl {
// Deprecated constant for backwards compatibility with pre-XRPFees amendment.
// This was the reference fee units used in the old fee calculation.
inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10;
/** Reflects the fee settings for a particular ledger.
The fees are always the same for any transactions applied
@@ -11,15 +15,25 @@ namespace xrpl {
*/
struct Fees
{
XRPAmount base{0}; // Reference tx cost (drops)
XRPAmount reserve{0}; // Reserve base (drops)
XRPAmount increment{0}; // Reserve increment (drops)
/** @brief Cost of a reference transaction in drops. */
XRPAmount base{0};
/** @brief Minimum XRP an account must hold to exist on the ledger. */
XRPAmount reserve{0};
/** @brief Additional XRP reserve required per owned ledger object. */
XRPAmount increment{0};
explicit Fees() = default;
Fees(Fees const&) = default;
Fees&
operator=(Fees const&) = default;
Fees(XRPAmount base_, XRPAmount reserve_, XRPAmount increment_)
: base(base_), reserve(reserve_), increment(increment_)
{
}
/** Returns the account reserve given the owner count, in drops.
The reserve is calculated as the reserve base plus

View File

@@ -72,4 +72,8 @@ deserializeHeader(Slice data, bool hasHash = false);
LedgerHeader
deserializePrefixedHeader(Slice data, bool hasHash = false);
/** Calculate the hash of a ledger header. */
uint256
calculateLedgerHash(LedgerHeader const& info);
} // namespace xrpl

View File

@@ -69,9 +69,6 @@ public:
std::vector<uint256>::iterator
insert(std::vector<uint256>::const_iterator pos, uint256 const& value);
std::vector<uint256>::iterator
insert(std::vector<uint256>::const_iterator pos, uint256&& value);
void
push_back(uint256 const& v);
@@ -184,12 +181,6 @@ STVector256::insert(std::vector<uint256>::const_iterator pos, uint256 const& val
return mValue.insert(pos, value);
}
inline std::vector<uint256>::iterator
STVector256::insert(std::vector<uint256>::const_iterator pos, uint256&& value)
{
return mValue.insert(pos, std::move(value));
}
inline void
STVector256::push_back(uint256 const& v)
{

View File

@@ -2,6 +2,6 @@
namespace xrpl {
enum class TxSearched { all, some, unknown };
enum class TxSearched { All, Some, Unknown };
}

View File

@@ -16,6 +16,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -1,6 +1,7 @@
#pragma once
#include <xrpl/core/PerfLog.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/core/StartUpType.h>
#include <xrpl/rdb/DBInit.h>
#include <xrpl/rdb/SociDB.h>
@@ -69,7 +70,7 @@ public:
{
explicit Setup() = default;
StartUpType startUp = StartUpType::NORMAL;
StartUpType startUp = StartUpType::Normal;
bool standAlone = false;
boost::filesystem::path dataDir;
// Indicates whether or not to return the `globalPragma`
@@ -94,7 +95,7 @@ public:
struct CheckpointerSetup
{
JobQueue* jobQueue;
Logs* logs;
std::reference_wrapper<ServiceRegistry> registry;
};
template <std::size_t N, std::size_t M>
@@ -106,9 +107,8 @@ public:
beast::Journal journal)
// Use temporary files or regular DB files?
: DatabaseCon(
setup.standAlone && setup.startUp != StartUpType::LOAD &&
setup.startUp != StartUpType::LOAD_FILE &&
setup.startUp != StartUpType::REPLAY
setup.standAlone && setup.startUp != StartUpType::Load &&
setup.startUp != StartUpType::LoadFile && setup.startUp != StartUpType::Replay
? ""
: (setup.dataDir / dbName),
setup.commonPragma(),
@@ -129,7 +129,7 @@ public:
beast::Journal journal)
: DatabaseCon(setup, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
}
template <std::size_t N, std::size_t M>
@@ -154,7 +154,7 @@ public:
beast::Journal journal)
: DatabaseCon(dataDir, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
}
~DatabaseCon();
@@ -177,7 +177,7 @@ public:
private:
void
setupCheckpointing(JobQueue*, Logs&);
setupCheckpointing(JobQueue*, ServiceRegistry&);
template <std::size_t N, std::size_t M>
DatabaseCon(

View File

@@ -49,8 +49,9 @@ public:
struct AccountTxOptions
{
AccountID const& account;
std::uint32_t minLedger;
std::uint32_t maxLedger;
/// Ledger sequence range to search. A value of 0 for min or max
/// means unbounded in that direction (no constraint applied).
LedgerRange ledgerRange;
std::uint32_t offset;
std::uint32_t limit;
bool bUnlimited;
@@ -59,8 +60,7 @@ public:
struct AccountTxPageOptions
{
AccountID const& account;
std::uint32_t minLedger;
std::uint32_t maxLedger;
LedgerRange ledgerRange;
std::optional<AccountTxMarker> marker;
std::uint32_t limit;
bool bAdmin;
@@ -247,7 +247,7 @@ public:
* @return Struct CountMinMax which contains the minimum sequence,
* maximum sequence and number of ledgers.
*/
virtual struct CountMinMax
virtual CountMinMax
getLedgerCountMinMax() = 0;
/**
@@ -405,10 +405,10 @@ public:
* @param id Hash of the transaction.
* @param range Range of ledgers to check, if present.
* @param ec Default error code value.
* @return Transaction and its metadata if found, otherwise TxSearched::all
* @return Transaction and its metadata if found, otherwise TxSearched::All
* if a range is provided and all ledgers from the range are present
* in the database, TxSearched::some if a range is provided and not
* all ledgers are present, TxSearched::unknown if the range is not
* in the database, TxSearched::Some if a range is provided and not
* all ledgers are present, TxSearched::Unknown if the range is not
* provided or a deserializing error occurred. In the last case the
* error code is returned via the ec parameter, in other cases the
* default error code is not changed.
@@ -455,9 +455,10 @@ public:
closeTransactionDB() = 0;
};
template <class T, class C>
template <typename T, typename C>
T
rangeCheckedCast(C c)
requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<C> && std::convertible_to<C, T>)
{
if ((c > std::numeric_limits<T>::max()) || (!std::numeric_limits<T>::is_signed && c < 0) ||
(std::numeric_limits<T>::is_signed && std::numeric_limits<C>::is_signed &&

View File

@@ -13,8 +13,8 @@
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
#include <xrpl/basics/Log.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/core/ServiceRegistry.h>
#define SOCI_USE_BOOST
#include <soci/soci.h>
@@ -111,7 +111,7 @@ public:
and so must outlive them both.
*/
std::shared_ptr<Checkpointer>
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, Logs&);
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, ServiceRegistry&);
} // namespace xrpl

View File

@@ -37,7 +37,7 @@ public:
XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set");
}
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
STTx const& tx;
TER const preclaimResult;
XRPAmount const baseFee;

View File

@@ -13,7 +13,7 @@ namespace xrpl {
struct PreflightContext
{
public:
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
STTx const& tx;
Rules const rules;
ApplyFlags flags;
@@ -56,7 +56,7 @@ public:
struct PreclaimContext
{
public:
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
ReadView const& view;
TER preflightResult;
ApplyFlags flags;

View File

@@ -7,6 +7,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/tx/invariants/AMMInvariant.h>
#include <xrpl/tx/invariants/FreezeInvariant.h>
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
#include <xrpl/tx/invariants/LoanInvariant.h>
#include <xrpl/tx/invariants/MPTInvariant.h>
#include <xrpl/tx/invariants/NFTInvariant.h>

View File

@@ -0,0 +1,55 @@
#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 <map>
#include <vector>
namespace xrpl {
/**
* @brief Invariants: Loan brokers are internally consistent
*
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
* node (the root), which will only hold entries for `RippleState` or
* `MPToken` objects.
*
*/
class ValidLoanBroker
{
// Not all of these elements will necessarily be populated. Remaining items
// will be looked up as needed.
struct BrokerInfo
{
SLE::const_pointer brokerBefore = nullptr;
// After is used for most of the checks, except
// those that check changed values.
SLE::const_pointer brokerAfter = nullptr;
};
// Collect all the LoanBrokers found directly or indirectly through
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
// LoanBroker object if brokerBefore and brokerAfter are nullptr
std::map<uint256, BrokerInfo> brokers_;
// Collect all the modified trust lines. Their high and low accounts will be
// loaded to look for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> lines_;
// Collect all the modified MPTokens. Their accounts will be loaded to look
// for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> mpts_;
static bool
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j);
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
} // namespace xrpl

View File

@@ -1,57 +1,14 @@
#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 <map>
#include <vector>
namespace xrpl {
/**
* @brief Invariants: Loan brokers are internally consistent
*
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
* node (the root), which will only hold entries for `RippleState` or
* `MPToken` objects.
*
*/
class ValidLoanBroker
{
// Not all of these elements will necessarily be populated. Remaining items
// will be looked up as needed.
struct BrokerInfo
{
SLE::const_pointer brokerBefore = nullptr;
// After is used for most of the checks, except
// those that check changed values.
SLE::const_pointer brokerAfter = nullptr;
};
// Collect all the LoanBrokers found directly or indirectly through
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
// LoanBroker object if brokerBefore and brokerAfter are nullptr
std::map<uint256, BrokerInfo> brokers_;
// Collect all the modified trust lines. Their high and low accounts will be
// loaded to look for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> lines_;
// Collect all the modified MPTokens. Their accounts will be loaded to look
// for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> mpts_;
static bool
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j);
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&);
};
/**
* @brief Invariants: Loans are internally consistent
*

View File

@@ -1,6 +1,6 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
@@ -92,7 +92,7 @@ public:
STPathSet const& spsPaths,
std::optional<uint256> const& domainID,
Logs& l,
ServiceRegistry& registry,
Input const* const pInputs = nullptr);
// The view we are currently working on

View File

@@ -203,7 +203,7 @@ getAMMOfferStartWithTakerGets(
// Try to reduce the offer size to improve the quality.
// The quality might still not match the targetQuality for a tiny offer.
if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
if (auto amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
return getAmounts(detail::reduceOffer(amounts.out));
else
return amounts;
@@ -270,7 +270,7 @@ getAMMOfferStartWithTakerPays(
// Try to reduce the offer size to improve the quality.
// The quality might still not match the targetQuality for a tiny offer.
if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
if (auto amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
return getAmounts(detail::reduceOffer(amounts.in));
else
return amounts;
@@ -335,8 +335,7 @@ changeSpotPriceQuality(
}
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
// should not fail
if (auto const amounts =
TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
Quality{amounts} < quality &&
!withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
{
@@ -362,7 +361,7 @@ changeSpotPriceQuality(
// Generate the offer starting with XRP side. Return seated offer amounts
// if the offer can be generated, otherwise nullopt.
auto const amounts = [&]() {
auto amounts = [&]() {
if (isXRP(getIssue(pool.out)))
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
return getAMMOfferStartWithTakerPays(pool, quality, tfee);

118
package/README.md Normal file
View File

@@ -0,0 +1,118 @@
# 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.in RPM spec template (substitutes @xrpld_version@, @pkg_release@)
deb/
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 /opt/xrpld/bin/, user activates)
update-xrpld.sh auto-update script (installed to /opt/xrpld/bin/)
update-xrpld-cron cron entry for auto-update (installed to /opt/xrpld/bin/)
test/
smoketest.sh Package install smoke test
check_install_paths.sh Verify install paths and compat symlinks
```
## Prerequisites
| Package type | Container | Tool required |
| ------------ | -------------------------------------- | --------------------------------------------------------------- |
| RPM | `ghcr.io/xrplf/ci/rhel-9:gcc-12` | `rpmbuild` |
| DEB | `ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` |
## Building packages
### Via CI (recommended)
The `reusable-package.yml` workflow downloads a pre-built `xrpld` binary artifact
and calls `build_pkg.sh` directly. No CMake configure or build step is needed in
the packaging job.
### Via CMake (local development)
Configure with the required install prefix, then invoke the target:
```bash
cmake \
-DCMAKE_INSTALL_PREFIX=/opt/xrpld \
-Dxrpld=ON \
-Dtests=OFF \
..
# RPM (in RHEL container):
cmake --build . --target package-rpm
# DEB (in Debian/Ubuntu container):
cmake --build . --target package-deb
```
The `cmake/XrplPackaging.cmake` module gates each target on whether the required
tool (`rpmbuild` / `dpkg-buildpackage`) is present at configure time, so
configuring on a host that lacks one simply omits the corresponding target.
`CMAKE_INSTALL_PREFIX` must be `/opt/xrpld`; if it is not, both targets are
skipped with a `STATUS` message.
## How `build_pkg.sh` works
`build_pkg.sh <pkg_type> <src_dir> <build_dir> [version] [pkg_release]` stages
all files and invokes the platform build tool. 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.
### RPM
1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory.
2. Copies the generated `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`.
3. Runs `rpmbuild -bb`. 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/deb/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"
```
## TODO
- Port debsigs signing instructions and integrate into CI.
- Port RPM GPG signing setup (key import + `%{?_gpg_sign}` in spec).
- Introduce a virtual package for key rotation.

92
package/build_pkg.sh Executable file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env bash
# Build an RPM or Debian package from a pre-built xrpld binary.
#
# Usage: build_pkg.sh <pkg_type> <src_dir> <build_dir> [version] [pkg_release]
# pkg_type : rpm | deb
# src_dir : path to repository root
# build_dir : directory containing the pre-built xrpld binary
# version : package version string (e.g. 2.4.0-b1)
# pkg_release : package release number (default: 1)
set -euo pipefail
PKG_TYPE="${1:?pkg_type required}"
SRC_DIR="$(cd "${2:?src_dir required}" && pwd)"
BUILD_DIR="$(cd "${3:?build_dir required}" && pwd)"
VERSION="${4:-1.0.0}"
PKG_RELEASE="${5:-1}"
SHARED="${SRC_DIR}/package/shared"
# Stage files common to both package types into a target directory.
stage_common() {
local dest="$1"
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 "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
cp "${SHARED}/update-xrpld.sh" "${dest}/update-xrpld.sh"
cp "${SHARED}/update-xrpld-cron" "${dest}/update-xrpld-cron"
}
build_rpm() {
local topdir="${BUILD_DIR}/rpmbuild"
mkdir -p "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
cp "${BUILD_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
stage_common "${topdir}/SOURCES"
cp "${SHARED}/xrpld.service" "${topdir}/SOURCES/xrpld.service"
cp "${SHARED}/xrpld.sysusers" "${topdir}/SOURCES/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${topdir}/SOURCES/xrpld.tmpfiles"
cp "${SRC_DIR}/LICENSE.md" "${topdir}/SOURCES/LICENSE.md"
cp "${SRC_DIR}/README.md" "${topdir}/SOURCES/README.md"
set -x
rpmbuild -bb \
--define "_topdir ${topdir}" \
"${topdir}/SPECS/xrpld.spec"
}
build_deb() {
local staging="${BUILD_DIR}/debbuild/source"
rm -rf "${staging}"
mkdir -p "${staging}"
stage_common "${staging}"
cp "${SRC_DIR}/README.md" "${staging}/"
cp "${SRC_DIR}/LICENSE.md" "${staging}/"
# debian/ control files
cp -r "${SRC_DIR}/package/deb/debian" "${staging}/debian"
# Shared support files for dh_installsystemd / sysusers / tmpfiles
cp "${SHARED}/xrpld.service" "${staging}/debian/xrpld.service"
cp "${SHARED}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
# Generate debian/changelog (pre-release versions use ~ instead of -).
local deb_version="${VERSION//-/\~}"
# TODO: Add facility for generating the changelog
cat > "${staging}/debian/changelog" <<EOF
xrpld (${deb_version}-${PKG_RELEASE}) unstable; urgency=medium
* Release ${VERSION}.
-- XRPL Foundation <contact@xrplf.org> $(LC_ALL=C date -u -R)
EOF
chmod +x "${staging}/debian/rules"
set -x
( cd "${staging}" && dpkg-buildpackage -b --no-sign -d )
}
case "${PKG_TYPE}" in
rpm) build_rpm ;;
deb) build_deb ;;
*)
echo "Unknown package type: ${PKG_TYPE}" >&2
exit 1
;;
esac

View File

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

View File

@@ -0,0 +1,20 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: rippled
Source: https://github.com/XRPLF/rippled
Files: *
Copyright: 2012-2025 Ripple Labs Inc.
License: ISC
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.

35
package/deb/debian/rules Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1
export DH_OPTIONS = -v
%:
dh $@
override_dh_auto_configure override_dh_auto_build override_dh_auto_test:
@:
override_dh_auto_install:
install -Dm0755 xrpld debian/tmp/opt/xrpld/bin/xrpld
install -Dm0644 xrpld.cfg debian/tmp/opt/xrpld/etc/xrpld.cfg
install -Dm0644 validators.txt debian/tmp/opt/xrpld/etc/validators.txt
install -Dm0644 xrpld.logrotate debian/tmp/opt/xrpld/bin/xrpld.logrotate
install -Dm0755 update-xrpld.sh debian/tmp/opt/xrpld/bin/update-xrpld.sh
install -Dm0644 update-xrpld-cron debian/tmp/opt/xrpld/bin/update-xrpld-cron
install -Dm0644 README.md debian/tmp/usr/share/doc/xrpld/README.md
install -Dm0644 LICENSE.md debian/tmp/usr/share/doc/xrpld/LICENSE.md
override_dh_installsystemd:
dh_installsystemd
override_dh_installsysusers:
dh_installsysusers
override_dh_installtmpfiles:
dh_installtmpfiles
override_dh_install:
dh_install
override_dh_dwz:
@:

View File

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

View File

@@ -0,0 +1,2 @@
/opt/xrpld/etc/xrpld.cfg
/opt/xrpld/etc/validators.txt

View File

@@ -0,0 +1,10 @@
opt/xrpld/bin/xrpld
opt/xrpld/bin/xrpld.logrotate
opt/xrpld/bin/update-xrpld.sh
opt/xrpld/bin/update-xrpld-cron
opt/xrpld/etc/xrpld.cfg
opt/xrpld/etc/validators.txt
usr/share/doc/xrpld/README.md
usr/share/doc/xrpld/LICENSE.md

View File

@@ -0,0 +1,10 @@
opt/xrpld/etc etc/opt/xrpld
opt/xrpld/bin/xrpld usr/bin/xrpld
## remove when "rippled" deprecated
opt/xrpld/bin/xrpld opt/xrpld/bin/rippled
opt/xrpld/bin/xrpld usr/local/bin/rippled
opt/xrpld/etc/xrpld.cfg opt/xrpld/etc/rippled.cfg
opt/xrpld opt/ripple
etc/opt/xrpld etc/opt/ripple

121
package/rpm/xrpld.spec.in Normal file
View File

@@ -0,0 +1,121 @@
%global xrpld_version @xrpld_version@
%global pkg_release @pkg_release@
%global _opt_prefix /opt/xrpld
%global ver_base %(v=%{xrpld_version}; echo ${v%%-*})
%global _has_dash %(v=%{xrpld_version}; [ "${v#*-}" != "$v" ] && echo 1 || echo 0)
%if 0%{?_has_dash}
%global ver_suffix %(v=%{xrpld_version}; printf %s "${v#*-}")
%endif
Name: xrpld
Version: %{ver_base}
Release: %{?ver_suffix:0.%{ver_suffix}.}%{pkg_release}%{?dist}
Summary: XRP Ledger daemon
License: ISC
URL: https://github.com/XRPLF/rippled
Source0: xrpld
Source1: xrpld.cfg
Source2: validators.txt
Source3: xrpld.service
Source4: xrpld.sysusers
Source5: xrpld.tmpfiles
Source6: xrpld.logrotate
Source7: update-xrpld.sh
Source8: update-xrpld-cron
Source9: LICENSE.md
Source10: README.md
ExclusiveArch: x86_64
BuildRequires: systemd-rpm-macros
%undefine _debugsource_packages
%debug_package
%{?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.
%install
rm -rf %{buildroot}
# Suppress debugsource subpackage — no source files in the build tree.
touch %{_builddir}/debugsourcefiles.list
# Install binary and config files.
install -Dm0755 %{SOURCE0} %{buildroot}%{_opt_prefix}/bin/xrpld
install -Dm0644 %{SOURCE1} %{buildroot}%{_opt_prefix}/etc/xrpld.cfg
install -Dm0644 %{SOURCE2} %{buildroot}%{_opt_prefix}/etc/validators.txt
mkdir -p %{buildroot}/etc/opt %{buildroot}/usr/bin %{buildroot}/usr/local/bin
ln -s %{_opt_prefix}/etc %{buildroot}/etc/opt/xrpld
ln -s %{_opt_prefix}/bin/xrpld %{buildroot}/usr/bin/xrpld
## remove when "rippled" deprecated
ln -s xrpld %{buildroot}%{_opt_prefix}/bin/rippled
ln -s %{_opt_prefix}/bin/xrpld %{buildroot}/usr/local/bin/rippled
ln -s xrpld.cfg %{buildroot}%{_opt_prefix}/etc/rippled.cfg
ln -s %{_opt_prefix} %{buildroot}/opt/ripple
ln -s /etc/opt/xrpld %{buildroot}/etc/opt/ripple
# Install systemd/sysusers/tmpfiles support files.
install -Dm0644 %{SOURCE3} %{buildroot}%{_unitdir}/xrpld.service
install -Dm0644 %{SOURCE4} %{buildroot}%{_sysusersdir}/xrpld.conf
install -Dm0644 %{SOURCE5} %{buildroot}%{_tmpfilesdir}/xrpld.conf
install -Dm0644 %{SOURCE6} %{buildroot}%{_opt_prefix}/bin/xrpld.logrotate
install -Dm0755 %{SOURCE7} %{buildroot}%{_opt_prefix}/bin/update-xrpld.sh
install -Dm0644 %{SOURCE8} %{buildroot}%{_opt_prefix}/bin/update-xrpld-cron
# Install doc/license files.
install -Dm0644 %{SOURCE9} %{buildroot}%{_opt_prefix}/share/LICENSE.md
install -Dm0644 %{SOURCE10} %{buildroot}%{_opt_prefix}/share/README.md
%pre
%sysusers_create_compat %{SOURCE4}
%post
systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
%systemd_post xrpld.service
%preun
%systemd_preun xrpld.service
%postun
%systemd_postun_with_restart xrpld.service
%files
%license %{_opt_prefix}/share/LICENSE.md
%doc %{_opt_prefix}/share/README.md
%dir %{_opt_prefix}
%dir %{_opt_prefix}/bin
%dir %{_opt_prefix}/etc
%{_opt_prefix}/bin/xrpld
%{_opt_prefix}/bin/xrpld.logrotate
%{_opt_prefix}/bin/update-xrpld.sh
%{_opt_prefix}/bin/update-xrpld-cron
/usr/bin/xrpld
/etc/opt/xrpld
%config(noreplace) %{_opt_prefix}/etc/xrpld.cfg
%config(noreplace) %{_opt_prefix}/etc/validators.txt
%{_unitdir}/xrpld.service
%{_sysusersdir}/xrpld.conf
%{_tmpfilesdir}/xrpld.conf
%ghost %dir /var/lib/xrpld
%ghost %dir /var/log/xrpld
# TODO: remove when rippled deprecated
%{_opt_prefix}/bin/rippled
/usr/local/bin/rippled
%{_opt_prefix}/etc/rippled.cfg
/etc/opt/ripple
/opt/ripple

View File

@@ -0,0 +1,9 @@
# For automatic updates, symlink this file to /etc/cron.d/
# Do not remove the newline at the end of this cron script
# bash required for use of RANDOM below.
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
# invoke check/update script with random delay up to 59 mins
0 * * * * root sleep $((RANDOM*3540/32768)) && /opt/xrpld/bin/update-xrpld.sh

74
package/shared/update-xrpld.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# auto-update script for xrpld daemon
# Check for sudo/root permissions
if [[ $(id -u) -ne 0 ]] ; then
echo "This update script must be run as root or sudo"
exit 1
fi
LOCKDIR=/run/lock/xrpld-update.lock
UPDATELOG=/var/log/xrpld/update.log
function cleanup {
# If this directory isn't removed, future updates will fail.
rmdir "$LOCKDIR"
}
# Use mkdir to check if process is already running. mkdir is atomic, as against file create.
if ! mkdir "$LOCKDIR" 2>/dev/null; then
echo "$(date -u) lockdir exists - won't proceed." >> "$UPDATELOG"
exit 1
fi
trap cleanup EXIT
can_update=false
if command -v apt-get &>/dev/null; then
apt-get update -qq
if apt-get -s --only-upgrade install xrpld 2>/dev/null | grep -q '^Inst xrpld'; then
can_update=true
fi
function apply_update {
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq xrpld
}
elif command -v yum &>/dev/null; then
REPO=${REPO:-stable}
if [[ ! "$REPO" =~ ^(stable|unstable|nightly|develop)$ ]]; then
echo "Invalid REPO value: ${REPO}" >&2
exit 1
fi
yum --disablerepo=* --enablerepo="ripple-${REPO}" clean expire-cache
# yum check-update exits 100 when updates are available, 0 for none, 1 for errors.
yum check-update -q --enablerepo="ripple-${REPO}" xrpld
rc=$?
if [ $rc -eq 100 ]; then
can_update=true
elif [ $rc -ne 0 ]; then
echo "yum check-update failed with exit code $rc"
exit 1
fi
function apply_update {
yum update -y --enablerepo="ripple-${REPO}" xrpld
}
else
echo "No supported package manager found (apt-get or yum)"
exit 1
fi
# Do the actual update and restart the service after reloading systemctl daemon.
if [ "$can_update" = true ] ; then
exec >>"${UPDATELOG}" 2>&1
set -e
apply_update
systemctl daemon-reload
systemctl restart xrpld.service
echo "$(date -u) xrpld daemon updated."
else
echo "$(date -u) no updates available" >> "$UPDATELOG"
fi

View File

@@ -0,0 +1,15 @@
/var/log/xrpld/*.log {
daily
minsize 200M
rotate 7
nocreate
missingok
notifempty
compress
compresscmd /usr/bin/nice
compressoptions -n19 ionice -c3 gzip
compressext .gz
postrotate
/opt/xrpld/bin/xrpld --conf /etc/opt/xrpld/xrpld.cfg logrotate
endscript
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env sh
# Validate installed paths and compat symlinks for xrpld packages.
set -e
set -x
trap 'test $? -ne 0 && touch /tmp/test_failed' EXIT
check() { test $1 "$2" || { echo "FAIL: $1 $2"; exit 1; }; }
check_resolves_to() {
actual=$(readlink -f "$1")
[ "$actual" = "$2" ] || { echo "FAIL: $1 resolves to $actual, expected $2"; exit 1; }
}
# compat directory symlinks — existence and resolved target
check -L /opt/ripple
check_resolves_to /opt/ripple /opt/xrpld
check -L /etc/opt/xrpld
check_resolves_to /etc/opt/xrpld /opt/xrpld/etc
check -L /etc/opt/ripple
check_resolves_to /etc/opt/ripple /opt/xrpld/etc
# config accessible via all expected paths
check -f /opt/xrpld/etc/xrpld.cfg
check -f /opt/xrpld/etc/rippled.cfg
check -f /etc/opt/xrpld/xrpld.cfg
check -f /etc/opt/xrpld/rippled.cfg
check -f /etc/opt/ripple/xrpld.cfg
check -f /etc/opt/ripple/rippled.cfg
if systemctl is-system-running >/dev/null 2>&1; then
# service file sanity check
SERVICE=$(systemctl cat xrpld)
echo "$SERVICE" | grep -q 'ExecStart=/opt/xrpld/bin/xrpld' || { echo "FAIL: ExecStart wrong"; echo "$SERVICE"; exit 1; }
echo "$SERVICE" | grep -q 'User=xrpld' || { echo "FAIL: User not xrpld"; echo "$SERVICE"; exit 1; }
fi
# binary accessible via all expected paths
/opt/xrpld/bin/xrpld --version
/opt/xrpld/bin/rippled --version
/opt/ripple/bin/xrpld --version
/opt/ripple/bin/rippled --version
/usr/bin/xrpld --version
/usr/local/bin/rippled --version

88
package/test/smoketest.sh Executable file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Install a locally-built package and run basic verification.
#
# Usage: smoketest.sh local
# Expects packages in build/{dpkg,rpm}/packages/ or build/debbuild/ / build/rpmbuild/RPMS/
set -o pipefail
set -x
rm -f /tmp/test_failed /tmp/unittest_results
trap 'test $? -ne 0 && touch /tmp/test_failed' EXIT
install_from=$1
. /etc/os-release
case ${ID} in
ubuntu|debian)
pkgtype="dpkg"
;;
fedora|centos|rhel|rocky|almalinux)
pkgtype="rpm"
;;
*)
echo "unrecognized distro!"
exit 1
;;
esac
if [ "${install_from}" != "local" ]; then
echo "only 'local' install mode is supported"
exit 1
fi
# Install the package
if [ "${pkgtype}" = "dpkg" ] ; then
apt-get -y update
# Find .deb files — check both possible output locations
mapfile -t debs < <(find build/debbuild/ build/dpkg/packages/ -name '*.deb' ! -name '*dbgsym*' 2>/dev/null)
if [ ${#debs[@]} -eq 0 ]; then
echo "No .deb files found"
exit 1
fi
dpkg --no-debsig -i "${debs[@]}" || apt-get -y install -f
elif [ "${pkgtype}" = "rpm" ] ; then
# Find .rpm files — check both possible output locations
mapfile -t rpms < <(find build/rpmbuild/RPMS/ build/rpm/packages/ -name '*.rpm' \
! -name '*debug*' ! -name '*devel*' ! -name '*.src.rpm' 2>/dev/null)
if [ ${#rpms[@]} -eq 0 ]; then
echo "No .rpm files found"
exit 1
fi
rpm -i "${rpms[@]}"
fi
# Verify installed version
VERSION_OUTPUT=$(/opt/xrpld/bin/xrpld --version)
INSTALLED=$(echo "$VERSION_OUTPUT" | head -1 | awk '{print $NF}')
echo "Installed version: ${INSTALLED}"
# Run unit tests
if [ -n "${CI:-}" ]; then
unittest_jobs=$(nproc)
else
unittest_jobs=16
fi
(
cd /tmp
/opt/xrpld/bin/xrpld --unittest --unittest-jobs "${unittest_jobs}" > /tmp/unittest_results || true
)
if [ ! -s /tmp/unittest_results ]; then
echo "Unit test results file is empty — xrpld may have crashed"
exit 1
fi
num_failures=$(tail /tmp/unittest_results -n1 | grep -oP '\d+(?= failures)')
if [ -z "$num_failures" ]; then
echo "Could not parse unit test results — expected summary line not found"
exit 1
fi
if [ "${num_failures}" -ne 0 ]; then
echo "$num_failures unit test(s) failed:"
grep 'failed:' /tmp/unittest_results
exit 1
fi
# Compat path checks
"$(dirname "${BASH_SOURCE[0]}")/check_install_paths.sh"

View File

@@ -45,7 +45,7 @@ getFileContents(
return {};
}
std::string const result{
std::string result{
std::istreambuf_iterator<char>{fileStream}, std::istreambuf_iterator<char>{}};
if (fileStream.bad())

View File

@@ -1,4 +1,4 @@
#include <xrpld/app/misc/CanonicalTXSet.h>
#include <xrpl/ledger/CanonicalTXSet.h>
namespace xrpl {

View File

@@ -1,19 +1,9 @@
#include <xrpld/app/ledger/InboundLedgers.h>
#include <xrpld/app/ledger/Ledger.h>
#include <xrpld/app/ledger/LedgerToJson.h>
#include <xrpld/app/ledger/PendingSaves.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/consensus/LedgerTiming.h>
#include <xrpld/core/Config.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/HashRouter.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/json/to_string.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/detail/DatabaseNodeImp.h>
#include <xrpl/ledger/Ledger.h>
#include <xrpl/ledger/LedgerTiming.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/Indexes.h>
@@ -21,7 +11,6 @@
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/rdb/RelationalDatabase.h>
#include <utility>
#include <vector>
@@ -30,23 +19,6 @@ namespace xrpl {
create_genesis_t const create_genesis{};
uint256
calculateLedgerHash(LedgerHeader const& info)
{
// VFALCO This has to match addRaw in View.h.
return sha512Half(
HashPrefix::ledgerMaster,
std::uint32_t(info.seq),
std::uint64_t(info.drops.drops()),
info.parentHash,
info.txHash,
info.accountHash,
std::uint32_t(info.parentCloseTime.time_since_epoch().count()),
std::uint32_t(info.closeTime.time_since_epoch().count()),
std::uint8_t(info.closeTimeResolution.count()),
std::uint8_t(info.closeFlags));
}
//------------------------------------------------------------------------------
class Ledger::sles_iter_impl : public sles_type::iter_base
@@ -108,8 +80,7 @@ public:
txs_iter_impl(txs_iter_impl const&) = default;
txs_iter_impl(bool metadata, SHAMap::const_iterator iter)
: metadata_(metadata), iter_(std::move(iter))
txs_iter_impl(bool metadata, SHAMap::const_iterator iter) : metadata_(metadata), iter_(iter)
{
}
@@ -138,8 +109,8 @@ public:
{
auto const& item = *iter_;
if (metadata_)
return deserializeTxPlusMeta(item);
return {deserializeTx(item), nullptr};
return Ledger::deserializeTxPlusMeta(item);
return {Ledger::deserializeTx(item), nullptr};
}
};
@@ -147,13 +118,15 @@ public:
Ledger::Ledger(
create_genesis_t,
Config const& config,
Rules const& rules,
Fees const& fees,
std::vector<uint256> const& amendments,
Family& family)
: mImmutable(false)
, txMap_(SHAMapType::TRANSACTION, family)
, stateMap_(SHAMapType::STATE, family)
, rules_{config.features}
, fees_(fees)
, rules_(rules)
, j_(beast::Journal(beast::Journal::getNullSink()))
{
header_.seq = 1;
@@ -182,19 +155,19 @@ Ledger::Ledger(
// Whether featureXRPFees is supported will depend on startup options.
if (std::find(amendments.begin(), amendments.end(), featureXRPFees) != amendments.end())
{
sle->at(sfBaseFeeDrops) = config.FEES.reference_fee;
sle->at(sfReserveBaseDrops) = config.FEES.account_reserve;
sle->at(sfReserveIncrementDrops) = config.FEES.owner_reserve;
sle->at(sfBaseFeeDrops) = fees.base;
sle->at(sfReserveBaseDrops) = fees.reserve;
sle->at(sfReserveIncrementDrops) = fees.increment;
}
else
{
if (auto const f = config.FEES.reference_fee.dropsAs<std::uint64_t>())
if (auto const f = fees.base.dropsAs<std::uint64_t>())
sle->at(sfBaseFee) = *f;
if (auto const f = config.FEES.account_reserve.dropsAs<std::uint32_t>())
if (auto const f = fees.reserve.dropsAs<std::uint32_t>())
sle->at(sfReserveBase) = *f;
if (auto const f = config.FEES.owner_reserve.dropsAs<std::uint32_t>())
if (auto const f = fees.increment.dropsAs<std::uint32_t>())
sle->at(sfReserveIncrement) = *f;
sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED;
sle->at(sfReferenceFeeUnits) = FEE_UNITS_DEPRECATED;
}
rawInsert(sle);
}
@@ -207,13 +180,15 @@ Ledger::Ledger(
LedgerHeader const& info,
bool& loaded,
bool acquire,
Config const& config,
Rules const& rules,
Fees const& fees,
Family& family,
beast::Journal j)
: mImmutable(true)
, txMap_(SHAMapType::TRANSACTION, info.txHash, family)
, stateMap_(SHAMapType::STATE, info.accountHash, family)
, rules_(config.features)
, fees_(fees)
, rules_(rules)
, header_(info)
, j_(j)
{
@@ -235,7 +210,6 @@ Ledger::Ledger(
txMap_.setImmutable();
stateMap_.setImmutable();
defaultFees(config);
if (!setup())
loaded = false;
@@ -275,11 +249,11 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime)
}
}
Ledger::Ledger(LedgerHeader const& info, Config const& config, Family& family)
Ledger::Ledger(LedgerHeader const& info, Rules const& rules, Family& family)
: mImmutable(true)
, txMap_(SHAMapType::TRANSACTION, info.txHash, family)
, stateMap_(SHAMapType::STATE, info.accountHash, family)
, rules_{config.features}
, rules_(rules)
, header_(info)
, j_(beast::Journal(beast::Journal::getNullSink()))
{
@@ -289,18 +263,19 @@ Ledger::Ledger(LedgerHeader const& info, Config const& config, Family& family)
Ledger::Ledger(
std::uint32_t ledgerSeq,
NetClock::time_point closeTime,
Config const& config,
Rules const& rules,
Fees const& fees,
Family& family)
: mImmutable(false)
, txMap_(SHAMapType::TRANSACTION, family)
, stateMap_(SHAMapType::STATE, family)
, rules_{config.features}
, fees_(fees)
, rules_(rules)
, j_(beast::Journal(beast::Journal::getNullSink()))
{
header_.seq = ledgerSeq;
header_.closeTime = closeTime;
header_.closeTimeResolution = ledgerDefaultTimeResolution;
defaultFees(config);
setup();
}
@@ -350,14 +325,14 @@ Ledger::addSLE(SLE const& sle)
//------------------------------------------------------------------------------
std::shared_ptr<STTx const>
deserializeTx(SHAMapItem const& item)
Ledger::deserializeTx(SHAMapItem const& item)
{
SerialIter sit(item.slice());
return std::make_shared<STTx const>(sit);
}
std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>>
deserializeTxPlusMeta(SHAMapItem const& item)
Ledger::deserializeTxPlusMeta(SHAMapItem const& item)
{
std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>> result;
SerialIter sit(item.slice());
@@ -636,20 +611,6 @@ Ledger::setup()
return ret;
}
void
Ledger::defaultFees(Config const& config)
{
XRPL_ASSERT(
fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0,
"xrpl::Ledger::defaultFees : zero fees");
if (fees_.base == 0)
fees_.base = config.FEES.reference_fee;
if (fees_.reserve == 0)
fees_.reserve = config.FEES.account_reserve;
if (fees_.increment == 0)
fees_.increment = config.FEES.owner_reserve;
}
std::shared_ptr<SLE>
Ledger::peek(Keylet const& k) const
{
@@ -732,7 +693,7 @@ Ledger::updateNegativeUNL()
if (sle->isFieldPresent(sfDisabledValidators))
{
auto const& oldNUnl = sle->getFieldArray(sfDisabledValidators);
for (auto v : oldNUnl)
for (auto const& v : oldNUnl)
{
if (hasToReEnable && v.isFieldPresent(sfPublicKey) &&
v.getFieldVL(sfPublicKey) == sle->getFieldVL(sfValidatorToReEnable))
@@ -816,27 +777,17 @@ Ledger::walkLedger(beast::Journal j, bool parallel) const
}
bool
Ledger::assertSensible(beast::Journal ledgerJ) const
Ledger::isSensible() const
{
if (header_.hash.isNonZero() && header_.accountHash.isNonZero() &&
(header_.accountHash == stateMap_.getHash().as_uint256()) &&
(header_.txHash == txMap_.getHash().as_uint256()))
{
return true;
}
// LCOV_EXCL_START
Json::Value j = getJson({*this, {}});
j[jss::accountTreeHash] = to_string(header_.accountHash);
j[jss::transTreeHash] = to_string(header_.txHash);
JLOG(ledgerJ.fatal()) << "ledger is not sensible" << j;
UNREACHABLE("xrpl::Ledger::assertSensible : ledger is not sensible");
return false;
// LCOV_EXCL_STOP
if (header_.hash.isZero())
return false;
if (header_.accountHash.isZero())
return false;
if (header_.accountHash != stateMap_.getHash().as_uint256())
return false;
if (header_.txHash != txMap_.getHash().as_uint256())
return false;
return true;
}
// update the skip list with the information from our previous ledger
@@ -925,76 +876,6 @@ Ledger::isVotingLedger() const
return ::xrpl::isVotingLedger(header_.seq + 1);
}
static bool
saveValidatedLedger(Application& app, std::shared_ptr<Ledger const> const& ledger, bool current)
{
auto j = app.journal("Ledger");
auto seq = ledger->header().seq;
if (!app.pendingSaves().startWork(seq))
{
// The save was completed synchronously
JLOG(j.debug()) << "Save aborted";
return true;
}
auto& db = app.getRelationalDatabase();
auto const res = db.saveValidatedLedger(ledger, current);
// Clients can now trust the database for
// information about this ledger sequence.
app.pendingSaves().finishWork(seq);
return res;
}
/** Save, or arrange to save, a fully-validated ledger
Returns false on error
*/
bool
pendSaveValidated(
Application& app,
std::shared_ptr<Ledger const> const& ledger,
bool isSynchronous,
bool isCurrent)
{
if (!app.getHashRouter().setFlags(ledger->header().hash, HashRouterFlags::SAVED))
{
// We have tried to save this ledger recently
auto stream = app.journal("Ledger").debug();
JLOG(stream) << "Double pend save for " << ledger->header().seq;
if (!isSynchronous || !app.pendingSaves().pending(ledger->header().seq))
{
// Either we don't need it to be finished
// or it is finished
return true;
}
}
XRPL_ASSERT(ledger->isImmutable(), "xrpl::pendSaveValidated : immutable ledger");
if (!app.pendingSaves().shouldWork(ledger->header().seq, isSynchronous))
{
auto stream = app.journal("Ledger").debug();
JLOG(stream) << "Pend save with seq in pending saves " << ledger->header().seq;
return true;
}
// See if we can use the JobQueue.
if (!isSynchronous &&
app.getJobQueue().addJob(
isCurrent ? jtPUBLEDGER : jtPUBOLDLEDGER,
std::to_string(ledger->seq()),
[&app, ledger, isCurrent]() { saveValidatedLedger(app, ledger, isCurrent); }))
{
return true;
}
// The JobQueue won't do the Job. Do the save synchronously.
return saveValidatedLedger(app, ledger, isCurrent);
}
void
Ledger::unshare() const
{
@@ -1008,84 +889,5 @@ Ledger::invariants() const
stateMap_.invariants();
txMap_.invariants();
}
//------------------------------------------------------------------------------
/*
* Make ledger using info loaded from database.
*
* @param LedgerHeader: Ledger information.
* @param app: Link to the Application.
* @param acquire: Acquire the ledger if not found locally.
* @return Shared pointer to the ledger.
*/
std::shared_ptr<Ledger>
loadLedgerHelper(LedgerHeader const& info, Application& app, bool acquire)
{
bool loaded = false;
auto ledger = std::make_shared<Ledger>(
info, loaded, acquire, app.config(), app.getNodeFamily(), app.journal("Ledger"));
if (!loaded)
ledger.reset();
return ledger;
}
static void
finishLoadByIndexOrHash(
std::shared_ptr<Ledger> const& ledger,
Config const& config,
beast::Journal j)
{
if (!ledger)
return;
XRPL_ASSERT(
ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()),
"xrpl::finishLoadByIndexOrHash : valid ledger fees");
ledger->setImmutable();
JLOG(j.trace()) << "Loaded ledger: " << to_string(ledger->header().hash);
ledger->setFull();
}
std::tuple<std::shared_ptr<Ledger>, std::uint32_t, uint256>
getLatestLedger(Application& app)
{
std::optional<LedgerHeader> const info = app.getRelationalDatabase().getNewestLedgerInfo();
if (!info)
return {std::shared_ptr<Ledger>(), {}, {}};
return {loadLedgerHelper(*info, app, true), info->seq, info->hash};
}
std::shared_ptr<Ledger>
loadByIndex(std::uint32_t ledgerIndex, Application& app, bool acquire)
{
if (std::optional<LedgerHeader> info =
app.getRelationalDatabase().getLedgerInfoByIndex(ledgerIndex))
{
std::shared_ptr<Ledger> ledger = loadLedgerHelper(*info, app, acquire);
finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger"));
return ledger;
}
return {};
}
std::shared_ptr<Ledger>
loadByHash(uint256 const& ledgerHash, Application& app, bool acquire)
{
if (std::optional<LedgerHeader> info =
app.getRelationalDatabase().getLedgerInfoByHash(ledgerHash))
{
std::shared_ptr<Ledger> ledger = loadLedgerHelper(*info, app, acquire);
finishLoadByIndexOrHash(ledger, app.config(), app.journal("Ledger"));
XRPL_ASSERT(
!ledger || ledger->header().hash == ledgerHash,
"xrpl::loadByHash : ledger hash match if loaded");
return ledger;
}
return {};
}
} // namespace xrpl

View File

@@ -161,7 +161,7 @@ getLineIfUsable(
FreezeHandling zeroIfFrozen,
beast::Journal j)
{
auto const sle = view.read(keylet::line(account, issuer, currency));
auto sle = view.read(keylet::line(account, issuer, currency));
if (!sle)
{

View File

@@ -113,7 +113,7 @@ encodeSoftwareVersion(std::string_view versionStr)
{
std::uint8_t x = 0;
for (auto id : v.preReleaseIdentifiers)
for (auto const& id : v.preReleaseIdentifiers)
{
auto parsePreRelease = [](std::string_view identifier,
std::string_view prefix,

View File

@@ -1,7 +1,9 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/protocol/HashPrefix.h>
#include <xrpl/protocol/LedgerHeader.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/digest.h>
namespace xrpl {
@@ -51,4 +53,21 @@ deserializePrefixedHeader(Slice data, bool hasHash)
return deserializeHeader(data + 4, hasHash);
}
uint256
calculateLedgerHash(LedgerHeader const& info)
{
// VFALCO This has to match addRaw in View.h.
return sha512Half(
HashPrefix::ledgerMaster,
std::uint32_t(info.seq),
std::uint64_t(info.drops.drops()),
info.parentHash,
info.txHash,
info.accountHash,
std::uint32_t(info.parentCloseTime.time_since_epoch().count()),
std::uint32_t(info.closeTime.time_since_epoch().count()),
std::uint8_t(info.closeTimeResolution.count()),
std::uint8_t(info.closeFlags));
}
} // namespace xrpl

View File

@@ -153,7 +153,7 @@ STPath::getJson(JsonOptions) const
{
Json::Value ret(Json::arrayValue);
for (auto it : mPath)
for (auto const& it : mPath)
{
Json::Value elem(Json::objectValue);
auto const iType = it.getNodeType();
@@ -179,7 +179,7 @@ Json::Value
STPathSet::getJson(JsonOptions options) const
{
Json::Value ret(Json::arrayValue);
for (auto it : value)
for (auto const& it : value)
ret.append(it.getJson(options));
return ret;

View File

@@ -1,5 +1,5 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/rdb/DatabaseCon.h>
#include <xrpl/rdb/SociDB.h>
@@ -29,7 +29,7 @@ public:
auto it = checkpointers_.find(id);
if (it != checkpointers_.end())
return it->second;
return {};
return nullptr;
}
void
@@ -40,11 +40,14 @@ public:
}
std::shared_ptr<Checkpointer>
create(std::shared_ptr<soci::session> const& session, JobQueue& jobQueue, Logs& logs)
create(
std::shared_ptr<soci::session> const& session,
JobQueue& jobQueue,
ServiceRegistry& registry)
{
std::lock_guard lock{mutex_};
auto const id = nextId_++;
auto const r = makeCheckpointer(id, session, jobQueue, logs);
auto const r = makeCheckpointer(id, session, jobQueue, registry);
checkpointers_[id] = r;
return r;
}
@@ -82,11 +85,11 @@ DatabaseCon::~DatabaseCon()
std::unique_ptr<std::vector<std::string> const> DatabaseCon::Setup::globalPragma;
void
DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l)
DatabaseCon::setupCheckpointing(JobQueue* q, ServiceRegistry& registry)
{
if (q == nullptr)
Throw<std::logic_error>("No JobQueue");
checkpointer_ = checkpointers.create(session_, *q, l);
checkpointer_ = checkpointers.create(session_, *q, registry);
}
} // namespace xrpl

View File

@@ -187,8 +187,11 @@ public:
std::uintptr_t id,
std::weak_ptr<soci::session> session,
JobQueue& q,
Logs& logs)
: id_(id), session_(std::move(session)), jobQueue_(q), j_(logs.journal("WALCheckpointer"))
ServiceRegistry& registry)
: id_(id)
, session_(std::move(session))
, jobQueue_(q)
, j_(registry.getJournal("WALCheckpointer"))
{
if (auto [conn, keepAlive] = getConnection(); conn)
{
@@ -307,9 +310,9 @@ makeCheckpointer(
std::uintptr_t id,
std::weak_ptr<soci::session> session,
JobQueue& queue,
Logs& logs)
ServiceRegistry& registry)
{
return std::make_shared<WALCheckpointer>(id, std::move(session), queue, logs);
return std::make_shared<WALCheckpointer>(id, std::move(session), queue, registry);
}
} // namespace xrpl

View File

@@ -1021,10 +1021,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t)
// save our place and work on this node
stack.emplace(std::move(node), branch);
// The semantics of this changes when we move to c++-20
// Right now no move will occur; With c++-20 child will
// be moved from.
node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(std::move(child));
node = intr_ptr::static_pointer_cast<SHAMapInnerNode>(child);
pos = 0;
}
else

View File

@@ -12,8 +12,6 @@ namespace xrpl {
Expected<std::vector<SignerEntries::SignerEntry>, NotTEC>
SignerEntries::deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation)
{
std::pair<std::vector<SignerEntry>, NotTEC> s;
if (!obj.isFieldPresent(sfSignerEntries))
{
JLOG(journal.trace()) << "Malformed " << annotation << ": Need signer entry array.";

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/core/NetworkIDService.h>
#include <xrpl/json/to_string.h>
@@ -35,7 +34,7 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask)
if (!isPseudoTx(ctx.tx) || ctx.tx.isFieldPresent(sfNetworkID))
{
uint32_t nodeNID = ctx.registry.getNetworkIDService().getNetworkID();
uint32_t nodeNID = ctx.registry.get().getNetworkIDService().getNetworkID();
std::optional<uint32_t> txNID = ctx.tx[~sfNetworkID];
if (nodeNID <= 1024)
@@ -212,7 +211,7 @@ Transactor::preflight2(PreflightContext const& ctx)
// Do not add any checks after this point that are relevant for
// batch inner transactions. They will be skipped.
auto const sigValid = checkValidity(ctx.registry.getHashRouter(), ctx.tx, ctx.rules);
auto const sigValid = checkValidity(ctx.registry.get().getHashRouter(), ctx.tx, ctx.rules);
if (sigValid.first == Validity::SigBad)
{ // LCOV_EXCL_START
JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second;
@@ -302,7 +301,7 @@ Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx)
// need to rethink charging an owner reserve as a transaction fee.
// TODO: This function is static, and I don't want to add more parameters.
// When it is finally refactored to be in a context that has access to the
// Application, include "app().overlay().networkID() > 2 ||" in the
// Application, include "app().getOverlay().networkID() > 2 ||" in the
// condition.
XRPL_ASSERT(
view.fees().increment > view.fees().base * 100,
@@ -677,8 +676,7 @@ Transactor::checkSign(
}
// Look up the account.
auto const idSigner =
pkSigner.empty() ? idAccount : calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = view.read(keylet::account(idAccount));
if (!sleAccount)
return terNO_ACCOUNT;
@@ -1097,7 +1095,8 @@ Transactor::operator()()
}
#endif
if (auto const& trap = ctx_.registry.trapTxID(); trap && *trap == ctx_.tx.getTransactionID())
if (auto const& trap = ctx_.registry.get().getTrapTxID();
trap && *trap == ctx_.tx.getTransactionID())
{
trapTransaction(*trap);
}
@@ -1199,16 +1198,27 @@ Transactor::operator()()
// If necessary, remove any offers found unfunded during processing
if ((result == tecOVERSIZE) || (result == tecKILLED))
removeUnfundedOffers(view(), removedOffers, ctx_.registry.journal("View"));
{
removeUnfundedOffers(view(), removedOffers, ctx_.registry.get().getJournal("View"));
}
if (result == tecEXPIRED)
removeExpiredNFTokenOffers(view(), expiredNFTokenOffers, ctx_.registry.journal("View"));
{
removeExpiredNFTokenOffers(
view(), expiredNFTokenOffers, ctx_.registry.get().getJournal("View"));
}
if (result == tecINCOMPLETE)
removeDeletedTrustLines(view(), removedTrustLines, ctx_.registry.journal("View"));
{
removeDeletedTrustLines(
view(), removedTrustLines, ctx_.registry.get().getJournal("View"));
}
if (result == tecEXPIRED)
removeExpiredCredentials(view(), expiredCredentials, ctx_.registry.journal("View"));
{
removeExpiredCredentials(
view(), expiredCredentials, ctx_.registry.get().getJournal("View"));
}
applied = isTecClaim(result);
}

View File

@@ -0,0 +1,194 @@
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
//
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/TxFormats.h>
namespace xrpl {
void
ValidLoanBroker::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
if (after)
{
if (after->getType() == ltLOAN_BROKER)
{
auto& broker = brokers_[after->key()];
broker.brokerBefore = before;
broker.brokerAfter = after;
}
else if (after->getType() == ltACCOUNT_ROOT && after->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = after->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
else if (after->getType() == ltRIPPLE_STATE)
{
lines_.emplace_back(after);
}
else if (after->getType() == ltMPTOKEN)
{
mpts_.emplace_back(after);
}
}
}
bool
ValidLoanBroker::goodZeroDirectory(
ReadView const& view,
SLE::const_ref dir,
beast::Journal const& j)
{
auto const next = dir->at(~sfIndexNext);
auto const prev = dir->at(~sfIndexPrevious);
if ((prev && (*prev != 0u)) || (next && (*next != 0u)))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has multiple directory pages";
return false;
}
auto indexes = dir->getFieldV256(sfIndexes);
if (indexes.size() > 1)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has multiple indexes in the Directory root";
return false;
}
if (indexes.size() == 1)
{
auto const index = indexes.value().front();
auto const sle = view.read(keylet::unchecked(index));
if (!sle)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker directory corrupt";
return false;
}
if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has an unexpected entry in the directory";
return false;
}
}
return true;
}
bool
ValidLoanBroker::finalize(
STTx const& tx,
TER const,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
// Loan Brokers will not exist on ledger if the Lending Protocol amendment
// is not enabled, so there's no need to check it.
for (auto const& line : lines_)
{
for (auto const& field : {&sfLowLimit, &sfHighLimit})
{
auto const account = view.read(keylet::account(line->at(*field).getIssuer()));
// This Invariant doesn't know about the rules for Trust Lines, so
// if the account is missing, don't treat it as an error. This
// loop is only concerned with finding Broker pseudo-accounts
if (account && account->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = account->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
}
}
for (auto const& mpt : mpts_)
{
auto const account = view.read(keylet::account(mpt->at(sfAccount)));
// This Invariant doesn't know about the rules for MPTokens, so
// if the account is missing, don't treat is as an error. This
// loop is only concerned with finding Broker pseudo-accounts
if (account && account->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = account->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
}
for (auto const& [brokerID, broker] : brokers_)
{
auto const& after =
broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID));
if (!after)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker missing";
return false;
}
auto const& before = broker.brokerBefore;
// https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants
// If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most
// one node (the root), which will only hold entries for `RippleState`
// or `MPToken` objects.
if (after->at(sfOwnerCount) == 0)
{
auto const dir = view.read(keylet::ownerDir(after->at(sfAccount)));
if (dir)
{
if (!goodZeroDirectory(view, dir, j))
{
return false;
}
}
}
if (before && before->at(sfLoanSequence) > after->at(sfLoanSequence))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker sequence number "
"decreased";
return false;
}
if (after->at(sfDebtTotal) < 0)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker debt total is negative";
return false;
}
if (after->at(sfCoverAvailable) < 0)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is negative";
return false;
}
auto const vault = view.read(keylet::vault(after->at(sfVaultID)));
if (!vault)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker vault ID is invalid";
return false;
}
auto const& vaultAsset = vault->at(sfAsset);
if (after->at(sfCoverAvailable) < accountHolds(
view,
after->at(sfAccount),
vaultAsset,
FreezeHandling::fhIGNORE_FREEZE,
AuthHandling::ahIGNORE_AUTH,
j))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available "
"is less than pseudo-account asset balance";
return false;
}
}
return true;
}
} // namespace xrpl

View File

@@ -2,197 +2,11 @@
//
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/TxFormats.h>
namespace xrpl {
void
ValidLoanBroker::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
if (after)
{
if (after->getType() == ltLOAN_BROKER)
{
auto& broker = brokers_[after->key()];
broker.brokerBefore = before;
broker.brokerAfter = after;
}
else if (after->getType() == ltACCOUNT_ROOT && after->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = after->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
else if (after->getType() == ltRIPPLE_STATE)
{
lines_.emplace_back(after);
}
else if (after->getType() == ltMPTOKEN)
{
mpts_.emplace_back(after);
}
}
}
bool
ValidLoanBroker::goodZeroDirectory(
ReadView const& view,
SLE::const_ref dir,
beast::Journal const& j)
{
auto const next = dir->at(~sfIndexNext);
auto const prev = dir->at(~sfIndexPrevious);
if ((prev && (*prev != 0u)) || (next && (*next != 0u)))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has multiple directory pages";
return false;
}
auto indexes = dir->getFieldV256(sfIndexes);
if (indexes.size() > 1)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has multiple indexes in the Directory root";
return false;
}
if (indexes.size() == 1)
{
auto const index = indexes.value().front();
auto const sle = view.read(keylet::unchecked(index));
if (!sle)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker directory corrupt";
return false;
}
if (sle->getType() != ltRIPPLE_STATE && sle->getType() != ltMPTOKEN)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker with zero "
"OwnerCount has an unexpected entry in the directory";
return false;
}
}
return true;
}
bool
ValidLoanBroker::finalize(
STTx const& tx,
TER const,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
// Loan Brokers will not exist on ledger if the Lending Protocol amendment
// is not enabled, so there's no need to check it.
for (auto const& line : lines_)
{
for (auto const& field : {&sfLowLimit, &sfHighLimit})
{
auto const account = view.read(keylet::account(line->at(*field).getIssuer()));
// This Invariant doesn't know about the rules for Trust Lines, so
// if the account is missing, don't treat it as an error. This
// loop is only concerned with finding Broker pseudo-accounts
if (account && account->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = account->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
}
}
for (auto const& mpt : mpts_)
{
auto const account = view.read(keylet::account(mpt->at(sfAccount)));
// This Invariant doesn't know about the rules for MPTokens, so
// if the account is missing, don't treat is as an error. This
// loop is only concerned with finding Broker pseudo-accounts
if (account && account->isFieldPresent(sfLoanBrokerID))
{
auto const& loanBrokerID = account->at(sfLoanBrokerID);
// create an entry if one doesn't already exist
brokers_.emplace(loanBrokerID, BrokerInfo{});
}
}
for (auto const& [brokerID, broker] : brokers_)
{
auto const& after =
broker.brokerAfter ? broker.brokerAfter : view.read(keylet::loanbroker(brokerID));
if (!after)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker missing";
return false;
}
auto const& before = broker.brokerBefore;
// https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants
// If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most
// one node (the root), which will only hold entries for `RippleState`
// or `MPToken` objects.
if (after->at(sfOwnerCount) == 0)
{
auto const dir = view.read(keylet::ownerDir(after->at(sfAccount)));
if (dir)
{
if (!goodZeroDirectory(view, dir, j))
{
return false;
}
}
}
if (before && before->at(sfLoanSequence) > after->at(sfLoanSequence))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker sequence number "
"decreased";
return false;
}
if (after->at(sfDebtTotal) < 0)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker debt total is negative";
return false;
}
if (after->at(sfCoverAvailable) < 0)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available is negative";
return false;
}
auto const vault = view.read(keylet::vault(after->at(sfVaultID)));
if (!vault)
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker vault ID is invalid";
return false;
}
auto const& vaultAsset = vault->at(sfAsset);
if (after->at(sfCoverAvailable) < accountHolds(
view,
after->at(sfAccount),
vaultAsset,
FreezeHandling::fhIGNORE_FREEZE,
AuthHandling::ahIGNORE_AUTH,
j))
{
JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available "
"is less than pseudo-account asset balance";
return false;
}
}
return true;
}
//------------------------------------------------------------------------------
void
ValidLoan::visitEntry(
bool isDelete,
@@ -236,8 +50,7 @@ ValidLoan::finalize(
after->at(sfPrincipalOutstanding) == beast::zero &&
after->at(sfManagementFeeOutstanding) == beast::zero)
{
JLOG(j.fatal()) << "Invariant failed: Loan with zero payments "
"remaining has not been paid off";
JLOG(j.fatal()) << "Invariant failed: Fully paid off Loan still has payments remaining";
return false;
}
if (before && (before->isFlag(lsfLoanOverpayment) != after->isFlag(lsfLoanOverpayment)))

View File

@@ -38,7 +38,7 @@ ValidPermissionedDomain::visitEntry(
break;
}
}
sleStatus.emplace_back(std::move(ss));
sleStatus.emplace_back(ss);
};
if (after)

View File

@@ -205,7 +205,7 @@ ValidVault::finalize(
for (auto const& e : beforeMPTs_)
{
if (e.share.getMptID() == beforeVault.shareMPTID)
return std::move(e);
return e;
}
return std::nullopt;
}();
@@ -374,7 +374,7 @@ ValidVault::finalize(
for (auto const& e : beforeMPTs_)
{
if (e.share.getMptID() == beforeVault.shareMPTID)
return std::move(e);
return e;
}
return std::nullopt;
}();

View File

@@ -1,5 +1,5 @@
#include <xrpld/app/paths/AMMLiquidity.h>
#include <xrpld/app/paths/AMMOffer.h>
#include <xrpl/tx/paths/AMMLiquidity.h>
#include <xrpl/tx/paths/AMMOffer.h>
namespace xrpl {

View File

@@ -1,7 +1,6 @@
#include <xrpld/app/paths/AMMLiquidity.h>
#include <xrpld/app/paths/AMMOffer.h>
#include <xrpl/protocol/QualityFunction.h>
#include <xrpl/tx/paths/AMMLiquidity.h>
#include <xrpl/tx/paths/AMMOffer.h>
namespace xrpl {

View File

@@ -1,6 +1,3 @@
#include <xrpld/app/paths/AMMLiquidity.h>
#include <xrpld/app/paths/AMMOffer.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
@@ -11,6 +8,8 @@
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/AMMLiquidity.h>
#include <xrpl/tx/paths/AMMOffer.h>
#include <xrpl/tx/paths/OfferStream.h>
#include <xrpl/tx/paths/detail/FlatSets.h>
#include <xrpl/tx/paths/detail/Steps.h>

View File

@@ -1,5 +1,3 @@
#include <xrpld/app/paths/detail/StepChecks.h>
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -7,6 +5,7 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/tx/paths/detail/StepChecks.h>
#include <xrpl/tx/paths/detail/Steps.h>
#include <boost/container/flat_set.hpp>

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/tx/paths/Flow.h>
@@ -35,12 +34,12 @@ RippleCalc::rippleCalculate(
STPathSet const& spsPaths,
std::optional<uint256> const& domainID,
Logs& l,
ServiceRegistry& registry,
Input const* const pInputs)
{
Output flowOut;
PaymentSandbox flowSB(&view);
auto j = l.journal("Flow");
auto j = registry.getJournal("Flow");
{
bool const defaultPaths = (pInputs == nullptr) ? true : pInputs->defaultPathsAllowed;

View File

@@ -1,5 +1,3 @@
#include <xrpld/app/paths/detail/StepChecks.h>
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -9,6 +7,7 @@
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/paths/detail/AmountSpec.h>
#include <xrpl/tx/paths/detail/StepChecks.h>
#include <xrpl/tx/paths/detail/Steps.h>
#include <boost/container/flat_set.hpp>

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -197,7 +196,7 @@ removeSignersFromLedger(
}
adjustOwnerCount(
view, view.peek(accountKeylet), removeFromOwnerCount, registry.journal("View"));
view, view.peek(accountKeylet), removeFromOwnerCount, registry.getJournal("View"));
view.erase(signers);
@@ -315,7 +314,7 @@ SignerListSet::replaceSignerList()
view().insert(signerList);
writeSignersToSLE(signerList, flags);
auto viewJ = ctx_.registry.journal("View");
auto viewJ = ctx_.registry.get().getJournal("View");
// Add the signer list to the account's directory.
auto const page =
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));

View File

@@ -418,7 +418,7 @@ transferHelper(
auto const reserve = psb.fees().accountReserve(ownerCount);
auto const availableBalance = [&]() -> STAmount {
STAmount const curBal = (*sleSrc)[sfBalance];
STAmount curBal = (*sleSrc)[sfBalance];
// Checking that account == src and postFeeBalance == curBal is
// not strictly necessary, but helps protect against future
// changes

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -58,7 +57,7 @@ CheckCancel::doApply()
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
AccountID const dstId{sleCheck->getAccountID(sfDestination)};
auto viewJ = ctx_.registry.journal("View");
auto viewJ = ctx_.registry.get().getJournal("View");
// If the check is not written to self (and it shouldn't be), remove the
// check from the destination account root.

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/scope.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -233,7 +232,7 @@ CheckCash::doApply()
//
// If it is not a check to self (as should be the case), then there's
// work to do...
auto viewJ = ctx_.registry.journal("View");
auto viewJ = ctx_.registry.get().getJournal("View");
auto const optDeliverMin = ctx_.tx[~sfDeliverMin];
if (srcId != account_)

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -169,7 +168,7 @@ CheckCreate::doApply()
view().insert(sleCheck);
auto viewJ = ctx_.registry.journal("View");
auto viewJ = ctx_.registry.get().getJournal("View");
// If it's not a self-send (and it shouldn't be), add Check to the
// destination's owner directory.
if (dstAccountId != account_)

View File

@@ -281,7 +281,7 @@ applyCreate(ApplyContext& ctx_, Sandbox& sb, AccountID const& account_, beast::J
Book const book{issueIn, issueOut, std::nullopt};
auto const dir = keylet::quality(keylet::book(book), uRate);
if (auto const bookExisted = static_cast<bool>(sb.read(dir)); !bookExisted)
ctx_.registry.getOrderBookDB().addOrderBook(book);
ctx_.registry.get().getOrderBookDB().addOrderBook(book);
};
addOrderBook(amount.issue(), amount2.issue(), getRate(amount2, amount));
addOrderBook(amount2.issue(), amount.issue(), getRate(amount, amount2));

View File

@@ -1,4 +1,3 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/OfferHelpers.h>
#include <xrpl/protocol/st.h>
@@ -54,7 +53,7 @@ OfferCancel::doApply()
if (auto sleOffer = view().peek(keylet::offer(account_, offerSequence)))
{
JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence;
return offerDelete(view(), sleOffer, ctx_.registry.journal("View"));
return offerDelete(view(), sleOffer, ctx_.registry.get().getJournal("View"));
}
JLOG(j_.debug()) << "Offer #" << offerSequence << " can't be found.";

View File

@@ -144,7 +144,7 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence);
auto viewJ = ctx.registry.journal("View");
auto viewJ = ctx.registry.get().getJournal("View");
if (isGlobalFrozen(ctx.view, uPaysIssuerID) || isGlobalFrozen(ctx.view, uGetsIssuerID))
{
@@ -502,7 +502,7 @@ OfferCreate::applyHybrid(
bookArr.push_back(std::move(bookInfo));
if (!bookExists)
ctx_.registry.getOrderBookDB().addOrderBook(book);
ctx_.registry.get().getOrderBookDB().addOrderBook(book);
sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
return tesSUCCESS;
@@ -536,7 +536,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
// end up on the books.
auto uRate = getRate(saTakerGets, saTakerPays);
auto viewJ = ctx_.registry.journal("View");
auto viewJ = ctx_.registry.get().getJournal("View");
TER result = tesSUCCESS;
@@ -846,7 +846,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
sb.insert(sleOffer);
if (!bookExisted)
ctx_.registry.getOrderBookDB().addOrderBook(book);
ctx_.registry.get().getOrderBookDB().addOrderBook(book);
JLOG(j_.debug()) << "final result: success";

View File

@@ -73,7 +73,7 @@ EscrowFinish::preflightSigValidated(PreflightContext const& ctx)
if (cb && fb)
{
auto& router = ctx.registry.getHashRouter();
auto& router = ctx.registry.get().getHashRouter();
auto const id = ctx.tx.getTransactionID();
auto const flags = router.getFlags(id);
@@ -237,7 +237,7 @@ EscrowFinish::doApply()
// Check cryptocondition fulfillment
{
auto const id = ctx_.tx.getTransactionID();
auto flags = ctx_.registry.getHashRouter().getFlags(id);
auto flags = ctx_.registry.get().getHashRouter().getFlags(id);
auto const cb = ctx_.tx[~sfCondition];
@@ -261,7 +261,7 @@ EscrowFinish::doApply()
flags = SF_CF_INVALID;
}
ctx_.registry.getHashRouter().setFlags(id, flags);
ctx_.registry.get().getHashRouter().setFlags(id, flags);
// LCOV_EXCL_STOP
}

View File

@@ -344,7 +344,7 @@ NFTokenAcceptOffer::transferNFToken(
if (!tokenAndPage)
return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = nft::removeToken(view(), seller, nftokenID, std::move(tokenAndPage->page));
if (auto const ret = nft::removeToken(view(), seller, nftokenID, tokenAndPage->page);
!isTesSuccess(ret))
return ret;

View File

@@ -344,7 +344,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID)
if (!page)
return tecNO_ENTRY;
return removeToken(view, owner, nftokenID, std::move(page));
return removeToken(view, owner, nftokenID, page);
}
/** Remove the token from the owner's token directory. */

View File

@@ -236,7 +236,7 @@ OracleSet::doApply()
}
STArray updatedSeries;
for (auto const& iter : pairs)
updatedSeries.push_back(std::move(iter.second));
updatedSeries.push_back(iter.second);
sle->setFieldArray(sfPriceDataSeries, updatedSeries);
if (ctx_.tx.isFieldPresent(sfURI))
sle->setFieldVL(sfURI, ctx_.tx[sfURI]);
@@ -284,7 +284,7 @@ OracleSet::doApply()
pairs.emplace(key, std::move(priceData));
}
for (auto const& iter : pairs)
series.push_back(std::move(iter.second));
series.push_back(iter.second);
}
sle->setFieldArray(sfPriceDataSeries, series);

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